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

1"""Test stuff on ordered choices-based spaces.""" 

2 

3from typing import Any, Callable, Iterable, cast 

4 

5import numpy as np 

6from numpy.random import Generator, default_rng 

7from pycommons.types import type_error 

8 

9from moptipy.api.operators import Op0 

10from moptipy.spaces.ordered_choices import OrderedChoices 

11from moptipy.tests.op0 import validate_op0 

12 

13 

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. 

19 

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)] 

32 

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)) 

51 

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 

58 

59 

60def make_choices_valid(choices: OrderedChoices) -> \ 

61 Callable[[Generator, np.ndarray], np.ndarray]: 

62 """ 

63 Create a function that can make ordered choices valid. 

64 

65 :param choices: the ordered choices 

66 :returns: the function 

67 """ 

68 

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 

78 

79 return __make_valid 

80 

81 

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. 

90 

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) 

107 

108 

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. 

118 

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)