Coverage for moptipy / tests / on_permutations.py: 91%

65 statements  

« prev     ^ index     » next       coverage.py v7.12.0, created at 2025-11-24 08:49 +0000

1"""Test stuff on permutations with repetitions.""" 

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, Op1, Op1WithStepSize, Op2 

10from moptipy.spaces.permutations import Permutations 

11from moptipy.tests.op0 import validate_op0 

12from moptipy.tests.op1 import validate_op1 

13from moptipy.tests.op1_with_step_size import validate_op1_with_step_size 

14from moptipy.tests.op2 import validate_op2 

15 

16 

17def permutations_for_tests( 

18 perm_filter: Callable[[Permutations], bool] | None = None) \ 

19 -> Iterable[Permutations]: 

20 """ 

21 Get a sequence of permutations for tests. 

22 

23 :param perm_filter: an optional filter to sort out permutations we cannot 

24 use for testing 

25 :returns: the sequence of Permutations 

26 """ 

27 r = default_rng() 

28 pwrs: list[Permutations] = [ 

29 Permutations.standard(2), 

30 Permutations.standard(3), 

31 Permutations.standard(4), 

32 Permutations.standard(5), 

33 Permutations.standard(6), 

34 Permutations.standard(12), 

35 Permutations.standard(23), 

36 Permutations.with_repetitions(2, 2), 

37 Permutations.with_repetitions(2, 3), 

38 Permutations.with_repetitions(3, 2), 

39 Permutations.with_repetitions(3, 3), 

40 Permutations.with_repetitions(5, 5), 

41 Permutations.with_repetitions(int(r.integers(6, 10)), 

42 int(r.integers(2, 7))), 

43 Permutations.with_repetitions(int(r.integers(2, 5)), 

44 int(r.integers(6, 10))), 

45 Permutations.with_repetitions(int(r.integers(130, 500)), 

46 int(r.integers(2, 200))), 

47 Permutations([1, 1, 1, 1, 1, 5, 5, 3]), 

48 Permutations([0, 1, 1, 1, 1, 1])] 

49 if perm_filter is not None: 

50 if not callable(perm_filter): 

51 raise type_error(perm_filter, "perm_filter", None, call=True) 

52 pwrs = [p for p in pwrs if perm_filter(p)] 

53 r.shuffle(cast("list", pwrs)) 

54 return pwrs 

55 

56 

57def make_permutation_valid(pwr: Permutations) -> \ 

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

59 """ 

60 Create a function that can make permutations with repetitions valid. 

61 

62 :param pwr: the permutations 

63 :returns: the function 

64 """ 

65 def __make_valid(prnd: Generator, x: np.ndarray, ppp=pwr) -> np.ndarray: 

66 np.copyto(x, ppp.blueprint) 

67 prnd.shuffle(x) 

68 return x 

69 return __make_valid 

70 

71 

72def validate_op0_on_1_permutations( 

73 op0: Op0 | Callable[[Permutations], Op0], 

74 search_space: Permutations, 

75 number_of_samples: int | None = None, 

76 min_unique_samples: int | Callable[[ 

77 int, Permutations], int] | None = None) -> None: 

78 """ 

79 Validate the nullary operator on one `Permutations` instance. 

80 

81 :param op0: the operator or operator factory 

82 :param search_space: the search space 

83 :param number_of_samples: the optional number of samples 

84 :param min_unique_samples: the optional unique samples 

85 """ 

86 args: dict[str, Any] = { 

87 "op0": op0(search_space) if callable(op0) else op0, 

88 "search_space": search_space, 

89 "make_search_space_element_valid": 

90 make_permutation_valid(search_space), 

91 } 

92 if number_of_samples is not None: 

93 args["number_of_samples"] = number_of_samples 

94 if min_unique_samples is not None: 

95 args["min_unique_samples"] = min_unique_samples 

96 validate_op0(**args) 

97 

98 

99def validate_op0_on_permutations( 

100 op0: Op0 | Callable[[Permutations], Op0], 

101 number_of_samples: int | None = None, 

102 min_unique_samples: int | Callable[[ 

103 int, Permutations], int] | None = None, 

104 perm_filter: Callable[[Permutations], bool] | None = None) -> None: 

105 """ 

106 Validate the nullary operator on several `Permutations` instances. 

107 

108 :param op0: the operator or operator factory 

109 :param number_of_samples: the optional number of samples 

110 :param min_unique_samples: the optional unique samples 

111 :param perm_filter: an optional filter to sort out permutations we cannot 

112 use for testing 

113 """ 

114 for pwr in permutations_for_tests(perm_filter): 

115 validate_op0_on_1_permutations(op0, pwr, number_of_samples, 

116 min_unique_samples) 

117 

118 

119def validate_op1_on_1_permutations( 

120 op1: Op1 | Callable[[Permutations], Op1], 

121 search_space: Permutations, 

122 number_of_samples: int | None = None, 

123 min_unique_samples: int | Callable[[ 

124 int, Permutations], int] | None = None) -> None: 

125 """ 

126 Validate the unary operator on one `Permutations` instance. 

127 

128 :param op1: the operator or operator factory 

129 :param search_space: the search space 

130 :param number_of_samples: the optional number of samples 

131 :param min_unique_samples: the optional unique samples 

132 """ 

133 args: dict[str, Any] = { 

134 "op1": op1(search_space) if callable(op1) else op1, 

135 "search_space": search_space, 

136 "make_search_space_element_valid": 

137 make_permutation_valid(search_space), 

138 } 

139 if number_of_samples is not None: 

140 args["number_of_samples"] = number_of_samples 

141 if min_unique_samples is not None: 

