@@ -26,7 +26,7 @@ class Value:
26
26
@dataclass
27
27
class Entity :
28
28
code : Annotated [str , PlanningId ]
29
- value : Annotated [Value , PlanningVariable ] = field (default = None )
29
+ value : Annotated [Value , PlanningVariable ] = field (default = None , compare = False )
30
30
31
31
@constraint_provider
32
32
def my_constraints (constraint_factory : ConstraintFactory ):
@@ -54,24 +54,22 @@ class Solution:
54
54
ValueRangeProvider ]
55
55
score : Annotated [SimpleScore , PlanningScore ] = field (default = None )
56
56
57
- # TODO: Support problem changes
58
- # @Problem_change
59
- # class UseOnlyEntityAndValueProblemChange:
60
- # def __init__(self, entity, value):
61
- # self.entity = entity
62
- # self.value = value
63
- #
64
- # def doChange(self, solution: Solution, problem_change_director: timefold.solver.types.ProblemChangeDirector):
65
- # problem_facts_to_remove = solution.value_list.copy()
66
- # entities_to_remove = solution.entity_list.copy()
67
- # for problem_fact in problem_facts_to_remove:
68
- # problem_change_director.removeProblemFact(problem_fact,
69
- # lambda value: solution.value_list.remove(problem_fact))
70
- # for removed_entity in entities_to_remove:
71
- # problem_change_director.removeEntity(removed_entity,
72
- # lambda entity: solution.entity_list.remove(removed_entity))
73
- # problem_change_director.addEntity(self.entity, lambda entity: solution.entity_list.append(entity))
74
- # problem_change_director.addProblemFact(self.value, lambda value: solution.value_list.append(value))
57
+ class UseOnlyEntityAndValueProblemChange (ProblemChange [Solution ]):
58
+ def __init__ (self , entity , value ):
59
+ self .entity = entity
60
+ self .value = value
61
+
62
+ def do_change (self , solution : Solution , problem_change_director : ProblemChangeDirector ):
63
+ problem_facts_to_remove = solution .value_list .copy ()
64
+ entities_to_remove = solution .entity_list .copy ()
65
+ for problem_fact in problem_facts_to_remove :
66
+ problem_change_director .remove_problem_fact (problem_fact ,
67
+ lambda value : solution .value_list .remove (value ))
68
+ for removed_entity in entities_to_remove :
69
+ problem_change_director .remove_entity (removed_entity ,
70
+ lambda entity : solution .entity_list .remove (entity ))
71
+ problem_change_director .add_entity (self .entity , lambda entity : solution .entity_list .append (entity ))
72
+ problem_change_director .add_problem_fact (self .value , lambda value : solution .value_list .append (value ))
75
73
76
74
solver_config = SolverConfig (
77
75
solution_class = Solution ,
@@ -97,27 +95,27 @@ def assert_solver_run(solver_manager, solver_job):
97
95
assert 3 in value_list
98
96
assert solver_manager .get_solver_status (1 ) == SolverStatus .NOT_SOLVING
99
97
100
- # def assert_problem_change_solver_run(solver_manager, solver_job):
101
- # assert solver_manager.get_solver_status(1) != SolverStatus.NOT_SOLVING
102
- # solver_manager.addProblemChange (1, UseOnlyEntityAndValueProblemChange(Entity('D'), Value(6)))
103
- # lock.release()
104
- # solution = solver_job.get_final_best_solution()
105
- # assert solution.score.score() == 6
106
- # assert len(solution.entity_list) == 1
107
- # assert len(solution.value_range ) == 1
108
- # assert solution.entity_list[0].code == 'D'
109
- # assert solution.entity_list[0].value.value == 6
110
- # assert solution.value_range [0].value == 6
111
- # assert solver_manager.get_solver_status(1) == SolverStatus.NOT_SOLVING
98
+ def assert_problem_change_solver_run (solver_manager , solver_job ):
99
+ assert solver_manager .get_solver_status (1 ) != SolverStatus .NOT_SOLVING
100
+ solver_manager .add_problem_change (1 , UseOnlyEntityAndValueProblemChange (Entity ('D' ), Value (6 )))
101
+ lock .release ()
102
+ solution = solver_job .get_final_best_solution ()
103
+ assert solution .score .score () == 6
104
+ assert len (solution .entity_list ) == 1
105
+ assert len (solution .value_list ) == 1
106
+ assert solution .entity_list [0 ].code == 'D'
107
+ assert solution .entity_list [0 ].value .value == 6
108
+ assert solution .value_list [0 ].value == 6
109
+ assert solver_manager .get_solver_status (1 ) == SolverStatus .NOT_SOLVING
112
110
113
111
with SolverManager .create (SolverFactory .create (solver_config )) as solver_manager :
114
112
lock .acquire ()
115
113
solver_job = solver_manager .solve (1 , problem )
116
114
assert_solver_run (solver_manager , solver_job )
117
115
118
- # lock.acquire()
119
- # solver_job = solver_manager.solve(1, problem)
120
- # assert_problem_change_solver_run(solver_manager, solver_job)
116
+ lock .acquire ()
117
+ solver_job = solver_manager .solve (1 , problem )
118
+ assert_problem_change_solver_run (solver_manager , solver_job )
121
119
122
120
def get_problem (problem_id ):
123
121
assert problem_id == 1
@@ -129,9 +127,11 @@ def get_problem(problem_id):
129
127
.with_problem_finder (get_problem )).run ()
130
128
assert_solver_run (solver_manager , solver_job )
131
129
132
- # lock.acquire()
133
- #solver_job = solver_manager.solve(1, get_problem)
134
- #assert_problem_change_solver_run(solver_manager, solver_job)
130
+ lock .acquire ()
131
+ solver_job = (solver_manager .solve_builder ()
132
+ .with_problem_id (1 )
133
+ .with_problem_finder (get_problem )).run ()
134
+ assert_problem_change_solver_run (solver_manager , solver_job )
135
135
136
136
solution_list = []
137
137
semaphore = Semaphore (0 )
@@ -150,15 +150,16 @@ def on_best_solution_changed(solution):
150
150
assert semaphore .acquire (timeout = 1 )
151
151
assert len (solution_list ) == 1
152
152
153
- # solution_list = []
154
- # lock.acquire()
155
- # solver_job = (solver_manager.solve_builder()
156
- # .with_problem_id(1)
157
- # .with_problem_finder(get_problem)
158
- # .with_best_solution_consumer(on_best_solution_changed)
159
- # ).run()
160
- #assert_problem_change_solver_run(solver_manager, solver_job)
161
- # assert len(solution_list) == 1
153
+ solution_list = []
154
+ lock .acquire ()
155
+ solver_job = (solver_manager .solve_builder ()
156
+ .with_problem_id (1 )
157
+ .with_problem_finder (get_problem )
158
+ .with_best_solution_consumer (on_best_solution_changed )
159
+ ).run ()
160
+ assert_problem_change_solver_run (solver_manager , solver_job )
161
+ assert semaphore .acquire (timeout = 1 )
162
+ assert len (solution_list ) == 1
162
163
163
164
solution_list = []
164
165
lock .acquire ()
@@ -175,16 +176,20 @@ def on_best_solution_changed(solution):
175
176
assert semaphore .acquire (timeout = 1 )
176
177
assert len (solution_list ) == 2
177
178
178
- # solution_list = []
179
- # lock.acquire()
180
- # solver_job = (solver_manager.solve_builder()
181
- # .with_problem_id(1)
182
- # .with_problem_finder(get_problem)
183
- # .with_best_solution_consumer(on_best_solution_changed)
184
- # .with_final_best_solution_consumer(on_best_solution_changed)
185
- # ).run()
186
- # assert_problem_change_solver_run(solver_manager, solver_job)
187
- # assert len(solution_list) == 2
179
+ solution_list = []
180
+ lock .acquire ()
181
+ solver_job = (solver_manager .solve_builder ()
182
+ .with_problem_id (1 )
183
+ .with_problem_finder (get_problem )
184
+ .with_best_solution_consumer (on_best_solution_changed )
185
+ .with_final_best_solution_consumer (on_best_solution_changed )
186
+ ).run ()
187
+ assert_problem_change_solver_run (solver_manager , solver_job )
188
+ # Wait for 2 acquires, one for best solution consumer,
189
+ # another for final best solution consumer
190
+ assert semaphore .acquire (timeout = 1 )
191
+ assert semaphore .acquire (timeout = 1 )
192
+ assert len (solution_list ) == 2
188
193
189
194
190
195
@pytest .mark .filterwarnings ("ignore:.*Exception in thread.*:pytest.PytestUnhandledThreadExceptionWarning" )
0 commit comments