Skip to content

Commit db846a8

Browse files
committed
test: custom phase and move
1 parent e6843d4 commit db846a8

4 files changed

Lines changed: 127 additions & 0 deletions

File tree

core/src/test/java/ai/timefold/solver/core/impl/solver/DefaultSolverTest.java

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
import ai.timefold.solver.core.config.heuristic.selector.list.SubListSelectorConfig;
5151
import ai.timefold.solver.core.config.heuristic.selector.move.MoveSelectorConfig;
5252
import ai.timefold.solver.core.config.heuristic.selector.move.composite.UnionMoveSelectorConfig;
53+
import ai.timefold.solver.core.config.heuristic.selector.move.factory.MoveIteratorFactoryConfig;
5354
import ai.timefold.solver.core.config.heuristic.selector.move.generic.ChangeMoveSelectorConfig;
5455
import ai.timefold.solver.core.config.heuristic.selector.move.generic.PillarChangeMoveSelectorConfig;
5556
import ai.timefold.solver.core.config.heuristic.selector.move.generic.PillarSwapMoveSelectorConfig;
@@ -118,6 +119,8 @@
118119
import ai.timefold.solver.core.testdomain.mixed.multientity.TestdataMixedMultiEntityFirstEntity;
119120
import ai.timefold.solver.core.testdomain.mixed.multientity.TestdataMixedMultiEntitySecondEntity;
120121
import ai.timefold.solver.core.testdomain.mixed.multientity.TestdataMixedMultiEntitySolution;
122+
import ai.timefold.solver.core.testdomain.mixed.singleentity.MixedCustomBasicVariableFactory;
123+
import ai.timefold.solver.core.testdomain.mixed.singleentity.MixedCustomPhase;
121124
import ai.timefold.solver.core.testdomain.mixed.singleentity.TestdataMixedEasyScoreCalculator;
122125
import ai.timefold.solver.core.testdomain.mixed.singleentity.TestdataMixedEntity;
123126
import ai.timefold.solver.core.testdomain.mixed.singleentity.TestdataMixedOtherValue;
@@ -1682,6 +1685,49 @@ void solveMixedModel() {
16821685
.isTrue();
16831686
}
16841687

