Skip to content

Commit a15bd88

Browse files
committed
fix: improve strategy
1 parent caebd3b commit a15bd88

File tree

2 files changed

+18
-10
lines changed

2 files changed

+18
-10
lines changed

core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/move/decorator/FilteringMoveSelector.java

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ public Iterator<Move<Solution_>> iterator() {
7777

7878
private class JustInTimeFilteringMoveIterator extends UpcomingSelectionIterator<Move<Solution_>> {
7979

80+
private final double TERMINATION_CHECK_RATIO = 0.1;
8081
private final Iterator<Move<Solution_>> childMoveIterator;
8182
private final long bailOutSize;
8283
private final AbstractPhaseScope<Solution_> phaseScope;
@@ -94,6 +95,9 @@ public JustInTimeFilteringMoveIterator(Iterator<Move<Solution_>> childMoveIterat
9495
protected Move<Solution_> createUpcomingSelection() {
9596
Move<Solution_> next;
9697
long attemptsBeforeBailOut = bailOutSize;
98+
// To reduce the impact of checking for termination on each move,
99+
// we only check termination after evaluating 10% of the available moves
100+
long attemptsBeforeCheckTermination = (long) (bailOutSize * TERMINATION_CHECK_RATIO);
97101
do {
98102
if (!childMoveIterator.hasNext()) {
99103
return noUpcomingSelection();
@@ -104,13 +108,18 @@ protected Move<Solution_> createUpcomingSelection() {
104108
logger.trace("Bailing out of neverEnding selector ({}) after ({}) attempts to avoid infinite loop.",
105109
FilteringMoveSelector.this, bailOutSize);
106110
return noUpcomingSelection();
107-
} else if (termination != null && termination.isPhaseTerminated(phaseScope)) {
108-
logger.trace(
109-
"Bailing out of neverEnding selector ({}) because the termination setting has been triggered.",
110-
FilteringMoveSelector.this);
111-
return noUpcomingSelection();
111+
} else if (termination != null && attemptsBeforeCheckTermination <= 0L) {
112+
// Reset the counter
113+
attemptsBeforeCheckTermination = (long) (bailOutSize * TERMINATION_CHECK_RATIO);
114+
if (termination.isPhaseTerminated(phaseScope)) {
115+
logger.trace(
116+
"Bailing out of neverEnding selector ({}) because the termination setting has been triggered.",
117+
FilteringMoveSelector.this);
118+
return noUpcomingSelection();
119+
}
112120
}
113121
attemptsBeforeBailOut--;
122+
attemptsBeforeCheckTermination--;
114123
}
115124
next = childMoveIterator.next();
116125
} while (!accept(scoreDirector, next));

core/src/test/java/ai/timefold/solver/core/impl/heuristic/selector/move/decorator/FilteringMoveSelectorTest.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,18 +52,17 @@ void bailOutByTermination() {
5252
var moveSelector = mock(MoveSelector.class);
5353
var termination = mock(BasicPlumbingTermination.class);
5454
var iterator = mock(Iterator.class);
55-
// We set the maximum value to force it to run many evaluations
56-
when(moveSelector.getSize()).thenReturn(Long.MAX_VALUE / 11);
55+
when(moveSelector.getSize()).thenReturn(100L);
5756
when(moveSelector.isNeverEnding()).thenReturn(true);
5857
when(moveSelector.iterator()).thenReturn(iterator);
5958
when(iterator.hasNext()).thenReturn(true);
6059
when(phaseScope.getTermination()).thenReturn(termination);
61-
when(termination.isPhaseTerminated(any(AbstractPhaseScope.class))).thenReturn(false, false, true);
60+
when(termination.isPhaseTerminated(any(AbstractPhaseScope.class))).thenReturn(false, true);
6261
var filteredMoveSelector = FilteringMoveSelector.of(moveSelector, (scoreDirector, selection) -> false);
6362
filteredMoveSelector.phaseStarted(phaseScope);
6463
assertThat(filteredMoveSelector.iterator().hasNext()).isFalse();
65-
// The termination returns true at the third call
66-
verify(iterator, times(2)).next();
64+
// The termination returns true at the second call, and only (100 * 10) * 30% calls are executed per check
65+
verify(iterator, times(200)).next();
6766
}
6867

6968
public void filter(SelectionCacheType cacheType, int timesCalled) {

0 commit comments

Comments
 (0)