142 args["min_unique_samples"] = min_unique_samples 

143 validate_op1(**args) 

144 

145 

146def validate_op1_on_permutations( 

147 op1: Op1 | Callable[[Permutations], Op1], 

148 number_of_samples: int | None = None, 

149 min_unique_samples: int | Callable[[ 

150 int, Permutations], int] | None = None, 

151 perm_filter: Callable[[Permutations], bool] | None = None) -> None: 

152 """ 

153 Validate the unary operator on several `Permutations` instances. 

154 

155 :param op1: the operator or operator factory 

156 :param number_of_samples: the optional number of samples 

157 :param min_unique_samples: the optional unique samples 

158 :param perm_filter: an optional filter to sort out permutations we cannot 

159 use for testing 

160 """ 

161 for pwr in permutations_for_tests(perm_filter): 

162 validate_op1_on_1_permutations(op1, pwr, number_of_samples, 

163 min_unique_samples) 

164 

165 

166def validate_op1_with_step_size_on_1_permutations( 

167 op1: Op1WithStepSize | Callable[[Permutations], Op1WithStepSize], 

168 search_space: Permutations, 

169 number_of_samples: int | None = None, 

170 min_unique_samples: int | Callable[[ 

171 int, Permutations], int] | None = None, 

172 step_sizes: Iterable[float] | Callable[ 

173 [Permutations], Iterable[float]] = (), 

174 get_step_size: Callable[[ 

175 Permutations, np.ndarray, np.ndarray, 

176 ], float | None] | None = None) -> None: 

177 """ 

178 Validate the step-sized unary operator on one `Permutations` instance. 

179 

180 :param op1: the operator or operator factory 

181 :param search_space: the search space 

182 :param number_of_samples: the optional number of samples 

183 :param min_unique_samples: the optional unique samples 

184 :param step_sizes: the step sizes to test 

185 :param get_step_size: try to get the step size from two space elements 

186 """ 

187 args: dict[str, Any] = { 

188 "op1": op1(search_space) if callable(op1) else op1, 

189 "search_space": search_space, 

190 "make_search_space_element_valid": 

191 make_permutation_valid(search_space), 

192 "step_sizes": step_sizes(search_space) if callable(step_sizes) 

193 else step_sizes, 

194 "get_step_size": get_step_size, 

195 } 

196 if number_of_samples is not None: 

197 args["number_of_samples"] = number_of_samples 

198 if min_unique_samples is not None: 

199 args["min_unique_samples"] = min_unique_samples 

200 validate_op1_with_step_size(**args) 

201 

202 

203def validate_op1_with_step_size_on_permutations( 

204 op1: Op1WithStepSize | Callable[[Permutations], Op1WithStepSize], 

205 number_of_samples: int | None = None, 

206 min_unique_samples: int | Callable[[ 

207 int, Permutations], int] | None = None, 

208 step_sizes: Iterable[float] | Callable[ 

209 [Permutations], Iterable[float]] = (), 

210 get_step_size: Callable[[ 

211 Permutations, np.ndarray, np.ndarray, 

212 ], float | None] | None = None, 

213 perm_filter: Callable[[Permutations], bool] | None = None) -> None: 

214 """ 

215 Validate the unary operator on several `Permutations` instances. 

216 

217 :param op1: the operator or operator factory 

218 :param number_of_samples: the optional number of samples 

219 :param min_unique_samples: the optional unique samples 

220 :param step_sizes: the step sizes to test 

221 :param get_step_size: try to get the step size from two space elements 

222 :param perm_filter: an optional filter to sort out permutations we cannot 

223 use for testing 

224 """ 

225 for pwr in permutations_for_tests(perm_filter): 

226 validate_op1_with_step_size_on_1_permutations( 

227 op1, pwr, number_of_samples, min_unique_samples, step_sizes, 

228 get_step_size) 

229 

230 

231def validate_op2_on_1_permutations( 

232 op2: Op2 | Callable[[Permutations], Op2], 

233 search_space: Permutations, 

234 number_of_samples: int | None = None, 

235 min_unique_samples: int | Callable[[ 

236 int, Permutations], int] | None = None) -> None: 

237 """ 

238 Validate the binary operator on one `Permutations` instance. 

239 

240 :param op2: the operator or operator factory 

241 :param search_space: the search space 

242 :param number_of_samples: the optional number of samples 

243 :param min_unique_samples: the optional unique samples 

244 """ 

245 args: dict[str, Any] = { 

246 "op2": op2(search_space) if callable(op2) else op2, 

247 "search_space": search_space, 

248 "make_search_space_element_valid": 

249 make_permutation_valid(search_space), 

250 } 

251 if number_of_samples is not None: 

252 args["number_of_samples"] = number_of_samples 

253 if min_unique_samples is not None: 

254 args["min_unique_samples"] = min_unique_samples 

255 validate_op2(**args) 

256 

257 

258def validate_op2_on_permutations( 

259 op2: Op2 | Callable[[Permutations], Op2], 

260 number_of_samples: int | None = None, 

261 min_unique_samples: int | Callable[[ 

262 int, Permutations], int] | None = None, 

263 perm_filter: Callable[[Permutations], bool] | None = None) -> None: 

264 """ 

265 Validate the binary operator on several `Permutations` instances. 

266 

267 :param op2: the operator or operator factory 

268 :param number_of_samples: the optional number of samples 

269 :param min_unique_samples: the optional unique samples 

270 :param perm_filter: an optional filter to sort out permutations we cannot 

271 use for testing 

272 """ 

273 for pwr in permutations_for_tests(perm_filter): 

274 validate_op2_on_1_permutations(op2, pwr, number_of_samples, 

275 min_unique_samples)