Skip to content

Commit 8f37ec3

Browse files
authored
refactor: unify move interfaces (#2127)
This PR takes both the old and new `Move` interfaces, and merges them into one. In order to not require large changes to the existing generic move selectors, it also introduces `AbstractSelectorBasedMove`, a non-public extension of `Move` which allows to deal with move doability. I am adding the `SelectoBased` prefix to all such move implementations, to distinguish them in profiling and stack traces from the Neighborhoods-based equivalents we either already have or will create. It also makes large changes to the documentation around moves, and finally exposes Neighborhoods as a documented preview feature.
1 parent 88b96cb commit 8f37ec3

180 files changed

Lines changed: 3372 additions & 4073 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

core/src/main/java/ai/timefold/solver/core/api/domain/common/ComparatorFactory.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
import ai.timefold.solver.core.config.constructionheuristic.ConstructionHeuristicPhaseConfig;
88
import ai.timefold.solver.core.config.heuristic.selector.entity.EntitySelectorConfig;
99
import ai.timefold.solver.core.config.heuristic.selector.value.ValueSelectorConfig;
10-
import ai.timefold.solver.core.impl.heuristic.move.Move;
1110
import ai.timefold.solver.core.impl.heuristic.selector.Selector;
11+
import ai.timefold.solver.core.preview.api.move.Move;
1212

1313
import org.jspecify.annotations.NullMarked;
1414

core/src/main/java/ai/timefold/solver/core/api/domain/common/PlanningId.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
import ai.timefold.solver.core.api.domain.valuerange.ValueRangeProvider;
1313
import ai.timefold.solver.core.api.score.director.ScoreDirector;
1414
import ai.timefold.solver.core.api.solver.change.ProblemChange;
15-
import ai.timefold.solver.core.impl.heuristic.move.Move;
15+
import ai.timefold.solver.core.preview.api.move.Move;
1616

1717
/**
1818
* Specifies that a bean property (or a field) is the id to match

core/src/main/java/ai/timefold/solver/core/api/score/analysis/ConstraintAnalysis.java

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

66
import java.util.Comparator;
77
import java.util.HashSet;
8+
import java.util.LinkedHashMap;
89
import java.util.List;
910
import java.util.Map;
1011
import java.util.Objects;
@@ -14,7 +15,6 @@
1415
import ai.timefold.solver.core.api.score.constraint.ConstraintRef;
1516
import ai.timefold.solver.core.api.score.stream.ConstraintJustification;
1617
import ai.timefold.solver.core.api.solver.SolutionManager;
17-
import ai.timefold.solver.core.impl.util.CollectionUtils;
1818

1919
import org.jspecify.annotations.NullMarked;
2020
import org.jspecify.annotations.Nullable;
@@ -154,8 +154,9 @@ private static int getMatchCount(ConstraintAnalysis<?> analysis, ConstraintAnaly
154154

155155
private static <Score_ extends Score<Score_>> Map<ConstraintJustification, MatchAnalysis<Score_>>
156156
mapMatchesToJustifications(List<MatchAnalysis<Score_>> matchAnalyses) {
157+
157158
Map<ConstraintJustification, MatchAnalysis<Score_>> matchAnalysisMap =
158-
CollectionUtils.newLinkedHashMap(matchAnalyses.size());
159+
LinkedHashMap.newLinkedHashMap(matchAnalyses.size());
159160
for (var matchAnalysis : matchAnalyses) {
160161
var previous = matchAnalysisMap.put(matchAnalysis.justification(), matchAnalysis);
161162
if (previous != null) {

core/src/main/java/ai/timefold/solver/core/api/solver/change/ProblemChange.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import ai.timefold.solver.core.api.score.Score;
77
import ai.timefold.solver.core.api.solver.Solver;
88
import ai.timefold.solver.core.api.solver.event.BestSolutionChangedEvent;
9-
import ai.timefold.solver.core.impl.heuristic.move.Move;
9+
import ai.timefold.solver.core.preview.api.move.Move;
1010

1111
import org.jspecify.annotations.NullMarked;
1212

core/src/main/java/ai/timefold/solver/core/config/solver/EnvironmentMode.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
import ai.timefold.solver.core.api.domain.entity.PlanningEntity;
1010
import ai.timefold.solver.core.api.domain.variable.ShadowVariable;
1111
import ai.timefold.solver.core.api.solver.Solver;
12-
import ai.timefold.solver.core.impl.heuristic.move.Move;
1312
import ai.timefold.solver.core.impl.score.director.InnerScoreDirector;
13+
import ai.timefold.solver.core.preview.api.move.Move;
1414

1515
/**
1616
* The environment mode also allows you to detect common bugs in your implementation.

core/src/main/java/ai/timefold/solver/core/impl/bavet/common/AbstractFlattenNode.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
import ai.timefold.solver.core.impl.bavet.common.tuple.Tuple;
1313
import ai.timefold.solver.core.impl.bavet.common.tuple.TupleLifecycle;
1414
import ai.timefold.solver.core.impl.bavet.common.tuple.TupleState;
15-
import ai.timefold.solver.core.impl.util.CollectionUtils;
1615

1716
public abstract class AbstractFlattenNode<InTuple_ extends Tuple, OutTuple_ extends Tuple, FlattenedItem_>
1817
extends AbstractNode
@@ -124,7 +123,8 @@ private record FlattenBagByItem<FlattenedItem_, OutTuple_>(
124123
}
125124

126125
FlattenBagByItem(int size) {
127-
this(CollectionUtils.newLinkedHashMap(size));
126+
127+
this(LinkedHashMap.newLinkedHashMap(size));
128128
}
129129

130130
/**

core/src/main/java/ai/timefold/solver/core/impl/bavet/common/RecordAndReplayPropagator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ public RecordAndReplayPropagator(Supplier<BavetPrecomputeBuildHelper<Tuple_>> pr
5050
UnaryOperator<Tuple_> internalTupleToOutputTupleMapper, TupleLifecycle<Tuple_> nextNodesTupleLifecycle, int size) {
5151
this.precomputeBuildHelperSupplier = precomputeBuildHelperSupplier;
5252
this.internalTupleToOutputTupleMapper = internalTupleToOutputTupleMapper;
53-
this.objectToOutputTuplesMap = CollectionUtils.newIdentityHashMap(size);
53+
this.objectToOutputTuplesMap = new IdentityHashMap<>(size);
5454

5555
// Guesstimate that updates are dominant.
5656
this.retractQueue = CollectionUtils.newIdentityHashSet(size / 20);

core/src/main/java/ai/timefold/solver/core/impl/constructionheuristic/decider/ConstructionHeuristicDecider.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
import ai.timefold.solver.core.impl.constructionheuristic.scope.ConstructionHeuristicPhaseScope;
1010
import ai.timefold.solver.core.impl.constructionheuristic.scope.ConstructionHeuristicStepScope;
1111
import ai.timefold.solver.core.impl.heuristic.move.MoveAdapters;
12-
import ai.timefold.solver.core.impl.heuristic.move.NoChangeMove;
12+
import ai.timefold.solver.core.impl.heuristic.move.SelectorBasedNoChangeMove;
13+
import ai.timefold.solver.core.impl.heuristic.selector.move.generic.SelectorBasedChangeMove;
1314
import ai.timefold.solver.core.impl.phase.scope.SolverLifecyclePoint;
1415
import ai.timefold.solver.core.impl.solver.scope.SolverScope;
1516
import ai.timefold.solver.core.impl.solver.termination.PhaseTermination;
@@ -130,8 +131,8 @@ public void decideNextStep(ConstructionHeuristicStepScope<Solution_> stepScope,
130131
}
131132

132133
private static <Solution_> boolean isAllowedNonDoableMove(Move<Solution_> move) {
133-
return MoveAdapters.testWhenLegacyMove(move, legacyMove -> legacyMove instanceof NoChangeMove<Solution_>
134-
|| legacyMove instanceof ai.timefold.solver.core.impl.heuristic.selector.move.generic.ChangeMove<Solution_>);
134+
return MoveAdapters.testWhenLegacyMove(move, legacyMove -> legacyMove instanceof SelectorBasedNoChangeMove<Solution_>
135+
|| legacyMove instanceof SelectorBasedChangeMove<Solution_>);
135136
}
136137

137138
protected void pickMove(ConstructionHeuristicStepScope<Solution_> stepScope) {

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

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,6 @@
1010
*/
1111
public class Placement<Solution_> implements Iterable<Move<Solution_>> {
1212

13-
@SuppressWarnings({ "unchecked", "rawtypes" })
14-
public static <Solution_> Iterator<ai.timefold.solver.core.preview.api.move.Move<Solution_>>
15-
toNewMoveIterator(Iterator<ai.timefold.solver.core.impl.heuristic.move.Move<Solution_>> legacyIterator) {
16-
return (Iterator) legacyIterator;
17-
}
18-
1913
private final Iterator<Move<Solution_>> moveIterator;
2014

2115
public Placement(Iterator<Move<Solution_>> moveIterator) {

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@
33
import java.util.Iterator;
44

55
import ai.timefold.solver.core.impl.heuristic.HeuristicConfigPolicy;
6-
import ai.timefold.solver.core.impl.heuristic.move.Move;
76
import ai.timefold.solver.core.impl.heuristic.selector.common.decorator.SelectionFilter;
87
import ai.timefold.solver.core.impl.heuristic.selector.common.iterator.UpcomingSelectionIterator;
98
import ai.timefold.solver.core.impl.heuristic.selector.move.MoveSelector;
109
import ai.timefold.solver.core.impl.heuristic.selector.move.decorator.FilteringMoveSelector;
10+
import ai.timefold.solver.core.preview.api.move.Move;
1111

1212
public class PooledEntityPlacer<Solution_> extends AbstractEntityPlacer<Solution_> implements EntityPlacer<Solution_> {
1313

@@ -41,7 +41,7 @@ protected Placement<Solution_> createUpcomingSelection() {
4141
if (!moveIterator.hasNext()) {
4242
return noUpcomingSelection();
4343
}
44-
return new Placement<>(Placement.toNewMoveIterator(moveIterator));
44+
return new Placement<>(moveIterator);
4545
}
4646

4747
}

0 commit comments

Comments
 (0)