refactor: unify move interfaces#2127
Conversation
There was a problem hiding this comment.
Pull request overview
This PR unifies the old and new Move interfaces into a single interface hierarchy. It introduces AbstractSelectorBasedMove as a non-public extension to handle move doability for existing generic move selectors. The refactoring involves extensive renaming of move classes (prefixing with "SelectorBased"), updating imports from core.impl.heuristic.move to core.preview.api.move, and updating method signatures throughout the codebase. Additionally, it exposes Neighborhoods as a preview feature and updates documentation references.
Changes:
- Unified move interfaces with
AbstractSelectorBasedMovefor backward compatibility - Renamed move classes with "SelectorBased" prefix (e.g.,
ChangeMove→SelectorBasedChangeMove) - Updated imports from legacy to preview API package
- Changed method signatures (
rebase,execute,describe) - Updated documentation links and preview feature status
- Removed deprecated classes and adapters
Reviewed changes
Copilot reviewed 157 out of 157 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| Documentation files (*.adoc) | Updated cross-references and changed Neighborhoods from "research project" to "preview feature" |
| Benchmark statistic files | Updated import statements and class references to use new naming |
| Test files | Extensive renaming of move classes with "SelectorBased" prefix and method signature updates |
| Core move classes | Replaced AbstractMove with AbstractSelectorBasedMove, updated rebase() and execute() signatures |
| Move selector files | Updated to use new move class names and imports |
| Utility files | Removed adapter classes, simplified move handling |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 180 out of 180 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (2)
core/src/test/java/ai/timefold/solver/core/impl/heuristic/selector/move/generic/list/SelectorBasedListUnassignMoveTest.java:34
- This test calls move.execute(scoreDirector) with scoreDirector being an InnerScoreDirector mock, but Move.execute expects a MutableSolutionView (typically MoveDirector), so this should not compile or will fail at runtime if forced via casts.
core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/move/generic/list/SelectorBasedSubListUnassignMove.java:103 - SelectorBasedSubListUnassignMove.getPlanningValues() returns the planningValues field which is nullable and only set during execute(); this violates the non-null return contract and can cause NPEs if getPlanningValues() is called before execute().
core/src/main/java/ai/timefold/solver/core/impl/bavet/common/AbstractFlattenNode.java
Show resolved
Hide resolved
docs/src/modules/ROOT/pages/optimization-algorithms/move-selector-reference.adoc
Show resolved
Hide resolved
Christopher-Chianelli
left a comment
There was a problem hiding this comment.
I believe there is a bug in the new SelectorBasedCompositeMove; it has a field Move[] moves, but its rebase expects AbstractSelectorBasedMove (whereas its execute work with both AbstractSelectorBasedMove moves and normal moves).
A lot of suggestions to change Type x = ... to var x = ... where possible.
Once the bug is fixed, LGTM.
core/src/main/java/ai/timefold/solver/core/impl/heuristic/move/SelectorBasedCompositeMove.java
Outdated
Show resolved
Hide resolved
...efold/solver/core/impl/heuristic/selector/move/generic/list/SelectorBasedListAssignMove.java
Outdated
Show resolved
Hide resolved
...efold/solver/core/impl/heuristic/selector/move/generic/list/SelectorBasedListAssignMove.java
Outdated
Show resolved
Hide resolved
...efold/solver/core/impl/heuristic/selector/move/generic/list/SelectorBasedListChangeMove.java
Outdated
Show resolved
Hide resolved
...imefold/solver/core/impl/heuristic/selector/move/generic/list/SelectorBasedListSwapMove.java
Outdated
Show resolved
Hide resolved
core/src/test/java/ai/timefold/solver/core/impl/solver/DefaultSolverTest.java
Outdated
Show resolved
Hide resolved
core/src/test/java/ai/timefold/solver/core/impl/solver/DefaultSolverTest.java
Outdated
Show resolved
Hide resolved
...va/ai/timefold/solver/core/testdomain/mixed/singleentity/MixedCustomMoveIteratorFactory.java
Outdated
Show resolved
Hide resolved
...va/ai/timefold/solver/core/testdomain/mixed/singleentity/MixedCustomMoveIteratorFactory.java
Outdated
Show resolved
Hide resolved
core/src/test/java/ai/timefold/solver/core/testutil/CodeAssertable.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 180 out of 180 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (4)
core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/move/generic/list/SelectorBasedListSwapMove.java:119
- SelectorBasedListSwapMove should use reference equality (leftEntity == rightEntity) when deciding move doability; using Objects.equals() can incorrectly treat distinct planning entities as the same if the domain overrides equals().
core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/move/generic/list/SelectorBasedListSwapMove.java:127 - SelectorBasedListSwapMove should use reference equality (leftEntity == rightEntity) when deciding whether to treat this as an in-list swap vs cross-entity swap; Objects.equals() may route execution down the wrong branch for entities with custom equals().
core/src/main/java/ai/timefold/solver/core/impl/heuristic/move/SelectorBasedCompositeMove.java:79 - SelectorBasedCompositeMove.isMoveDoable() returns false when all child moves are "new" (non-AbstractSelectorBasedMove) moves, even though those moves are considered always doable; include new moves in the doability calculation.
core/src/main/java/ai/timefold/solver/core/impl/heuristic/move/SelectorBasedCompositeMove.java:91 - SelectorBasedCompositeMove.execute() calls legacyMove.execute(scoreDirector) directly, which skips triggerVariableListeners() between child moves and can break child moves that rely on up-to-date shadow variables; execute each child via the MoveDirector (or trigger listeners after each child) to preserve the previous behavior.
...a/ai/timefold/solver/core/impl/heuristic/selector/move/generic/list/kopt/KOptDescriptor.java
Show resolved
Hide resolved
|



This PR takes both the old and new
Moveinterfaces, 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 ofMovewhich allows to deal with move doability. I am adding theSelectoBasedprefix 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.