From 1ab769cc87ff7139fcc931754ac18103bd05cf0b Mon Sep 17 00:00:00 2001 From: Christopher Chianelli Date: Wed, 10 Jun 2026 16:07:04 -0400 Subject: [PATCH] fix: seperate move and solver RandomGenerator usage Before, some foragers and acceptors used the same RandomGenerator as moves, which can affect reproducibility when multithreading is used. Now, foragers and acceptors use their own independent random. --- .../heuristic/selector/AbstractSelector.java | 2 +- .../generic/RuinRecreateMoveSelector.java | 2 +- .../ruin/ListRuinRecreateMoveSelector.java | 2 +- .../SimulatedAnnealingAcceptor.java | 2 +- .../acceptor/tabu/AbstractTabuAcceptor.java | 2 +- .../forager/AcceptedLocalSearchForager.java | 2 +- .../NeighborhoodsBasedMoveRepository.java | 2 +- .../impl/phase/scope/AbstractMoveScope.java | 8 +++-- .../impl/phase/scope/AbstractPhaseScope.java | 8 +++-- .../impl/phase/scope/AbstractStepScope.java | 8 +++-- .../core/impl/solver/AbstractSolver.java | 10 +++---- .../core/impl/solver/DefaultSolver.java | 3 +- .../core/impl/solver/scope/SolverScope.java | 29 ++++++++++++++----- .../heuristic/selector/SelectorTestUtils.java | 2 +- .../FromSolutionEntitySelectorTest.java | 4 +-- .../ProbabilityEntitySelectorTest.java | 2 +- .../pillar/DefaultPillarSelectorTest.java | 6 ++-- .../list/ElementDestinationSelectorTest.java | 6 ++-- .../move/composite/UnionMoveSelectorTest.java | 8 ++--- .../decorator/CachingMoveSelectorTest.java | 2 +- .../ProbabilityMoveSelectorTest.java | 2 +- .../decorator/ShufflingMoveSelectorTest.java | 2 +- .../SimulatedAnnealingAcceptorTest.java | 14 ++++----- .../AcceptedLocalSearchForagerTest.java | 2 +- .../impl/neighborhood/NeighborhoodsTest.java | 2 +- .../core/testutil/PlannerTestUtils.java | 5 ++-- 26 files changed, 83 insertions(+), 54 deletions(-) diff --git a/core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/AbstractSelector.java b/core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/AbstractSelector.java index fdedc4cfda4..df5da84b72a 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/AbstractSelector.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/AbstractSelector.java @@ -26,7 +26,7 @@ public abstract class AbstractSelector implements Selector @Override public void solvingStarted(SolverScope solverScope) { - workingRandom = solverScope.getWorkingRandom(); + workingRandom = solverScope.getMoveGeneratorWorkingRandom(); phaseLifecycleSupport.fireSolvingStarted(solverScope); } diff --git a/core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/move/generic/RuinRecreateMoveSelector.java b/core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/move/generic/RuinRecreateMoveSelector.java index 4d999214e6b..1acb99ed565 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/move/generic/RuinRecreateMoveSelector.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/move/generic/RuinRecreateMoveSelector.java @@ -55,7 +55,7 @@ public boolean isNeverEnding() { public void solvingStarted(SolverScope solverScope) { super.solvingStarted(solverScope); this.solverScope = solverScope; - this.workingRandom = solverScope.getWorkingRandom(); + this.workingRandom = solverScope.getMoveGeneratorWorkingRandom(); } @Override diff --git a/core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/move/generic/list/ruin/ListRuinRecreateMoveSelector.java b/core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/move/generic/list/ruin/ListRuinRecreateMoveSelector.java index c41a21ddd86..a291e7cd7ad 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/move/generic/list/ruin/ListRuinRecreateMoveSelector.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/heuristic/selector/move/generic/list/ruin/ListRuinRecreateMoveSelector.java @@ -70,7 +70,7 @@ public void solvingStarted(SolverScope solverScope) { this.listVariableStateSupply = solverScope.getScoreDirector() .getSupplyManager() .demand(listVariableDescriptor.getStateDemand()); - this.workingRandom = solverScope.getWorkingRandom(); + this.workingRandom = solverScope.getMoveGeneratorWorkingRandom(); } @Override diff --git a/core/src/main/java/ai/timefold/solver/core/impl/localsearch/decider/acceptor/simulatedannealing/SimulatedAnnealingAcceptor.java b/core/src/main/java/ai/timefold/solver/core/impl/localsearch/decider/acceptor/simulatedannealing/SimulatedAnnealingAcceptor.java index a8833327aed..fddc88627dd 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/localsearch/decider/acceptor/simulatedannealing/SimulatedAnnealingAcceptor.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/localsearch/decider/acceptor/simulatedannealing/SimulatedAnnealingAcceptor.java @@ -75,7 +75,7 @@ public boolean isAccepted(LocalSearchMoveScope moveScope) { } acceptChance *= acceptChanceLevel; } - return moveScope.getWorkingRandom().nextDouble() < acceptChance; + return moveScope.getSolverWorkingRandom().nextDouble() < acceptChance; } @Override diff --git a/core/src/main/java/ai/timefold/solver/core/impl/localsearch/decider/acceptor/tabu/AbstractTabuAcceptor.java b/core/src/main/java/ai/timefold/solver/core/impl/localsearch/decider/acceptor/tabu/AbstractTabuAcceptor.java index 893ff43a3a4..e3b7709661c 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/localsearch/decider/acceptor/tabu/AbstractTabuAcceptor.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/localsearch/decider/acceptor/tabu/AbstractTabuAcceptor.java @@ -143,7 +143,7 @@ public boolean isAccepted(LocalSearchMoveScope moveScope) { return false; } var acceptChance = calculateFadingTabuAcceptChance(tabuStepCount - workingTabuSize); - var accepted = moveScope.getWorkingRandom().nextDouble() < acceptChance; + var accepted = moveScope.getSolverWorkingRandom().nextDouble() < acceptChance; if (accepted) { logger.trace("{} Proposed move ({}) is fading tabu with acceptChance ({}) and is accepted.", logIndentation, diff --git a/core/src/main/java/ai/timefold/solver/core/impl/localsearch/decider/forager/AcceptedLocalSearchForager.java b/core/src/main/java/ai/timefold/solver/core/impl/localsearch/decider/forager/AcceptedLocalSearchForager.java index f751a18212e..a238cfe2cde 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/localsearch/decider/forager/AcceptedLocalSearchForager.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/localsearch/decider/forager/AcceptedLocalSearchForager.java @@ -121,7 +121,7 @@ public LocalSearchMoveScope pickMove(LocalSearchStepScope if (finalistList.size() == 1 || !breakTieRandomly) { return finalistList.get(0); } - int randomIndex = stepScope.getWorkingRandom().nextInt(finalistList.size()); + int randomIndex = stepScope.getSolverWorkingRandom().nextInt(finalistList.size()); return finalistList.get(randomIndex); } diff --git a/core/src/main/java/ai/timefold/solver/core/impl/neighborhood/NeighborhoodsBasedMoveRepository.java b/core/src/main/java/ai/timefold/solver/core/impl/neighborhood/NeighborhoodsBasedMoveRepository.java index ee7afd1041f..91e175d7111 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/neighborhood/NeighborhoodsBasedMoveRepository.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/neighborhood/NeighborhoodsBasedMoveRepository.java @@ -73,7 +73,7 @@ public void solvingStarted(SolverScope solverScope) { @Override public void phaseStarted(AbstractPhaseScope phaseScope) { - workingRandom = phaseScope.getWorkingRandom(); + workingRandom = phaseScope.getMoveGeneratorWorkingRandom(); phaseScope.getScoreDirector().setMoveRepository(this); } diff --git a/core/src/main/java/ai/timefold/solver/core/impl/phase/scope/AbstractMoveScope.java b/core/src/main/java/ai/timefold/solver/core/impl/phase/scope/AbstractMoveScope.java index 604360f09e5..ab94b248766 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/phase/scope/AbstractMoveScope.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/phase/scope/AbstractMoveScope.java @@ -66,8 +66,12 @@ public Solution_ getWorkingSolution() { return getStepScope().getWorkingSolution(); } - public RandomGenerator getWorkingRandom() { - return getStepScope().getWorkingRandom(); + public RandomGenerator getMoveGeneratorWorkingRandom() { + return getStepScope().getMoveGeneratorWorkingRandom(); + } + + public RandomGenerator getSolverWorkingRandom() { + return getStepScope().getSolverWorkingRandom(); } @Override diff --git a/core/src/main/java/ai/timefold/solver/core/impl/phase/scope/AbstractPhaseScope.java b/core/src/main/java/ai/timefold/solver/core/impl/phase/scope/AbstractPhaseScope.java index 60a9cdf0e41..707ba60ad3f 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/phase/scope/AbstractPhaseScope.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/phase/scope/AbstractPhaseScope.java @@ -239,8 +239,12 @@ public > void assertShadowVariablesAreNotStale(Inne innerScoreDirector.assertShadowVariablesAreNotStale(workingScore, completedAction); } - public RandomGenerator getWorkingRandom() { - return getSolverScope().getWorkingRandom(); + public RandomGenerator getMoveGeneratorWorkingRandom() { + return getSolverScope().getMoveGeneratorWorkingRandom(); + } + + public RandomGenerator getSolverWorkingRandom() { + return getSolverScope().getSolverWorkingRandom(); } public boolean isBestSolutionInitialized() { diff --git a/core/src/main/java/ai/timefold/solver/core/impl/phase/scope/AbstractStepScope.java b/core/src/main/java/ai/timefold/solver/core/impl/phase/scope/AbstractStepScope.java index 5f2e85e50bb..569533216b6 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/phase/scope/AbstractStepScope.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/phase/scope/AbstractStepScope.java @@ -66,8 +66,12 @@ public Solution_ getWorkingSolution() { return getPhaseScope().getWorkingSolution(); } - public RandomGenerator getWorkingRandom() { - return getPhaseScope().getWorkingRandom(); + public RandomGenerator getMoveGeneratorWorkingRandom() { + return getPhaseScope().getMoveGeneratorWorkingRandom(); + } + + public RandomGenerator getSolverWorkingRandom() { + return getPhaseScope().getSolverWorkingRandom(); } public Solution_ cloneWorkingSolution() { diff --git a/core/src/main/java/ai/timefold/solver/core/impl/solver/AbstractSolver.java b/core/src/main/java/ai/timefold/solver/core/impl/solver/AbstractSolver.java index d2b1b96afc3..6b8c89d5936 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/solver/AbstractSolver.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/solver/AbstractSolver.java @@ -48,7 +48,7 @@ public abstract class AbstractSolver implements Solver { protected final UniversalTermination globalTermination; protected final List> phaseList; - private RandomGenerator.@Nullable SplittableGenerator savedRandom; + private RandomGenerator.@Nullable SplittableGenerator savedMoveGeneratorRandom; // ************************************************************************ // Constructors and simple getters/setters @@ -131,16 +131,16 @@ public void stepStarted(AbstractStepScope stepScope) { globalTermination.stepStarted(stepScope); // To ensure reproducibility even when the number of random calls is not deterministic, // split the random at step start. - var delegatingRandom = ((DelegatingSplittableRandomGenerator) stepScope.getWorkingRandom()); - savedRandom = delegatingRandom.getDelegate(); + var delegatingRandom = ((DelegatingSplittableRandomGenerator) stepScope.getMoveGeneratorWorkingRandom()); + savedMoveGeneratorRandom = delegatingRandom.getDelegate(); delegatingRandom.setDelegate(delegatingRandom.split()); // Do not propagate to phases; the active phase does that for itself and they should not propagate further. } public void stepEnded(AbstractStepScope stepScope) { // Restore from the split random - var delegatingRandom = ((DelegatingSplittableRandomGenerator) stepScope.getWorkingRandom()); - delegatingRandom.setDelegate(Objects.requireNonNull(savedRandom)); + var delegatingRandom = ((DelegatingSplittableRandomGenerator) stepScope.getMoveGeneratorWorkingRandom()); + delegatingRandom.setDelegate(Objects.requireNonNull(savedMoveGeneratorRandom)); bestSolutionRecaller.stepEnded(stepScope); phaseLifecycleSupport.fireStepEnded(stepScope); globalTermination.stepEnded(stepScope); diff --git a/core/src/main/java/ai/timefold/solver/core/impl/solver/DefaultSolver.java b/core/src/main/java/ai/timefold/solver/core/impl/solver/DefaultSolver.java index 8541e0e6c01..af0fb3db649 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/solver/DefaultSolver.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/solver/DefaultSolver.java @@ -18,6 +18,7 @@ import ai.timefold.solver.core.impl.phase.Phase; import ai.timefold.solver.core.impl.score.director.InnerScoreDirector; import ai.timefold.solver.core.impl.score.director.ScoreDirectorFactory; +import ai.timefold.solver.core.impl.solver.random.DelegatingSplittableRandomGenerator; import ai.timefold.solver.core.impl.solver.recaller.BestSolutionRecaller; import ai.timefold.solver.core.impl.solver.scope.SolverScope; import ai.timefold.solver.core.impl.solver.termination.BasicPlumbingTermination; @@ -188,7 +189,7 @@ public void outerSolvingStarted(SolverScope solverScope) { solving.set(true); basicPlumbingTermination.resetTerminateEarly(); solverScope.setStartingSolverCount(0); - solverScope.setWorkingRandom(randomFactory.get()); + solverScope.setWorkingRandom((DelegatingSplittableRandomGenerator) randomFactory.get()); } @Override diff --git a/core/src/main/java/ai/timefold/solver/core/impl/solver/scope/SolverScope.java b/core/src/main/java/ai/timefold/solver/core/impl/solver/scope/SolverScope.java index 096557360de..39b3dd0b7aa 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/solver/scope/SolverScope.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/solver/scope/SolverScope.java @@ -50,7 +50,8 @@ public class SolverScope { private Set solverMetricSet = Collections.emptySet(); private Tags monitoringTags; private int startingSolverCount; - private RandomGenerator workingRandom; + private RandomGenerator moveGeneratorWorkingRandom; + private RandomGenerator solverWorkingRandom; private InnerScoreDirector scoreDirector; private AbstractSolver solver; private DefaultProblemChangeDirector problemChangeDirector; @@ -143,12 +144,25 @@ public void setStartingSolverCount(int startingSolverCount) { this.startingSolverCount = startingSolverCount; } - public RandomGenerator getWorkingRandom() { - return workingRandom; + public RandomGenerator getMoveGeneratorWorkingRandom() { + return moveGeneratorWorkingRandom; } - public void setWorkingRandom(RandomGenerator workingRandom) { - this.workingRandom = workingRandom; + public RandomGenerator getSolverWorkingRandom() { + return solverWorkingRandom; + } + + /** + * Only used for tests + */ + public void setTestWorkingRandom(RandomGenerator workingRandom) { + this.moveGeneratorWorkingRandom = workingRandom; + this.solverWorkingRandom = workingRandom; + } + + public void setWorkingRandom(DelegatingSplittableRandomGenerator workingRandom) { + this.moveGeneratorWorkingRandom = workingRandom; + this.solverWorkingRandom = workingRandom.split(); } @SuppressWarnings("unchecked") @@ -354,9 +368,10 @@ public SolverScope createChildThreadSolverScope(ChildThreadType child childThreadSolverScope.solverMetricSet = solverMetricSet; childThreadSolverScope.startingSolverCount = startingSolverCount; // Experiments show that this trick to attain reproducibility doesn't break uniform distribution - var delegatingRandom = (DelegatingSplittableRandomGenerator) workingRandom; - childThreadSolverScope.workingRandom = + var delegatingRandom = (DelegatingSplittableRandomGenerator) moveGeneratorWorkingRandom; + childThreadSolverScope.moveGeneratorWorkingRandom = new DelegatingSplittableRandomGenerator(delegatingRandom.getSeed(), delegatingRandom.split()); + childThreadSolverScope.solverWorkingRandom = delegatingRandom.split(); childThreadSolverScope.scoreDirector = scoreDirector.createChildThreadScoreDirector(childThreadType); childThreadSolverScope.startingSystemTimeMillis.set(startingSystemTimeMillis.get()); resetAtomicLongTimeMillis(childThreadSolverScope.endingSystemTimeMillis); diff --git a/core/src/test/java/ai/timefold/solver/core/impl/heuristic/selector/SelectorTestUtils.java b/core/src/test/java/ai/timefold/solver/core/impl/heuristic/selector/SelectorTestUtils.java index 1ea63a6df73..b0accb0d975 100644 --- a/core/src/test/java/ai/timefold/solver/core/impl/heuristic/selector/SelectorTestUtils.java +++ b/core/src/test/java/ai/timefold/solver/core/impl/heuristic/selector/SelectorTestUtils.java @@ -219,7 +219,7 @@ public static > SolverScope s Selector... selectors) { SolverScope solverScope = new SolverScope<>(); solverScope.setScoreDirector(scoreDirector); - solverScope.setWorkingRandom(random); + solverScope.setTestWorkingRandom(random); listener.solvingStarted(solverScope); if (selectors != null) { for (var selector : selectors) { diff --git a/core/src/test/java/ai/timefold/solver/core/impl/heuristic/selector/entity/FromSolutionEntitySelectorTest.java b/core/src/test/java/ai/timefold/solver/core/impl/heuristic/selector/entity/FromSolutionEntitySelectorTest.java index 3b6898891c8..646517a0bab 100644 --- a/core/src/test/java/ai/timefold/solver/core/impl/heuristic/selector/entity/FromSolutionEntitySelectorTest.java +++ b/core/src/test/java/ai/timefold/solver/core/impl/heuristic/selector/entity/FromSolutionEntitySelectorTest.java @@ -223,7 +223,7 @@ public void runRandomWithoutEntityListDirtyAndMinimumCacheType(SelectionCacheTyp Random workingRandom = new TestRandom(1, 0, 0, 2, 1, 2, 2, 1, 0); SolverScope solverScope = mock(SolverScope.class); - when(solverScope.getWorkingRandom()).thenReturn(workingRandom); + when(solverScope.getMoveGeneratorWorkingRandom()).thenReturn(workingRandom); when(solverScope.getScoreDirector()).thenReturn(scoreDirector); entitySelector.solvingStarted(solverScope); @@ -283,7 +283,7 @@ void randomWithEntityListDirty() { Random workingRandom = new TestRandom(1, 0, 0, 2, 1, 2, 2, 1, 0); SolverScope solverScope = mock(SolverScope.class); - when(solverScope.getWorkingRandom()).thenReturn(workingRandom); + when(solverScope.getMoveGeneratorWorkingRandom()).thenReturn(workingRandom); when(solverScope.getScoreDirector()).thenReturn(scoreDirector); entitySelector.solvingStarted(solverScope); diff --git a/core/src/test/java/ai/timefold/solver/core/impl/heuristic/selector/entity/decorator/ProbabilityEntitySelectorTest.java b/core/src/test/java/ai/timefold/solver/core/impl/heuristic/selector/entity/decorator/ProbabilityEntitySelectorTest.java index e05723a7747..2ccadcc4ff0 100644 --- a/core/src/test/java/ai/timefold/solver/core/impl/heuristic/selector/entity/decorator/ProbabilityEntitySelectorTest.java +++ b/core/src/test/java/ai/timefold/solver/core/impl/heuristic/selector/entity/decorator/ProbabilityEntitySelectorTest.java @@ -60,7 +60,7 @@ void randomSelection() { 1199.0 / 1234.0); SolverScope solverScope = mock(SolverScope.class); - when(solverScope.getWorkingRandom()).thenReturn(workingRandom); + when(solverScope.getMoveGeneratorWorkingRandom()).thenReturn(workingRandom); entitySelector.solvingStarted(solverScope); AbstractPhaseScope phaseScopeA = PlannerTestUtils.delegatingPhaseScope(solverScope); entitySelector.phaseStarted(phaseScopeA); diff --git a/core/src/test/java/ai/timefold/solver/core/impl/heuristic/selector/entity/pillar/DefaultPillarSelectorTest.java b/core/src/test/java/ai/timefold/solver/core/impl/heuristic/selector/entity/pillar/DefaultPillarSelectorTest.java index a443bb65d80..9f5c1c72ff3 100644 --- a/core/src/test/java/ai/timefold/solver/core/impl/heuristic/selector/entity/pillar/DefaultPillarSelectorTest.java +++ b/core/src/test/java/ai/timefold/solver/core/impl/heuristic/selector/entity/pillar/DefaultPillarSelectorTest.java @@ -181,7 +181,7 @@ void randomWithSubs() { TestRandom workingRandom = new TestRandom(0); SolverScope solverScope = mockSolverScope(); - when(solverScope.getWorkingRandom()).thenReturn(workingRandom); + when(solverScope.getMoveGeneratorWorkingRandom()).thenReturn(workingRandom); pillarSelector.solvingStarted(solverScope); AbstractPhaseScope phaseScopeA = PlannerTestUtils.delegatingPhaseScope(solverScope); @@ -265,7 +265,7 @@ void randomWithSubs_Size2() { 0, 0, 0, 0); // [b, d] SolverScope solverScope = mockSolverScope(); - when(solverScope.getWorkingRandom()).thenReturn(workingRandom); + when(solverScope.getMoveGeneratorWorkingRandom()).thenReturn(workingRandom); pillarSelector.solvingStarted(solverScope); AbstractPhaseScope phaseScopeA = PlannerTestUtils.delegatingPhaseScope(solverScope); @@ -315,7 +315,7 @@ void sequentialUnlimited() { ); SolverScope solverScope = mockSolverScope(); - when(solverScope.getWorkingRandom()).thenReturn(workingRandom); + when(solverScope.getMoveGeneratorWorkingRandom()).thenReturn(workingRandom); pillarSelector.solvingStarted(solverScope); AbstractPhaseScope phaseScopeA = PlannerTestUtils.delegatingPhaseScope(solverScope); diff --git a/core/src/test/java/ai/timefold/solver/core/impl/heuristic/selector/list/ElementDestinationSelectorTest.java b/core/src/test/java/ai/timefold/solver/core/impl/heuristic/selector/list/ElementDestinationSelectorTest.java index 4f6311a356f..9129cfa54a6 100644 --- a/core/src/test/java/ai/timefold/solver/core/impl/heuristic/selector/list/ElementDestinationSelectorTest.java +++ b/core/src/test/java/ai/timefold/solver/core/impl/heuristic/selector/list/ElementDestinationSelectorTest.java @@ -371,7 +371,7 @@ void randomPartiallyPinnedAndUnassigned() { var solverScope = new SolverScope(); solverScope.setScoreDirector(scoreDirector); - solverScope.setWorkingRandom(random); + solverScope.setTestWorkingRandom(random); var entitySelector = new FromSolutionEntitySelector<>( solutionDescriptor.findEntityDescriptorOrFail(TestdataPinnedUnassignedValuesListEntity.class), SelectionCacheType.PHASE, true); @@ -415,7 +415,7 @@ void randomUnassignedSingleEntity() { solverScope.setScoreDirector(scoreDirector); // This needs to use a real Random instance, otherwise the test never covers the situation where // the random value of 1 needs to be produced to get to the entity. - solverScope.setWorkingRandom(new Random(0)); + solverScope.setTestWorkingRandom(new Random(0)); var entitySelector = new FromSolutionEntitySelector<>( solutionDescriptor.findEntityDescriptorOrFail(TestdataAllowsUnassignedValuesListEntity.class), SelectionCacheType.PHASE, true); @@ -467,7 +467,7 @@ void randomFullyPinned() { var solverScope = new SolverScope(); solverScope.setScoreDirector(scoreDirector); - solverScope.setWorkingRandom(random); + solverScope.setTestWorkingRandom(random); selector.solvingStarted(solverScope); selector.phaseStarted(new LocalSearchPhaseScope<>(solverScope, 0)); diff --git a/core/src/test/java/ai/timefold/solver/core/impl/heuristic/selector/move/composite/UnionMoveSelectorTest.java b/core/src/test/java/ai/timefold/solver/core/impl/heuristic/selector/move/composite/UnionMoveSelectorTest.java index d1a301e26fc..ff4ab3b5f04 100644 --- a/core/src/test/java/ai/timefold/solver/core/impl/heuristic/selector/move/composite/UnionMoveSelectorTest.java +++ b/core/src/test/java/ai/timefold/solver/core/impl/heuristic/selector/move/composite/UnionMoveSelectorTest.java @@ -104,7 +104,7 @@ void biasedRandomSelection() { 0.0, 999.0 / 1020.0); SolverScope solverScope = mock(SolverScope.class); - when(solverScope.getWorkingRandom()).thenReturn(workingRandom); + when(solverScope.getMoveGeneratorWorkingRandom()).thenReturn(workingRandom); moveSelector.solvingStarted(solverScope); AbstractPhaseScope phaseScopeA = PlannerTestUtils.delegatingPhaseScope(solverScope); moveSelector.phaseStarted(phaseScopeA); @@ -133,7 +133,7 @@ void uniformRandomSelection() { Random workingRandom = new TestRandom(0, 1, 1, 0, 0); SolverScope solverScope = mock(SolverScope.class); - when(solverScope.getWorkingRandom()).thenReturn(workingRandom); + when(solverScope.getMoveGeneratorWorkingRandom()).thenReturn(workingRandom); moveSelector.solvingStarted(solverScope); AbstractPhaseScope phaseScopeA = PlannerTestUtils.delegatingPhaseScope(solverScope); moveSelector.phaseStarted(phaseScopeA); @@ -162,7 +162,7 @@ void emptyUniformRandomSelection() { Random workingRandom = new TestRandom(1); SolverScope solverScope = mock(SolverScope.class); - when(solverScope.getWorkingRandom()).thenReturn(workingRandom); + when(solverScope.getMoveGeneratorWorkingRandom()).thenReturn(workingRandom); moveSelector.solvingStarted(solverScope); AbstractPhaseScope phaseScopeA = PlannerTestUtils.delegatingPhaseScope(solverScope); moveSelector.phaseStarted(phaseScopeA); @@ -195,7 +195,7 @@ void emptyBiasedRandomSelection() { Random workingRandom = new TestRandom(1); SolverScope solverScope = mock(SolverScope.class); - when(solverScope.getWorkingRandom()).thenReturn(workingRandom); + when(solverScope.getMoveGeneratorWorkingRandom()).thenReturn(workingRandom); moveSelector.solvingStarted(solverScope); AbstractPhaseScope phaseScopeA = PlannerTestUtils.delegatingPhaseScope(solverScope); moveSelector.phaseStarted(phaseScopeA); diff --git a/core/src/test/java/ai/timefold/solver/core/impl/heuristic/selector/move/decorator/CachingMoveSelectorTest.java b/core/src/test/java/ai/timefold/solver/core/impl/heuristic/selector/move/decorator/CachingMoveSelectorTest.java index c70776256d2..437da7bb2f4 100644 --- a/core/src/test/java/ai/timefold/solver/core/impl/heuristic/selector/move/decorator/CachingMoveSelectorTest.java +++ b/core/src/test/java/ai/timefold/solver/core/impl/heuristic/selector/move/decorator/CachingMoveSelectorTest.java @@ -121,7 +121,7 @@ public void runRandomSelection(SelectionCacheType cacheType, int timesCalled) { TestRandom workingRandom = new TestRandom(1, 0, 2); SolverScope solverScope = mock(SolverScope.class); - when(solverScope.getWorkingRandom()).thenReturn(workingRandom); + when(solverScope.getMoveGeneratorWorkingRandom()).thenReturn(workingRandom); moveSelector.solvingStarted(solverScope); AbstractPhaseScope phaseScopeA = PlannerTestUtils.delegatingPhaseScope(solverScope); diff --git a/core/src/test/java/ai/timefold/solver/core/impl/heuristic/selector/move/decorator/ProbabilityMoveSelectorTest.java b/core/src/test/java/ai/timefold/solver/core/impl/heuristic/selector/move/decorator/ProbabilityMoveSelectorTest.java index 8d16512fe22..386954e3772 100644 --- a/core/src/test/java/ai/timefold/solver/core/impl/heuristic/selector/move/decorator/ProbabilityMoveSelectorTest.java +++ b/core/src/test/java/ai/timefold/solver/core/impl/heuristic/selector/move/decorator/ProbabilityMoveSelectorTest.java @@ -57,7 +57,7 @@ void randomSelection() { 1199.0 / 1234.0); SolverScope solverScope = mock(SolverScope.class); - when(solverScope.getWorkingRandom()).thenReturn(workingRandom); + when(solverScope.getMoveGeneratorWorkingRandom()).thenReturn(workingRandom); moveSelector.solvingStarted(solverScope); AbstractPhaseScope phaseScopeA = PlannerTestUtils.delegatingPhaseScope(solverScope); moveSelector.phaseStarted(phaseScopeA); diff --git a/core/src/test/java/ai/timefold/solver/core/impl/heuristic/selector/move/decorator/ShufflingMoveSelectorTest.java b/core/src/test/java/ai/timefold/solver/core/impl/heuristic/selector/move/decorator/ShufflingMoveSelectorTest.java index 7505daab8d5..698ec6af01c 100644 --- a/core/src/test/java/ai/timefold/solver/core/impl/heuristic/selector/move/decorator/ShufflingMoveSelectorTest.java +++ b/core/src/test/java/ai/timefold/solver/core/impl/heuristic/selector/move/decorator/ShufflingMoveSelectorTest.java @@ -45,7 +45,7 @@ public void run(SelectionCacheType cacheType, int timesCalled) { TestRandom workingRandom = new TestRandom(2, 0); SolverScope solverScope = mock(SolverScope.class); - when(solverScope.getWorkingRandom()).thenReturn(workingRandom); + when(solverScope.getMoveGeneratorWorkingRandom()).thenReturn(workingRandom); moveSelector.solvingStarted(solverScope); AbstractPhaseScope phaseScopeA = PlannerTestUtils.delegatingPhaseScope(solverScope); diff --git a/core/src/test/java/ai/timefold/solver/core/impl/localsearch/decider/acceptor/simulatedannealing/SimulatedAnnealingAcceptorTest.java b/core/src/test/java/ai/timefold/solver/core/impl/localsearch/decider/acceptor/simulatedannealing/SimulatedAnnealingAcceptorTest.java index f0dd4e7875e..b1cb1f03a04 100644 --- a/core/src/test/java/ai/timefold/solver/core/impl/localsearch/decider/acceptor/simulatedannealing/SimulatedAnnealingAcceptorTest.java +++ b/core/src/test/java/ai/timefold/solver/core/impl/localsearch/decider/acceptor/simulatedannealing/SimulatedAnnealingAcceptorTest.java @@ -33,11 +33,11 @@ void lateAcceptanceSize() { stepScope0.setTimeGradient(0.0); acceptor.stepStarted(stepScope0); var moveScope0 = buildMoveScope(stepScope0, -500); - solverScope.setWorkingRandom(new TestRandom(0.3)); + solverScope.setTestWorkingRandom(new TestRandom(0.3)); assertThat(acceptor.isAccepted(buildMoveScope(stepScope0, -1300))).isFalse(); - solverScope.setWorkingRandom(new TestRandom(0.3)); + solverScope.setTestWorkingRandom(new TestRandom(0.3)); assertThat(acceptor.isAccepted(buildMoveScope(stepScope0, -1200))).isTrue(); - solverScope.setWorkingRandom(new TestRandom(0.4)); + solverScope.setTestWorkingRandom(new TestRandom(0.4)); assertThat(acceptor.isAccepted(buildMoveScope(stepScope0, -1200))).isFalse(); assertThat(acceptor.isAccepted(moveScope0)).isTrue(); stepScope0.setStep(moveScope0.getMove()); @@ -50,11 +50,11 @@ void lateAcceptanceSize() { stepScope1.setTimeGradient(0.5); acceptor.stepStarted(stepScope1); var moveScope1 = buildMoveScope(stepScope1, -800); - solverScope.setWorkingRandom(new TestRandom(0.13)); + solverScope.setTestWorkingRandom(new TestRandom(0.13)); assertThat(acceptor.isAccepted(buildMoveScope(stepScope1, -700))).isTrue(); - solverScope.setWorkingRandom(new TestRandom(0.14)); + solverScope.setTestWorkingRandom(new TestRandom(0.14)); assertThat(acceptor.isAccepted(buildMoveScope(stepScope1, -700))).isFalse(); - solverScope.setWorkingRandom(new TestRandom(0.04)); + solverScope.setTestWorkingRandom(new TestRandom(0.04)); assertThat(acceptor.isAccepted(moveScope1)).isTrue(); stepScope1.setStep(moveScope1.getMove()); stepScope1.setScore(moveScope1.getScore()); @@ -62,7 +62,7 @@ void lateAcceptanceSize() { acceptor.stepEnded(stepScope1); phaseScope.setLastCompletedStepScope(stepScope1); - solverScope.setWorkingRandom(new TestRandom(0.01, 0.01)); + solverScope.setTestWorkingRandom(new TestRandom(0.01, 0.01)); var stepScope2 = new LocalSearchStepScope<>(phaseScope); stepScope2.setTimeGradient(1.0); acceptor.stepStarted(stepScope2); diff --git a/core/src/test/java/ai/timefold/solver/core/impl/localsearch/decider/forager/AcceptedLocalSearchForagerTest.java b/core/src/test/java/ai/timefold/solver/core/impl/localsearch/decider/forager/AcceptedLocalSearchForagerTest.java index 7805e9c68bc..cc167efb8bd 100644 --- a/core/src/test/java/ai/timefold/solver/core/impl/localsearch/decider/forager/AcceptedLocalSearchForagerTest.java +++ b/core/src/test/java/ai/timefold/solver/core/impl/localsearch/decider/forager/AcceptedLocalSearchForagerTest.java @@ -219,7 +219,7 @@ private static LocalSearchPhaseScope createPhaseScope() { when(scoreDirector.getScoreDefinition()).thenReturn(new SimpleScoreDefinition()); solverScope.setScoreDirector(scoreDirector); Random workingRandom = new TestRandom(1, 1); - solverScope.setWorkingRandom(workingRandom); + solverScope.setTestWorkingRandom(workingRandom); solverScope.setInitializedBestScore(SimpleScore.of(-10)); solverScope.setSolverMetricSet(EnumSet.of(SolverMetric.MOVE_EVALUATION_COUNT)); LocalSearchStepScope lastLocalSearchStepScope = new LocalSearchStepScope<>(phaseScope); diff --git a/core/src/test/java/ai/timefold/solver/core/impl/neighborhood/NeighborhoodsTest.java b/core/src/test/java/ai/timefold/solver/core/impl/neighborhood/NeighborhoodsTest.java index a94afe7c3b1..f8f78596209 100644 --- a/core/src/test/java/ai/timefold/solver/core/impl/neighborhood/NeighborhoodsTest.java +++ b/core/src/test/java/ai/timefold/solver/core/impl/neighborhood/NeighborhoodsTest.java @@ -99,7 +99,7 @@ void changeMoveBasedLocalSearch() { bestSolutionRecaller.setSolverEventSupport(solverEventSupport); var solverScope = new SolverScope(); solverScope.setSolver(solver); - solverScope.setWorkingRandom(new Random()); + solverScope.setTestWorkingRandom(new Random()); solverScope.setScoreDirector(scoreDirector); solverScope.setBestScore(score); solverScope.setBestSolution(scoreDirector.cloneSolution(solution)); diff --git a/core/src/test/java/ai/timefold/solver/core/testutil/PlannerTestUtils.java b/core/src/test/java/ai/timefold/solver/core/testutil/PlannerTestUtils.java index 4f8c99eb3c5..c41b6e9c5e7 100644 --- a/core/src/test/java/ai/timefold/solver/core/testutil/PlannerTestUtils.java +++ b/core/src/test/java/ai/timefold/solver/core/testutil/PlannerTestUtils.java @@ -217,7 +217,7 @@ public static , Y> SortedMap asSortedMap(X x1, Y y // ************************************************************************ /** - * Returns {@link AbstractPhaseScope} instance that will delegate to {@link SolverScope#getWorkingRandom()}. + * Returns {@link AbstractPhaseScope} instance that will delegate to {@link SolverScope#getMoveGeneratorWorkingRandom()}. * * @param solverScope never null * @param generic type of the solution @@ -233,7 +233,8 @@ public AbstractStepScope getLastCompletedStepScope() { } /** - * Returns {@link AbstractPhaseScope} instance that will delegate to {@link AbstractPhaseScope#getWorkingRandom()}. + * Returns {@link AbstractPhaseScope} instance that will delegate to + * {@link AbstractPhaseScope#getMoveGeneratorWorkingRandom()}. * * @param phaseScope never null * @param generic type of the solution