1688+
@Test
1689+
void solveMixedModelCustomMove() {
1690+
var solverConfig = PlannerTestUtils.buildSolverConfig(
1691+
TestdataMixedSolution.class, TestdataMixedEntity.class, TestdataMixedValue.class,
1692+
TestdataMixedOtherValue.class)
1693+
.withPreviewFeature(DECLARATIVE_SHADOW_VARIABLES)
1694+
.withPhases(new ConstructionHeuristicPhaseConfig(),
1695+
new LocalSearchPhaseConfig()
1696+
.withMoveSelectorConfig(new MoveIteratorFactoryConfig()
1697+
.withMoveIteratorFactoryClass(MixedCustomBasicVariableFactory.class))
1698+
.withTerminationConfig(new TerminationConfig().withStepCountLimit(16)))
1699+
.withEasyScoreCalculatorClass(TestdataMixedEasyScoreCalculator.class);
1700+
1701+
var problem = TestdataMixedSolution.generateUninitializedSolution(2, 2, 2);
1702+
var solution = PlannerTestUtils.solve(solverConfig, problem);
1703+
1704+
// Check the solution
1705+
assertThat(solution.getEntityList().stream()
1706+
.filter(e -> e.getBasicValue() == null || e.getSecondBasicValue() == null || e.getValueList().isEmpty()))
1707+
.isEmpty();
1708+
}
1709+
1710+
@Test
1711+
void solveMixedModelCustomPhase() {
1712+
var solverConfig = PlannerTestUtils.buildSolverConfig(
1713+
TestdataMixedSolution.class, TestdataMixedEntity.class, TestdataMixedValue.class,
1714+
TestdataMixedOtherValue.class)
1715+
.withPreviewFeature(DECLARATIVE_SHADOW_VARIABLES)
1716+
.withPhases(new ConstructionHeuristicPhaseConfig(),
1717+
new CustomPhaseConfig()
1718+
.withCustomPhaseCommands(new MixedCustomPhase())
1719+
.withTerminationConfig(new TerminationConfig().withStepCountLimit(16)))
1720+
.withEasyScoreCalculatorClass(TestdataMixedEasyScoreCalculator.class);
1721+
1722+
var problem = TestdataMixedSolution.generateUninitializedSolution(2, 2, 2);
1723+
var solution = PlannerTestUtils.solve(solverConfig, problem);
1724+
1725+
// Check the solution
1726+
assertThat(solution.getEntityList().stream()
1727+
.filter(e -> e.getBasicValue() == null || e.getSecondBasicValue() == null || e.getValueList().isEmpty()))
1728+
.isEmpty();
1729+
}
1730+
16851731
private static List<Pair<EntitySorterManner, ValueSorterManner>> getSortMannerList() {
16861732
var sortMannerList = new ArrayList<Pair<EntitySorterManner, ValueSorterManner>>();
16871733
for (var valueSortManner : ValueSorterManner.values()) {
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package ai.timefold.solver.core.testdomain.mixed.singleentity;
2+
3+
import java.util.Iterator;
4+
import java.util.Random;
5+
import java.util.stream.Stream;
6+
7+
import ai.timefold.solver.core.api.score.director.ScoreDirector;
8+
import ai.timefold.solver.core.impl.heuristic.selector.move.factory.MoveIteratorFactory;
9+
10+
public class MixedCustomBasicVariableFactory
11+
implements MoveIteratorFactory<TestdataMixedSolution, MixedCustomBasicVariableSwapMove> {
12+
@Override
13+
public long getSize(ScoreDirector<TestdataMixedSolution> scoreDirector) {
14+
return scoreDirector.getWorkingSolution().getEntityList().size();
15+
}
16+
17+
@Override
18+
public Iterator<MixedCustomBasicVariableSwapMove>
19+
createOriginalMoveIterator(ScoreDirector<TestdataMixedSolution> scoreDirector) {
20+
throw new UnsupportedOperationException();
21+
}
22+
23+
@Override
24+
public Iterator<MixedCustomBasicVariableSwapMove>
25+
createRandomMoveIterator(ScoreDirector<TestdataMixedSolution> scoreDirector, Random workingRandom) {
26+
var move1 = new MixedCustomBasicVariableSwapMove(scoreDirector.getWorkingSolution().getEntityList().get(0),
27+
scoreDirector.getWorkingSolution().getEntityList().get(1));
28+
var move2 = new MixedCustomBasicVariableSwapMove(scoreDirector.getWorkingSolution().getEntityList().get(1),
29+
scoreDirector.getWorkingSolution().getEntityList().get(0));
30+
return Stream.of(move1, move2).iterator();
31+
}
32+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package ai.timefold.solver.core.testdomain.mixed.singleentity;
2+
3+
import ai.timefold.solver.core.api.score.director.ScoreDirector;
4+
import ai.timefold.solver.core.impl.heuristic.move.AbstractMove;
5+
6+
public class MixedCustomBasicVariableSwapMove extends AbstractMove<TestdataMixedSolution> {
7+
8+
private final TestdataMixedEntity left;
9+
private final TestdataMixedEntity right;
10+
11+
public MixedCustomBasicVariableSwapMove(TestdataMixedEntity left, TestdataMixedEntity right) {
12+
this.left = left;
13+
this.right = right;
14+
}
15+
16+
@Override
17+
protected void doMoveOnGenuineVariables(ScoreDirector<TestdataMixedSolution> scoreDirector) {
18+
scoreDirector.beforeVariableChanged(left, "basicValue");
19+
scoreDirector.beforeVariableChanged(right, "basicValue");
20+
var oldValue = left.getBasicValue();
21+
left.setBasicValue(right.getBasicValue());
22+
right.setBasicValue(oldValue);
23+
scoreDirector.afterVariableChanged(left, "basicValue");
24+
scoreDirector.afterVariableChanged(right, "basicValue");
25+
}
26+
27+
@Override
28+
public boolean isMoveDoable(ScoreDirector<TestdataMixedSolution> scoreDirector) {
29+
return left != right;
30+
}
31+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package ai.timefold.solver.core.testdomain.mixed.singleentity;
2+
3+
import java.util.function.BooleanSupplier;
4+
5+
import ai.timefold.solver.core.api.score.director.ScoreDirector;
6+
import ai.timefold.solver.core.api.solver.phase.PhaseCommand;
7+
8+
public class MixedCustomPhase implements PhaseCommand<TestdataMixedSolution> {
9+
10+
@Override
11+
public void changeWorkingSolution(ScoreDirector<TestdataMixedSolution> scoreDirector, BooleanSupplier isPhaseTerminated) {
12+
var moveIteratorFactory = new MixedCustomBasicVariableFactory();
13+
var moveIterator = moveIteratorFactory.createRandomMoveIterator(scoreDirector, null);
14+
var move = moveIterator.next();
15+
move.doMoveOnGenuineVariables(scoreDirector);
16+
scoreDirector.triggerVariableListeners();
17+
}
18+
}

0 commit comments

Comments
 (0)