Skip to content

Commit 3247e86

Browse files
authored
fix: Ruin&Recreate score corruption and multi-threaded fixes (#1320)
Fixes TimefoldAI/timefold-employee-scheduling#305
1 parent df5f405 commit 3247e86

28 files changed

+629
-198
lines changed

core/src/main/java/ai/timefold/solver/core/impl/constructionheuristic/DefaultConstructionHeuristicPhase.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ protected ConstructionHeuristicPhaseScope<Solution_> buildPhaseScope(SolverScope
101101
return new ConstructionHeuristicPhaseScope<>(solverScope, phaseIndex);
102102
}
103103

104-
private void doStep(ConstructionHeuristicStepScope<Solution_> stepScope) {
104+
protected void doStep(ConstructionHeuristicStepScope<Solution_> stepScope) {
105105
var step = stepScope.getStep();
106106
step.execute(stepScope.getMoveDirector());
107107
predictWorkingStepScore(stepScope, step);

core/src/main/java/ai/timefold/solver/core/impl/constructionheuristic/placer/AbstractEntityPlacer.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package ai.timefold.solver.core.impl.constructionheuristic.placer;
22

3+
import ai.timefold.solver.core.impl.heuristic.HeuristicConfigPolicy;
34
import ai.timefold.solver.core.impl.phase.event.PhaseLifecycleSupport;
45
import ai.timefold.solver.core.impl.phase.scope.AbstractPhaseScope;
56
import ai.timefold.solver.core.impl.phase.scope.AbstractStepScope;
@@ -17,8 +18,24 @@ public abstract class AbstractEntityPlacer<Solution_> implements EntityPlacer<So
1718

1819
protected final transient Logger logger = LoggerFactory.getLogger(getClass());
1920

21+
protected final EntityPlacerFactory<Solution_> factory;
22+
protected final HeuristicConfigPolicy<Solution_> configPolicy;
23+
2024
protected PhaseLifecycleSupport<Solution_> phaseLifecycleSupport = new PhaseLifecycleSupport<>();
2125

26+
AbstractEntityPlacer(EntityPlacerFactory<Solution_> factory, HeuristicConfigPolicy<Solution_> configPolicy) {
27+
this.factory = factory;
28+
this.configPolicy = configPolicy;
29+
}
30+
31+
@Override
32+
public EntityPlacer<Solution_> copy() {
33+
if (factory == null || configPolicy == null) {
34+
throw new IllegalStateException("The entity placer cannot be copied.");
35+
}
36+
return factory.buildEntityPlacer(configPolicy.copyConfigPolicy());
37+
}
38+
2239
@Override
2340
public void solvingStarted(SolverScope<Solution_> solverScope) {
2441
phaseLifecycleSupport.fireSolvingStarted(solverScope);

core/src/main/java/ai/timefold/solver/core/impl/constructionheuristic/placer/EntityPlacer.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ public interface EntityPlacer<Solution_> extends Iterable<Placement<Solution_>>,
77

88
EntityPlacer<Solution_> rebuildWithFilter(SelectionFilter<Solution_, Object> filter);
99

10+
EntityPlacer<Solution_> copy();
1011
}

core/src/main/java/ai/timefold/solver/core/impl/constructionheuristic/placer/PooledEntityPlacer.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import java.util.Iterator;
44

5+
import ai.timefold.solver.core.impl.heuristic.HeuristicConfigPolicy;
56
import ai.timefold.solver.core.impl.heuristic.move.Move;
67
import ai.timefold.solver.core.impl.heuristic.selector.common.decorator.SelectionFilter;
78
import ai.timefold.solver.core.impl.heuristic.selector.common.iterator.UpcomingSelectionIterator;
@@ -13,7 +14,9 @@ public class PooledEntityPlacer<Solution_> extends AbstractEntityPlacer<Solution
1314

1415
protected final MoveSelector<Solution_> moveSelector;
1516

16-
public PooledEntityPlacer(MoveSelector<Solution_> moveSelector) {
17+
public PooledEntityPlacer(EntityPlacerFactory<Solution_> factory, HeuristicConfigPolicy<Solution_> configPolicy,
18+
MoveSelector<Solution_> moveSelector) {
19+
super(factory, configPolicy);
1720
this.moveSelector = moveSelector;
1821
phaseLifecycleSupport.addEventListener(moveSelector);
1922
}
@@ -25,7 +28,7 @@ public Iterator<Placement<Solution_>> iterator() {
2528

2629
@Override
2730
public EntityPlacer<Solution_> rebuildWithFilter(SelectionFilter<Solution_, Object> filter) {
28-
return new PooledEntityPlacer<>(FilteringMoveSelector.of(moveSelector, filter::accept));
31+
return new PooledEntityPlacer<>(factory, configPolicy, FilteringMoveSelector.of(moveSelector, filter::accept));
2932
}
3033

3134
private class PooledEntityPlacingIterator extends UpcomingSelectionIterator<Placement<Solution_>> {

core/src/main/java/ai/timefold/solver/core/impl/constructionheuristic/placer/PooledEntityPlacerFactory.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ public PooledEntityPlacer<Solution_> buildEntityPlacer(HeuristicConfigPolicy<Sol
6969

7070
MoveSelector<Solution_> moveSelector = MoveSelectorFactory.<Solution_> create(moveSelectorConfig_)
7171
.buildMoveSelector(configPolicy, SelectionCacheType.JUST_IN_TIME, SelectionOrder.ORIGINAL, false);
72-
return new PooledEntityPlacer<>(moveSelector);
72+
return new PooledEntityPlacer<>(this, configPolicy, moveSelector);
7373
}
7474

7575
private MoveSelectorConfig buildMoveSelectorConfig(HeuristicConfigPolicy<Solution_> configPolicy) {

core/src/main/java/ai/timefold/solver/core/impl/constructionheuristic/placer/QueuedEntityPlacer.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import java.util.Iterator;
55
import java.util.List;
66

7+
import ai.timefold.solver.core.impl.heuristic.HeuristicConfigPolicy;
78
import ai.timefold.solver.core.impl.heuristic.move.Move;
89
import ai.timefold.solver.core.impl.heuristic.selector.common.decorator.SelectionFilter;
910
import ai.timefold.solver.core.impl.heuristic.selector.common.iterator.UpcomingSelectionIterator;
@@ -17,7 +18,9 @@ public class QueuedEntityPlacer<Solution_> extends AbstractEntityPlacer<Solution
1718
protected final EntitySelector<Solution_> entitySelector;
1819
protected final List<MoveSelector<Solution_>> moveSelectorList;
1920

20-
public QueuedEntityPlacer(EntitySelector<Solution_> entitySelector, List<MoveSelector<Solution_>> moveSelectorList) {
21+
public QueuedEntityPlacer(EntityPlacerFactory<Solution_> factory, HeuristicConfigPolicy<Solution_> configPolicy,
22+
EntitySelector<Solution_> entitySelector, List<MoveSelector<Solution_>> moveSelectorList) {
23+
super(factory, configPolicy);
2124
this.entitySelector = entitySelector;
2225
this.moveSelectorList = moveSelectorList;
2326
phaseLifecycleSupport.addEventListener(entitySelector);
@@ -33,7 +36,8 @@ public Iterator<Placement<Solution_>> iterator() {
3336

3437
@Override
3538
public EntityPlacer<Solution_> rebuildWithFilter(SelectionFilter<Solution_, Object> filter) {
36-
return new QueuedEntityPlacer<>(FilteringEntitySelector.of(entitySelector, filter), moveSelectorList);
39+
return new QueuedEntityPlacer<>(factory, configPolicy, FilteringEntitySelector.of(entitySelector, filter),
40+
moveSelectorList);
3741
}
3842

3943
private class QueuedEntityPlacingIterator extends UpcomingSelectionIterator<Placement<Solution_>> {

core/src/main/java/ai/timefold/solver/core/impl/constructionheuristic/placer/QueuedEntityPlacerFactory.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ public QueuedEntityPlacer<Solution_> buildEntityPlacer(HeuristicConfigPolicy<Sol
7676
.buildMoveSelector(configPolicy, SelectionCacheType.JUST_IN_TIME, SelectionOrder.ORIGINAL, false);
7777
moveSelectorList.add(moveSelector);
7878
}
79-
return new QueuedEntityPlacer<>(entitySelector, moveSelectorList);
79+
return new QueuedEntityPlacer<>(this, configPolicy, entitySelector, moveSelectorList);
8080
}
8181

8282
@SuppressWarnings("rawtypes")

core/src/main/java/ai/timefold/solver/core/impl/constructionheuristic/placer/QueuedValuePlacer.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import java.util.Collections;
44
import java.util.Iterator;
55

6+
import ai.timefold.solver.core.impl.heuristic.HeuristicConfigPolicy;
67
import ai.timefold.solver.core.impl.heuristic.selector.common.decorator.SelectionFilter;
78
import ai.timefold.solver.core.impl.heuristic.selector.common.iterator.UpcomingSelectionIterator;
89
import ai.timefold.solver.core.impl.heuristic.selector.move.MoveSelector;
@@ -16,8 +17,9 @@ public class QueuedValuePlacer<Solution_> extends AbstractEntityPlacer<Solution_
1617
protected final EntityIndependentValueSelector<Solution_> valueSelector;
1718
protected final MoveSelector<Solution_> moveSelector;
1819

19-
public QueuedValuePlacer(EntityIndependentValueSelector<Solution_> valueSelector,
20-
MoveSelector<Solution_> moveSelector) {
20+
public QueuedValuePlacer(EntityPlacerFactory<Solution_> factory, HeuristicConfigPolicy<Solution_> configPolicy,
21+
EntityIndependentValueSelector<Solution_> valueSelector, MoveSelector<Solution_> moveSelector) {
22+
super(factory, configPolicy);
2123
this.valueSelector = valueSelector;
2224
this.moveSelector = moveSelector;
2325
phaseLifecycleSupport.addEventListener(valueSelector);
@@ -59,7 +61,7 @@ protected Placement<Solution_> createUpcomingSelection() {
5961

6062
@Override
6163
public EntityPlacer<Solution_> rebuildWithFilter(SelectionFilter<Solution_, Object> filter) {
62-
return new QueuedValuePlacer<>(
64+
return new QueuedValuePlacer<>(factory, configPolicy,
6365
(EntityIndependentFilteringValueSelector<Solution_>) FilteringValueSelector.of(valueSelector, filter),
6466
moveSelector);
6567
}

core/src/main/java/ai/timefold/solver/core/impl/constructionheuristic/placer/QueuedValuePlacerFactory.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@ public QueuedValuePlacer<Solution_> buildEntityPlacer(HeuristicConfigPolicy<Solu
5555
+ " Check your @" + ValueRangeProvider.class.getSimpleName() + " annotations.");
5656

5757
}
58-
return new QueuedValuePlacer<>((EntityIndependentValueSelector<Solution_>) valueSelector, moveSelector);
58+
return new QueuedValuePlacer<>(this, configPolicy, (EntityIndependentValueSelector<Solution_>) valueSelector,
59+
moveSelector);
5960
}
6061

6162
private ValueSelectorConfig buildValueSelectorConfig(HeuristicConfigPolicy<Solution_> configPolicy,

core/src/main/java/ai/timefold/solver/core/impl/heuristic/HeuristicConfigPolicy.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,16 @@ public Builder<Solution_> cloneBuilder() {
147147
.withLogIndentation(logIndentation);
148148
}
149149

150+
public HeuristicConfigPolicy<Solution_> copyConfigPolicy() {
151+
return cloneBuilder()
152+
.withEntitySorterManner(entitySorterManner)
153+
.withValueSorterManner(valueSorterManner)
154+
.withReinitializeVariableFilterEnabled(reinitializeVariableFilterEnabled)
155+
.withInitializedChainedValueFilterEnabled(initializedChainedValueFilterEnabled)
156+
.withUnassignedValuesAllowed(unassignedValuesAllowed)
157+
.build();
158+
}
159+
150160
public HeuristicConfigPolicy<Solution_> createPhaseConfigPolicy() {
151161
return cloneBuilder().build();
152162
}

0 commit comments

Comments
 (0)