Coverage for moptipy / tests / on_ordered_choices.py: 90%
52 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"""Test stuff on ordered choices-based spaces."""
3from typing import Any, Callable, Iterable, cast
5import numpy as np
6from numpy.random import Generator, default_rng
7from pycommons.types import type_error
9from moptipy.api.operators import Op0
10from moptipy.spaces.ordered_choices import OrderedChoices
11from moptipy.tests.op0 import validate_op0
14def choices_for_tests(
15 choice_filter: Callable[[OrderedChoices], bool] | None = None) \
16 -> Iterable[OrderedChoices]:
17 """
18 Get a sequence of ordered choices for tests.
20 :param choice_filter: an optional filter to sort out ordered choices we
21 cannot use for testing
22 :returns: the sequence of ordered choices
23 """
24 r = default_rng()
25 pwrs: list[OrderedChoices] = [
26 OrderedChoices([[1], [2]]),
27 OrderedChoices([[1], [2], [3]]),
28 OrderedChoices([[1], [2, 4], [3]]),
29 OrderedChoices.signed_permutations(1),
30 OrderedChoices.signed_permutations(2),
31 OrderedChoices.signed_permutations(10)]
33 for _i in range(4):
34 done: set[int] = set()
35 created: list[list[int]] = []
36 choices: list[list[int]] = []
37 while (len(choices) <= 0) or (len(done) < 2) or (r.integers(6) > 0):
38 if r.integers(2) <= 0 < len(created):
39 choices.append(created[int(r.integers(len(created)))])
40 else:
41 picked: list[int] = []
42 while (len(picked) <= 0) or (r.integers(6) > 0):
43 cc = int(r.integers(-100, 100))
44 while cc in done:
45 cc += 1
46 done.add(cc)
47 picked.append(cc)
48 choices.append(picked)
49 created.append(picked)
50 pwrs.append(OrderedChoices(choices))
52 if choice_filter is not None:
53 if not callable(choice_filter):
54 raise type_error(choice_filter, "choice_filter", None, call=True)
55 pwrs = [p for p in pwrs if choice_filter(p)]
56 r.shuffle(cast("list", pwrs))
57 return pwrs
60def make_choices_valid(choices: OrderedChoices) -> \
61 Callable[[Generator, np.ndarray], np.ndarray]:
62 """
63 Create a function that can make ordered choices valid.
65 :param choices: the ordered choices
66 :returns: the function
67 """
69 def __make_valid(prnd: Generator, x: np.ndarray,
70 bb=choices.blueprint,
71 cc=choices.choices.__getitem__) -> np.ndarray:
72 np.copyto(x, bb)
73 prnd.shuffle(x)
74 for i, e in enumerate(x):
75 ff = cc(e)
76 x[i] = ff[prnd.integers(len(ff))]
77 return x
79 return __make_valid
82def validate_op0_on_1_choices(
83 op0: Op0 | Callable[[OrderedChoices], Op0],
84 search_space: OrderedChoices,
85 number_of_samples: int | None = None,
86 min_unique_samples: int | Callable[[
87 int, OrderedChoices], int] | None = None) -> None:
88 """
89 Validate the nullary operator on one `OrderedChoices` instance.
91 :param op0: the operator or operator factory
92 :param search_space: the search space
93 :param number_of_samples: the optional number of samples
94 :param min_unique_samples: the optional unique samples
95 """
96 args: dict[str, Any] = {
97 "op0": op0(search_space) if callable(op0) else op0,
98 "search_space": search_space,
99 "make_search_space_element_valid":
100 make_choices_valid(search_space),
101 }
102 if number_of_samples is not None:
103 args["number_of_samples"] = number_of_samples
104 if min_unique_samples is not None:
105 args["min_unique_samples"] = min_unique_samples
106 validate_op0(**args)
109def validate_op0_on_choices(
110 op0: Op0 | Callable[[OrderedChoices], Op0],
111 number_of_samples: int | None = None,
112 min_unique_samples: int | Callable[[
113 int, OrderedChoices], int] | None = None,
114 choice_filter: Callable[[OrderedChoices], bool] | None = None) \
115 -> None:
116 """
117 Validate the nullary operator on several `OrderedChoices` instances.
119 :param op0: the operator or operator factory
120 :param number_of_samples: the optional number of samples
121 :param min_unique_samples: the optional unique samples
122 :param choice_filter: an optional filter to sort out ordered choices we
123 cannot use for testing
124 """
125 for choices in choices_for_tests(choice_filter):
126 validate_op0_on_1_choices(op0, choices, number_of_samples,
127 min_unique_samples)