Skip to content

Commit b43b678

Browse files
committed
Value ranges are immutable
1 parent 7d614cf commit b43b678

3 files changed

Lines changed: 3 additions & 60 deletions

File tree

core/src/main/java/ai/timefold/solver/core/impl/score/director/AbstractScoreDirector.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -486,7 +486,6 @@ public void afterVariableChanged(VariableDescriptor<Solution_> variableDescripto
486486
if (moveRepository instanceof MoveStreamsBasedMoveRepository<Solution_> moveStreamsBasedMoveRepository) {
487487
moveStreamsBasedMoveRepository.update(entity);
488488
}
489-
valueRangeState.markEntityDependentValueRangesAsInvalid(entity);
490489
variableListenerSupport.afterVariableChanged(variableDescriptor, entity);
491490
}
492491

@@ -540,7 +539,6 @@ Attempting to change list variable (%s) on an entity (%s) in range [%d, %d), whi
540539
@Override
541540
public void afterListVariableChanged(ListVariableDescriptor<Solution_> variableDescriptor,
542541
Object entity, int fromIndex, int toIndex) {
543-
valueRangeState.markEntityDependentValueRangesAsInvalid(entity);
544542
variableListenerSupport.afterListVariableChanged(variableDescriptor, entity, fromIndex, toIndex);
545543
if (moveRepository instanceof MoveStreamsBasedMoveRepository<Solution_> moveStreamsBasedMoveRepository) {
546544
moveStreamsBasedMoveRepository.update(entity);
@@ -560,7 +558,6 @@ public void afterEntityRemoved(EntityDescriptor<Solution_> entityDescriptor, Obj
560558
if (lookUpEnabled) {
561559
lookUpManager.removeWorkingObject(entity);
562560
}
563-
valueRangeState.markEntityDependentValueRangesAsInvalid(entity);
564561
if (!allChangesWillBeUndoneBeforeStepEnds) {
565562
if (moveRepository instanceof MoveStreamsBasedMoveRepository<Solution_> moveStreamsBasedMoveRepository) {
566563
moveStreamsBasedMoveRepository.retract(entity);
Lines changed: 3 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,14 @@
11
package ai.timefold.solver.core.impl.score.director;
22

3-
import java.util.HashSet;
43
import java.util.IdentityHashMap;
54
import java.util.Map;
65
import java.util.Objects;
76

87
import ai.timefold.solver.core.api.domain.valuerange.ValueRange;
8+
import ai.timefold.solver.core.api.solver.change.ProblemChange;
99
import ai.timefold.solver.core.impl.domain.valuerange.descriptor.ValueRangeDescriptor;
1010
import ai.timefold.solver.core.impl.domain.variable.descriptor.BasicVariableDescriptor;
1111
import ai.timefold.solver.core.impl.domain.variable.descriptor.ListVariableDescriptor;
12-
import ai.timefold.solver.core.impl.phase.event.PhaseLifecycleListener;
13-
import ai.timefold.solver.core.impl.phase.scope.AbstractPhaseScope;
14-
import ai.timefold.solver.core.impl.phase.scope.AbstractStepScope;
15-
import ai.timefold.solver.core.impl.solver.scope.SolverScope;
1612
import ai.timefold.solver.core.preview.api.domain.metamodel.GenuineVariableMetaModel;
1713

1814
import org.jspecify.annotations.NullMarked;
@@ -28,21 +24,13 @@
2824
* It is not built in advance as it would be too expensive, and some code paths do not use it at all.
2925
*
3026
* <p>
27+
* Outside a {@link ProblemChange}, value ranges are not allowed to change.
3128
* Call {@link #resetWorkingSolution(Object)} every time the working solution changes through a problem fact,
3229
* so that all caches can be invalidated.
33-
*
34-
* <p>
35-
* Call {@link #markEntityDependentValueRangesAsInvalid(Object)} every time an entity is changed,
36-
* so that the cached value range can be invalidated.
37-
* The actual invalidation happens at {@link #stepEnded(AbstractStepScope)}, called by the solver.
38-
* Otherwise every undone move would be invalidating the value ranges.
39-
* Value ranges should only be invalidated when a move has been selected at the end of a step.
4030
*/
4131
@NullMarked
42-
public final class ValueRangeState<Solution_>
43-
implements PhaseLifecycleListener<Solution_> {
32+
public final class ValueRangeState<Solution_> {
4433

45-
private final HashSet<Object> entitiesWithInvalidValueRangesSet = new HashSet<>();
4634
private final Map<ValueRangeDescriptor<Solution_>, ValueRange<Object>> fromSolutionValueRangeMap = new IdentityHashMap<>();
4735
private final Map<Object, Map<ValueRangeDescriptor<Solution_>, ValueRange<Object>>> fromEntityValueRangeMap =
4836
new IdentityHashMap<>();
@@ -93,40 +81,4 @@ public boolean isInRange(ValueRangeDescriptor<Solution_> valueRangeDescriptor, @
9381
}
9482
}
9583

96-
void markEntityDependentValueRangesAsInvalid(Object entity) {
97-
entitiesWithInvalidValueRangesSet.add(entity);
98-
}
99-
100-
@Override
101-
public void solvingStarted(SolverScope<Solution_> solverScope) {
102-
// No need to do anything here.
103-
}
104-
105-
@Override
106-
public void phaseStarted(AbstractPhaseScope<Solution_> phaseScope) {
107-
// No need to do anything here.
108-
}
109-
110-
@Override
111-
public void stepStarted(AbstractStepScope<Solution_> stepScope) {
112-
// No need to do anything here.
113-
}
114-
115-
@Override
116-
public void stepEnded(AbstractStepScope<Solution_> stepScope) {
117-
entitiesWithInvalidValueRangesSet.forEach(fromEntityValueRangeMap::remove);
118-
entitiesWithInvalidValueRangesSet.clear();
119-
}
120-
121-
@Override
122-
public void phaseEnded(AbstractPhaseScope<Solution_> phaseScope) {
123-
// No need to do anything here.
124-
}
125-
126-
@Override
127-
public void solvingEnded(SolverScope<Solution_> solverScope) {
128-
this.workingSolution = null;
129-
fromSolutionValueRangeMap.clear();
130-
fromEntityValueRangeMap.clear();
131-
}
13284
}

core/src/main/java/ai/timefold/solver/core/impl/solver/AbstractSolver.java

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@ protected AbstractSolver(BestSolutionRecaller<Solution_> bestSolutionRecaller,
5757

5858
public void solvingStarted(SolverScope<Solution_> solverScope) {
5959
solverScope.setWorkingSolutionFromBestSolution();
60-
solverScope.getValueRangeState().solvingStarted(solverScope);
6160
bestSolutionRecaller.solvingStarted(solverScope);
6261
globalTermination.solvingStarted(solverScope);
6362
phaseLifecycleSupport.fireSolvingStarted(solverScope);
@@ -93,7 +92,6 @@ public void solvingEnded(SolverScope<Solution_> solverScope) {
9392
bestSolutionRecaller.solvingEnded(solverScope);
9493
globalTermination.solvingEnded(solverScope);
9594
phaseLifecycleSupport.fireSolvingEnded(solverScope);
96-
solverScope.getValueRangeState().solvingEnded(solverScope);
9795
}
9896

9997
public void solvingError(SolverScope<Solution_> solverScope, Exception exception) {
@@ -104,7 +102,6 @@ public void solvingError(SolverScope<Solution_> solverScope, Exception exception
104102
}
105103

106104
public void phaseStarted(AbstractPhaseScope<Solution_> phaseScope) {
107-
phaseScope.getSolverScope().getValueRangeState().phaseStarted(phaseScope);
108105
bestSolutionRecaller.phaseStarted(phaseScope);
109106
phaseLifecycleSupport.firePhaseStarted(phaseScope);
110107
globalTermination.phaseStarted(phaseScope);
@@ -115,12 +112,10 @@ public void phaseEnded(AbstractPhaseScope<Solution_> phaseScope) {
115112
bestSolutionRecaller.phaseEnded(phaseScope);
116113
phaseLifecycleSupport.firePhaseEnded(phaseScope);
117114
globalTermination.phaseEnded(phaseScope);
118-
phaseScope.getSolverScope().getValueRangeState().phaseEnded(phaseScope);
119115
// Do not propagate to phases; the active phase does that for itself and they should not propagate further.
120116
}
121117

122118
public void stepStarted(AbstractStepScope<Solution_> stepScope) {
123-
stepScope.getPhaseScope().getSolverScope().getValueRangeState().stepStarted(stepScope);
124119
bestSolutionRecaller.stepStarted(stepScope);
125120
phaseLifecycleSupport.fireStepStarted(stepScope);
126121
globalTermination.stepStarted(stepScope);
@@ -131,7 +126,6 @@ public void stepEnded(AbstractStepScope<Solution_> stepScope) {
131126
bestSolutionRecaller.stepEnded(stepScope);
132127
phaseLifecycleSupport.fireStepEnded(stepScope);
133128
globalTermination.stepEnded(stepScope);
134-
stepScope.getPhaseScope().getSolverScope().getValueRangeState().stepEnded(stepScope);
135129
// Do not propagate to phases; the active phase does that for itself and they should not propagate further.
136130
}
137131

0 commit comments

Comments
 (0)