Coverage for moptipy / tests / on_jssp.py: 85%
102 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"""Perform tests on the Job Shop Scheduling Problem."""
3from typing import Any, Callable, Final, Iterable, cast
5from numpy.random import Generator, default_rng
6from pycommons.types import type_error
8from moptipy.api.algorithm import Algorithm
9from moptipy.api.mo_algorithm import MOAlgorithm
10from moptipy.api.mo_problem import MOProblem
11from moptipy.api.objective import Objective
12from moptipy.examples.jssp.gantt import Gantt
13from moptipy.examples.jssp.gantt_space import GanttSpace
14from moptipy.examples.jssp.instance import Instance
15from moptipy.examples.jssp.makespan import Makespan
16from moptipy.examples.jssp.ob_encoding import OperationBasedEncoding
17from moptipy.examples.jssp.worktime import Worktime
18from moptipy.mo.problem.weighted_sum import WeightedSum
19from moptipy.operators.permutations.op0_shuffle import Op0Shuffle
20from moptipy.spaces.permutations import Permutations
21from moptipy.tests.algorithm import validate_algorithm
22from moptipy.tests.mo_algorithm import validate_mo_algorithm
23from moptipy.tests.objective import validate_objective
26def jssp_instances_for_tests() -> Iterable[str]:
27 """
28 Get a sequence of JSSP instances to test on.
30 :returns: an iterable of JSSP instance names
31 """
32 r = default_rng()
33 ri = r.integers
34 insts: list[str] = [
35 "demo", "ft06", "ft10", f"abz{ri(5, 10)}", f"dmu{ri(10, 81)}",
36 f"orb0{ri(1, 10)}", f"swv{ri(10, 21)}", f"ta{ri(10, 65)}",
37 f"ta{ri(65, 70)}", f"ta{ri(70, 75)}", f"yn{ri(1, 5)}"]
38 r.shuffle(cast("list", insts))
39 return insts
42def make_gantt_valid(inst: Instance) -> Callable[[Generator, Gantt], Gantt]:
43 """
44 Make a function that creates valid Gantt charts.
46 :param inst: the JSSP instance
47 :returns: a function that can make gantt charts valid
48 """
49 pr = Permutations.with_repetitions(inst.jobs, inst.machines)
50 op0 = Op0Shuffle(pr)
51 oe = OperationBasedEncoding(inst)
53 def __make_valid(prnd: Generator, x: Gantt, ppr=pr,
54 pop0=op0, poe=oe) -> Gantt:
55 xx = ppr.create()
56 pop0.op0(prnd, xx)
57 poe.decode(xx, x)
58 return x
60 return __make_valid
63def validate_algorithm_on_1_jssp(
64 algorithm: Algorithm | Callable[
65 [Instance, Permutations, Objective], Algorithm],
66 instance: str | None = None, max_fes: int = 100,
67 required_result: int | None = None,
68 post: Callable[[Algorithm, int], Any] | None = None) -> None:
69 """
70 Check the validity of a black-box algorithm on the JSSP.
72 :param algorithm: the algorithm or algorithm factory
73 :param instance: the instance name, or `None` to randomly pick one
74 :param max_fes: the maximum number of FEs
75 :param required_result: the optional required result quality
76 :param post: a check to run after each execution of the algorithm,
77 receiving the algorithm and the number of consumed FEs as parameter
78 """
79 if not (isinstance(algorithm, Algorithm) or callable(algorithm)):
80 raise type_error(algorithm, "algorithm", Algorithm, True)
81 if instance is None:
82 instance = str(default_rng().choice(Instance.list_resources()))
83 if not isinstance(instance, str):
84 raise type_error(instance, "JSSP instance name", (str, None))
85 inst = Instance.from_resource(instance)
86 if not isinstance(inst, Instance):
87 raise type_error(inst, f"loaded JSSP instance {instance!r}", Instance)
88 if (post is not None) and (not callable(post)):
89 raise type_error(post, "post", None, call=True)
91 search_space = Permutations.with_repetitions(inst.jobs,
92 inst.machines)
93 solution_space = GanttSpace(inst)
94 encoding = OperationBasedEncoding(inst)
95 objective = Makespan(inst)
96 if callable(algorithm):
97 algorithm = algorithm(inst, search_space, objective)
98 if not isinstance(algorithm, Algorithm):
99 raise type_error(algorithm, "algorithm", Algorithm, call=True)
101 goal: int
102 if required_result is None:
103 lb: int = objective.lower_bound()
104 ub: int = objective.upper_bound()
105 goal = max(lb + 1, min(ub - 1, int(0.5 + (lb + (0.96 * (ub - lb))))))
106 else:
107 goal = required_result
109 validate_algorithm(algorithm=algorithm,
110 solution_space=solution_space,
111 objective=objective,
112 search_space=search_space,
113 encoding=encoding,
114 max_fes=max_fes,
115 required_result=goal,
116 post=post)
119def validate_algorithm_on_jssp(
120 algorithm: Callable[[Instance, Permutations,
121 Objective], Algorithm],
122 max_fes: int = 100,
123 post: Callable[[Algorithm, int], Any] | None = None) -> None:
124 """
125 Validate an algorithm on a set of JSSP instances.
127 :param algorithm: the algorithm factory
128 :param max_fes: the maximum FEs
129 :param post: a check to run after each execution of the algorithm,
130 receiving the algorithm and the number of consumed FEs as parameter
131 """
132 for i in jssp_instances_for_tests():
133 validate_algorithm_on_1_jssp(algorithm, i, max_fes=max_fes, post=post)
136def validate_objective_on_1_jssp(
137 objective: Objective | Callable[[Instance], Objective],
138 instance: str | None = None,
139 is_deterministic: bool = True) -> None:
140 """
141 Validate an objective function on 1 JSSP instance.
143 :param objective: the objective function or a factory creating it
144 :param instance: the instance name
145 :param is_deterministic: is the objective function deterministic?
146 """
147 if instance is None:
148 instance = str(default_rng().choice(Instance.list_resources()))
149 if not isinstance(instance, str):
150 raise type_error(instance, "JSSP instance name", (str, None))
151 inst = Instance.from_resource(instance)
152 if not isinstance(inst, Instance):
153 raise type_error(inst, f"loaded JSSP instance {instance!r}", Instance)
155 if callable(objective):
156 objective = objective(inst)
158 validate_objective(
159 objective=objective,
160 solution_space=GanttSpace(inst),
161 make_solution_space_element_valid=make_gantt_valid(inst),
162 is_deterministic=is_deterministic)
165def validate_objective_on_jssp(
166 objective: Objective | Callable[[Instance], Objective],
167 is_deterministic: bool = True) -> None:
168 """
169 Validate an objective function on JSSP instances.
171 :param objective: the objective function or a factory creating it
172 :param is_deterministic: is the objective function deterministic?
173 """
174 for i in jssp_instances_for_tests():
175 validate_objective_on_1_jssp(objective, i, is_deterministic)
178def validate_mo_algorithm_on_1_jssp(
179 algorithm: MOAlgorithm | Callable[
180 [Instance, Permutations, MOProblem], MOAlgorithm],
181 instance: str | None = None, max_fes: int = 100) -> None:
182 """
183 Check the validity of a black-box multi-objective algorithm on the JSSP.
185 :param algorithm: the algorithm or algorithm factory
186 :param instance: the instance name, or `None` to randomly pick one
187 :param max_fes: the maximum number of FEs
188 """
189 if not (isinstance(algorithm, MOAlgorithm) or callable(algorithm)):
190 raise type_error(algorithm, "algorithm", MOAlgorithm, True)
192 random: Final[Generator] = default_rng()
193 if instance is None:
194 instance = str(random.choice(Instance.list_resources()))
195 if not isinstance(instance, str):
196 raise type_error(instance, "JSSP instance name", (str, None))
197 inst = Instance.from_resource(instance)
198 if not isinstance(inst, Instance):
199 raise type_error(inst, f"loaded JSSP instance '{instance}'", Instance)
201 search_space = Permutations.with_repetitions(inst.jobs,
202 inst.machines)
203 solution_space = GanttSpace(inst)
204 encoding = OperationBasedEncoding(inst)
206 weights: Final[list[int | float]] = [float(random.uniform(0.01, 10)),
207 float(random.uniform(0.01, 10))] \
208 if random.integers(2) <= 0 else \
209 [1 + int(random.integers(1 << random.integers(40))),
210 1 + int(random.integers(1 << random.integers(40)))]
211 problem: Final[MOProblem] = WeightedSum(
212 [Makespan(inst), Worktime(inst)], weights)
214 if callable(algorithm):
215 algorithm = algorithm(inst, search_space, problem)
216 if not isinstance(algorithm, MOAlgorithm):
217 raise type_error(algorithm, "algorithm", MOAlgorithm, call=True)
219 validate_mo_algorithm(algorithm=algorithm,
220 solution_space=solution_space,
221 problem=problem,
222 search_space=search_space,
223 encoding=encoding,
224 max_fes=max_fes)
227def validate_mo_algorithm_on_jssp(
228 algorithm: Callable[
229 [Instance, Permutations, MOProblem], MOAlgorithm]) -> None:
230 """
231 Validate a multi-objective algorithm on a set of JSSP instances.
233 :param algorithm: the algorithm factory
234 """
235 for i in jssp_instances_for_tests():
236 validate_mo_algorithm_on_1_jssp(algorithm, i, 100)