13
13
14
14
public final class FilteringMoveSelector <Solution_ > extends AbstractMoveSelector <Solution_ > {
15
15
16
+ private static final long BAIL_OUT_MULTIPLIER = 10L ;
17
+
16
18
public static <Solution_ > FilteringMoveSelector <Solution_ > of (MoveSelector <Solution_ > moveSelector ,
17
19
SelectionFilter <Solution_ , Move <Solution_ >> filter ) {
18
20
if (moveSelector instanceof FilteringMoveSelector <Solution_ > filteringMoveSelector ) {
@@ -75,6 +77,27 @@ public Iterator<Move<Solution_>> iterator() {
75
77
return new JustInTimeFilteringMoveIterator (childMoveSelector .iterator (), determineBailOutSize (), phaseScope );
76
78
}
77
79
80
+ private long determineBailOutSize () {
81
+ if (!bailOutEnabled ) {
82
+ return -1L ;
83
+ }
84
+ try {
85
+ return childMoveSelector .getSize () * BAIL_OUT_MULTIPLIER ;
86
+ } catch (Exception ex ) {
87
+ // Some move selectors throw an exception when getSize() is called.
88
+ // In this case, we choose to disregard it and pick a large-enough bail-out size anyway.
89
+ // The ${bailOutSize+1}th move could in theory show up where previous ${bailOutSize} moves did not,
90
+ // but we consider this to be an acceptable risk,
91
+ // outweighed by the benefit of the solver never running into an endless loop.
92
+ // The exception itself is swallowed, as it doesn't bring any useful information.
93
+ long bailOutSize = Short .MAX_VALUE * BAIL_OUT_MULTIPLIER ;
94
+ logger .trace (
95
+ " Never-ending move selector ({}) failed to provide size, choosing a bail-out size of ({}) attempts." ,
96
+ childMoveSelector , bailOutSize );
97
+ return bailOutSize ;
98
+ }
99
+ }
100
+
78
101
private class JustInTimeFilteringMoveIterator extends UpcomingSelectionIterator <Move <Solution_ >> {
79
102
80
103
private final Iterator <Move <Solution_ >> childMoveIterator ;
@@ -94,6 +117,10 @@ public JustInTimeFilteringMoveIterator(Iterator<Move<Solution_>> childMoveIterat
94
117
protected Move <Solution_ > createUpcomingSelection () {
95
118
Move <Solution_ > next ;
96
119
long attemptsBeforeBailOut = bailOutSize ;
120
+ // To reduce the impact of checking for termination on each move,
121
+ // we only check for termination
122
+ // after filtering out a total number of moves equal to size(childMoveSelector).
123
+ long attemptsBeforeCheckTermination = bailOutSize / BAIL_OUT_MULTIPLIER ;
97
124
do {
98
125
if (!childMoveIterator .hasNext ()) {
99
126
return noUpcomingSelection ();
@@ -104,13 +131,18 @@ protected Move<Solution_> createUpcomingSelection() {
104
131
logger .trace ("Bailing out of neverEnding selector ({}) after ({}) attempts to avoid infinite loop." ,
105
132
FilteringMoveSelector .this , bailOutSize );
106
133
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 ();
134
+ } else if (termination != null && attemptsBeforeCheckTermination <= 0L ) {
135
+ // Reset the counter
136
+ attemptsBeforeCheckTermination = bailOutSize / BAIL_OUT_MULTIPLIER ;
137
+ if (termination .isPhaseTerminated (phaseScope )) {
138
+ logger .trace (
139
+ "Bailing out of neverEnding selector ({}) because the termination setting has been triggered." ,
140
+ FilteringMoveSelector .this );
141
+ return noUpcomingSelection ();
142
+ }
112
143
}
113
144
attemptsBeforeBailOut --;
145
+ attemptsBeforeCheckTermination --;
114
146
}
115
147
next = childMoveIterator .next ();
116
148
} while (!accept (scoreDirector , next ));
@@ -119,27 +151,6 @@ protected Move<Solution_> createUpcomingSelection() {
119
151
120
152
}
121
153
122
- private long determineBailOutSize () {
123
- if (!bailOutEnabled ) {
124
- return -1L ;
125
- }
126
- try {
127
- return childMoveSelector .getSize () * 10L ;
128
- } catch (Exception ex ) {
129
- // Some move selectors throw an exception when getSize() is called.
130
- // In this case, we choose to disregard it and pick a large-enough bail-out size anyway.
131
- // The ${bailOutSize+1}th move could in theory show up where previous ${bailOutSize} moves did not,
132
- // but we consider this to be an acceptable risk,
133
- // outweighed by the benefit of the solver never running into an endless loop.
134
- // The exception itself is swallowed, as it doesn't bring any useful information.
135
- long bailOutSize = Short .MAX_VALUE * 10L ;
136
- logger .trace (
137
- " Never-ending move selector ({}) failed to provide size, choosing a bail-out size of ({}) attempts." ,
138
- childMoveSelector , bailOutSize );
139
- return bailOutSize ;
140
- }
141
- }
142
-
143
154
private boolean accept (ScoreDirector <Solution_ > scoreDirector , Move <Solution_ > move ) {
144
155
if (filter != null && !filter .accept (scoreDirector , move )) {
145
156
logger .trace (" Move ({}) filtered out by a selection filter ({})." , move , filter );
0 commit comments