Coverage for moptipy / tests / op1_with_step_size.py: 76%
62 statements
« prev ^ index » next coverage.py v7.12.0, created at 2025-11-24 08:49 +0000
« prev ^ index » next coverage.py v7.12.0, created at 2025-11-24 08:49 +0000
1"""Functions for testing unary search operators with step size."""
2from math import isfinite
3from typing import Any, Callable, Iterable
5from numpy.random import Generator, default_rng
6from pycommons.types import type_error
8from moptipy.api.operators import Op1WithStepSize, check_op1_with_step_size
9from moptipy.api.space import Space
10from moptipy.tests.op1 import default_min_unique_samples, validate_op1
13def validate_op1_with_step_size(
14 op1: Op1WithStepSize,
15 search_space: Space | None = None,
16 make_search_space_element_valid:
17 Callable[[Generator, Any], Any] | None = lambda _, x: x,
18 number_of_samples: int = 100,
19 min_unique_samples: int | Callable[[int, Space], int]
20 = default_min_unique_samples,
21 step_sizes: Iterable[float] = (),
22 get_step_size: Callable[[
23 Space, Any, Any], float | None] | None = None) -> None:
24 """
25 Check whether an object is a valid moptipy unary operator with step size.
27 :param op1: the operator
28 :param search_space: the search space
29 :param make_search_space_element_valid: make a point in the search
30 space valid
31 :param number_of_samples: the number of times to invoke the operator
32 :param min_unique_samples: a lambda for computing the number
33 :param step_sizes: the step sizes to test
34 :param get_step_size: try to get the step size difference from two space
35 elements
36 :raises ValueError: if `op1` is not a valid instance of
37 :class:`~moptipy.api.operators.Op1`
38 :raises TypeError: if incorrect types are encountered
39 """
40 if not isinstance(op1, Op1WithStepSize):
41 raise type_error(op1, "op1", Op1WithStepSize)
42 if op1.__class__ == Op1WithStepSize:
43 raise ValueError("Cannot use abstract base Op1WithStepSize directly.")
44 check_op1_with_step_size(op1)
45 if not isinstance(step_sizes, Iterable):
46 raise type_error(step_sizes, "step_sizes", Iterable)
47 if (get_step_size is not None) and (not callable(get_step_size)):
48 raise type_error(get_step_size, "get_step_size", None, call=True)
50 validate_op1(op1, search_space, make_search_space_element_valid,
51 number_of_samples, min_unique_samples)
53 random: Generator | None = None
54 x: Any = None
55 x_copy: Any = None
56 dest: Any = None
57 if search_space is not None:
58 if random is None:
59 random = default_rng()
60 x = search_space.create()
61 if x is None:
62 raise ValueError(
63 f"search_space.create()=None for {search_space}")
64 x_copy = search_space.create()
65 if x_copy is None:
66 raise ValueError(
67 f"search_space.create()=None for {search_space}")
68 dest = search_space.create()
69 if dest is None:
70 raise ValueError(
71 f"search_space.create()=None for {search_space}")
73 for i, step_size in enumerate(step_sizes):
74 if not isinstance(step_size, float):
75 raise type_error(step_size, f"step_sizes[{i}]", float)
76 if not (isfinite(step_size) and (0 <= step_size <= 1)):
77 raise ValueError(f"Forbidden step_sizes[{i}]={step_size}.")
78 if (random is not None) and (search_space is not None):
79 if make_search_space_element_valid is not None:
80 x = make_search_space_element_valid(random, x)
81 if x is None:
82 raise ValueError(
83 "make_search_space_element_valid(search_space.create())="
84 f"None for {search_space}")
85 search_space.validate(x)
86 search_space.copy(x_copy, x)
87 if not search_space.is_equal(x_copy, x):
88 raise ValueError(
89 f"error when copying {x}, got {x_copy} on {search_space}")
90 op1.op1(random, dest, x, step_size)
91 search_space.validate(dest)
92 if search_space.is_equal(dest, x_copy):
93 raise ValueError(
94 f"operator copies source for step_size={step_size} "
95 f"on {search_space} and {dest}")
96 if not search_space.is_equal(x, x_copy):
97 raise ValueError(
98 f"operator modifies source for step_size={step_size} "
99 f"on {search_space}")
100 if get_step_size is not None:
101 found_step_size: float | None = \
102 get_step_size(search_space, x, dest)
103 if found_step_size is None:
104 continue
105 if not (isfinite(found_step_size)
106 and (0 <= found_step_size <= 1)):
107 raise ValueError(
108 f"invalid detected step size {found_step_size} "
109 f"for {search_space}.")
110 if found_step_size != step_size:
111 raise ValueError(
112 f"step_size={step_size} but {op1} actually "
113 f"performs step of size {found_step_size} for "
114 f"x={x} and returns {dest} for {search_space}.")