Skip to content

Commit 75afd70

Browse files
zepfredtriceo
authored andcommitted
fix: enforce phase and solver terminations
1 parent a65ddd9 commit 75afd70

File tree

2 files changed

+25
-1
lines changed

2 files changed

+25
-1
lines changed

core/src/main/java/ai/timefold/solver/core/impl/solver/termination/SolverBridgePhaseTermination.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,14 @@ public SolverBridgePhaseTermination(SolverTermination<Solution_> solverTerminati
3737
}
3838

3939
@Override
40+
@SuppressWarnings({ "rawtypes", "unchecked" })
4041
public boolean isPhaseTerminated(AbstractPhaseScope<Solution_> phaseScope) {
41-
return solverTermination.isSolverTerminated(phaseScope.getSolverScope());
42+
var terminated = solverTermination.isSolverTerminated(phaseScope.getSolverScope());
43+
// If the solver is not finished yet, we need to check the phase termination
44+
if (!terminated && solverTermination instanceof PhaseTermination phaseTermination) {
45+
return phaseTermination.isPhaseTerminated(phaseScope);
46+
}
47+
return terminated;
4248
}
4349

4450
@Override

core/src/test/java/ai/timefold/solver/core/impl/solver/termination/TerminationTest.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,24 @@ void terminateEarlyLocalSearchInterrupted(TerminationConfig terminationConfig) {
329329
assertThat(resultingSolution).isNotNull();
330330
}
331331

332+
@Test
333+
void mixedSolverPhaseTerminations() {
334+
var solverConfig = new SolverConfig()
335+
.withSolutionClass(TestdataSolution.class)
336+
.withEntityClasses(TestdataEntity.class)
337+
.withEasyScoreCalculatorClass(TestdataEasyScoreCalculator.class)
338+
.withTerminationConfig(new TerminationConfig().withSpentLimit(Duration.ofHours(1)))
339+
.withPhases(new LocalSearchPhaseConfig()
340+
.withTerminationConfig(new TerminationConfig().withStepCountLimit(4)));
341+
var solution = TestdataSolution.generateSolution(2, 2);
342+
var solver = SolverFactory.<TestdataSolution> create(solverConfig)
343+
.buildSolver();
344+
var bestSolution = solver.solve(solution);
345+
// The global spent limit is one hour, but the phase limit is four steps.
346+
// The solver process is finished after 4 steps
347+
assertThat(bestSolution).isNotNull();
348+
}
349+
332350
static class TerminationArgumentSource implements ArgumentsProvider {
333351

334352
@Override

0 commit comments

Comments
 (0)