Skip to content

Commit

Permalink
push move stream session through the layers
Browse files Browse the repository at this point in the history
  • Loading branch information
triceo committed Feb 6, 2025
1 parent 728c0b9 commit 94cc32f
Show file tree
Hide file tree
Showing 32 changed files with 356 additions and 147 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package ai.timefold.solver.core.impl.move;

import ai.timefold.solver.core.impl.move.director.MoveStreamSession;

public interface MoveStreamSessionFactory<Solution_> {

MoveStreamSession<Solution_> createMoveStreamSession(Solution_ workingSolution);

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,16 @@ public sealed class MoveDirector<Solution_>
permits EphemeralMoveDirector {

protected final VariableDescriptorAwareScoreDirector<Solution_> scoreDirector;
private final MoveStreamSession<Solution_> moveStreamSession;

public MoveDirector(VariableDescriptorAwareScoreDirector<Solution_> scoreDirector) {
this(scoreDirector, null);
}

public MoveDirector(VariableDescriptorAwareScoreDirector<Solution_> scoreDirector,
MoveStreamSession<Solution_> moveStreamSession) {
this.scoreDirector = Objects.requireNonNull(scoreDirector);
this.moveStreamSession = moveStreamSession;
}

@Override
Expand All @@ -47,6 +54,9 @@ public final <Entity_, Value_> void changeVariable(
scoreDirector.beforeVariableChanged(variableDescriptor, entity);
variableDescriptor.setValue(entity, newValue);
scoreDirector.afterVariableChanged(variableDescriptor, entity);
if (moveStreamSession != null) {
moveStreamSession.update(entity);
}
}

@SuppressWarnings("unchecked")
Expand All @@ -64,6 +74,11 @@ public final <Entity_, Value_> Value_ moveValueBetweenLists(
scoreDirector.beforeListVariableChanged(variableDescriptor, destinationEntity, destinationIndex, destinationIndex);
variableDescriptor.addElement(destinationEntity, destinationIndex, element);
scoreDirector.afterListVariableChanged(variableDescriptor, destinationEntity, destinationIndex, destinationIndex + 1);

if (moveStreamSession != null) {
moveStreamSession.update(sourceEntity);
moveStreamSession.update(destinationEntity);
}
return element;
}

Expand All @@ -84,12 +99,24 @@ public final <Entity_, Value_> Value_ moveValueInList(
var value = (Value_) variable.remove(sourceIndex);
variable.add(destinationIndex, value);
scoreDirector.afterListVariableChanged(variableDescriptor, entity, sourceIndex, toIndex);
if (moveStreamSession != null) {
moveStreamSession.update(entity);
}
return value;
}

@Override
public final void updateShadowVariables() {
scoreDirector.triggerVariableListeners();
updateShadowVariables(false); // Called by the move itself.
}

public final void updateShadowVariables(boolean comingFromScoreDirector) {
if (!comingFromScoreDirector) { // Prevent recursion.
scoreDirector.triggerVariableListeners();
}
if (moveStreamSession != null) {
moveStreamSession.settle();
}
}

@SuppressWarnings("unchecked")
Expand Down Expand Up @@ -150,4 +177,12 @@ public VariableDescriptorAwareScoreDirector<Solution_> getScoreDirector() {
return scoreDirector;
}

public void resetWorkingSolution(Solution_ workingSolution) {
if (moveStreamSession == null) {
return;
}
moveStreamSession.resetWorkingSolution(workingSolution);
scoreDirector.getSolutionDescriptor().visitAll(workingSolution, moveStreamSession::insert);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package ai.timefold.solver.core.impl.move.director;

public interface MoveStreamSession<Solution_> {

void resetWorkingSolution(Solution_ workingSolution);

void insert(Object problemFactOrEntity);

void update(Object problemFactOrEntity);

void retract(Object problemFactOrEntity);

void settle();

}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import ai.timefold.solver.core.impl.domain.variable.listener.support.violation.SolutionTracker;
import ai.timefold.solver.core.impl.domain.variable.supply.SupplyManager;
import ai.timefold.solver.core.impl.move.director.MoveDirector;
import ai.timefold.solver.core.impl.move.director.MoveStreamSession;
import ai.timefold.solver.core.impl.phase.scope.SolverLifecyclePoint;
import ai.timefold.solver.core.impl.score.constraint.ConstraintMatchPolicy;
import ai.timefold.solver.core.impl.score.definition.ScoreDefinition;
Expand Down Expand Up @@ -81,13 +82,13 @@ public abstract class AbstractScoreDirector<Solution_, Score_ extends Score<Scor
// Null when tracking disabled
private final boolean trackingWorkingSolution;
private final SolutionTracker<Solution_> solutionTracker;
private final MoveDirector<Solution_> moveDirector = new MoveDirector<>(this);
private final MoveDirector<Solution_> moveDirector;

// Null when no list variable
private final ListVariableStateSupply<Solution_> listVariableStateSupply;

protected AbstractScoreDirector(Factory_ scoreDirectorFactory, boolean lookUpEnabled,
ConstraintMatchPolicy constraintMatchPolicy, boolean expectShadowVariablesInCorrectState) {
protected AbstractScoreDirector(Factory_ scoreDirectorFactory, MoveStreamSession<Solution_> moveStreamSession,
boolean lookUpEnabled, ConstraintMatchPolicy constraintMatchPolicy, boolean expectShadowVariablesInCorrectState) {
var solutionDescriptor = scoreDirectorFactory.getSolutionDescriptor();
this.lookUpEnabled = lookUpEnabled;
this.lookUpManager = lookUpEnabled
Expand All @@ -107,6 +108,7 @@ protected AbstractScoreDirector(Factory_ scoreDirectorFactory, boolean lookUpEna
this.solutionTracker = null;
this.trackingWorkingSolution = false;
}
this.moveDirector = new MoveDirector<>(this, moveStreamSession);
var listVariableDescriptor = solutionDescriptor.getListVariableDescriptor();
if (listVariableDescriptor == null) {
this.listVariableStateSupply = null;
Expand Down Expand Up @@ -245,6 +247,7 @@ public void setWorkingSolution(Solution_ workingSolution) {
assertInitScoreZeroOrLess();
workingGenuineEntityCount = initializationStatistics.genuineEntityCount();
variableListenerSupport.resetWorkingSolution();
moveDirector.resetWorkingSolution(workingSolution);
}

private void assertInitScoreZeroOrLess() {
Expand Down Expand Up @@ -320,11 +323,13 @@ public Solution_ cloneSolution(Solution_ originalSolution) {
@Override
public void triggerVariableListeners() {
variableListenerSupport.triggerVariableListenersInNotificationQueues();
moveDirector.updateShadowVariables(true);
}

@Override
public void forceTriggerVariableListeners() {
variableListenerSupport.forceTriggerAllVariableListeners(getWorkingSolution());
moveDirector.updateShadowVariables(true);
}

protected void setCalculatedScore(Score_ score) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import ai.timefold.solver.core.api.score.constraint.ConstraintMatch;
import ai.timefold.solver.core.api.score.director.ScoreDirector;
import ai.timefold.solver.core.impl.domain.solution.descriptor.SolutionDescriptor;
import ai.timefold.solver.core.impl.move.director.MoveStreamSession;
import ai.timefold.solver.core.impl.score.constraint.ConstraintMatchPolicy;
import ai.timefold.solver.core.impl.score.definition.ScoreDefinition;
import ai.timefold.solver.core.impl.score.trend.InitializingScoreTrend;
Expand All @@ -27,19 +28,22 @@ public interface InnerScoreDirectorFactory<Solution_, Score_ extends Score<Score
ScoreDefinition<Score_> getScoreDefinition();

@Override
default InnerScoreDirector<Solution_, Score_> buildScoreDirector(boolean lookUpEnabled,
default InnerScoreDirector<Solution_, Score_> buildScoreDirector(MoveStreamSession<Solution_> moveStreamSession,
boolean lookUpEnabled,
ConstraintMatchPolicy constraintMatchPolicy) {
return buildScoreDirector(lookUpEnabled, constraintMatchPolicy, true);
return buildScoreDirector(moveStreamSession, lookUpEnabled, constraintMatchPolicy, true);
}

@Override
InnerScoreDirector<Solution_, Score_> buildScoreDirector(boolean lookUpEnabled, ConstraintMatchPolicy constraintMatchPolicy,
boolean expectShadowVariablesInCorrectState);
InnerScoreDirector<Solution_, Score_> buildScoreDirector(MoveStreamSession<Solution_> moveStreamSession,
boolean lookUpEnabled, ConstraintMatchPolicy constraintMatchPolicy, boolean expectShadowVariablesInCorrectState);

/**
* Like {@link #buildScoreDirector(boolean, ConstraintMatchPolicy)}, but makes the score director a derived one.
* Like {@link #buildScoreDirector(MoveStreamSession, boolean, ConstraintMatchPolicy)},
* but makes the score director a derived one.
* Derived score directors may make choices which the main score director can not make, such as reducing logging.
* Derived score directors are typically used for multithreaded solving, testing and assert modes.
* Derived score directors do not support move streams, as they are only used to calculate the score.
*
* @param lookUpEnabled true if a {@link ScoreDirector} implementation should track all working objects
* for {@link ScoreDirector#lookUpWorkingObject(Object)}
Expand All @@ -51,7 +55,7 @@ InnerScoreDirector<Solution_, Score_> buildScoreDirector(boolean lookUpEnabled,
default InnerScoreDirector<Solution_, Score_> buildDerivedScoreDirector(boolean lookUpEnabled,
ConstraintMatchPolicy constraintMatchPolicy) {
// Most score directors don't need derived status; CS will override this.
return buildScoreDirector(lookUpEnabled, constraintMatchPolicy);
return buildScoreDirector(null, lookUpEnabled, constraintMatchPolicy);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import ai.timefold.solver.core.api.domain.solution.PlanningSolution;
import ai.timefold.solver.core.api.score.constraint.ConstraintMatch;
import ai.timefold.solver.core.api.score.director.ScoreDirector;
import ai.timefold.solver.core.impl.move.director.MoveStreamSession;
import ai.timefold.solver.core.impl.score.constraint.ConstraintMatchPolicy;

/**
Expand All @@ -13,19 +14,21 @@
public interface ScoreDirectorFactory<Solution_> {

/**
* Like {@link #buildScoreDirector(boolean, ConstraintMatchPolicy, boolean)},
* Like {@link #buildScoreDirector(MoveStreamSession, boolean, ConstraintMatchPolicy, boolean)},
* with the final parameter set to true.
*
* @param moveStreamSession null if move streams not supported
* @param lookUpEnabled true if a {@link ScoreDirector} implementation should track all working objects
* for {@link ScoreDirector#lookUpWorkingObject(Object)}
* @param constraintMatchPolicy how should the {@link ScoreDirector} track {@link ConstraintMatch constraint matches}.
* @return never null
*/
default ScoreDirector<Solution_> buildScoreDirector(boolean lookUpEnabled, ConstraintMatchPolicy constraintMatchPolicy) {
return buildScoreDirector(lookUpEnabled, constraintMatchPolicy, true);
default ScoreDirector<Solution_> buildScoreDirector(MoveStreamSession<Solution_> moveStreamSession, boolean lookUpEnabled,
ConstraintMatchPolicy constraintMatchPolicy) {
return buildScoreDirector(moveStreamSession, lookUpEnabled, constraintMatchPolicy, true);
}

ScoreDirector<Solution_> buildScoreDirector(boolean lookUpEnabled, ConstraintMatchPolicy constraintMatchPolicy,
boolean expectShadowVariablesInCorrectState);
ScoreDirector<Solution_> buildScoreDirector(MoveStreamSession<Solution_> moveStreamSession, boolean lookUpEnabled,
ConstraintMatchPolicy constraintMatchPolicy, boolean expectShadowVariablesInCorrectState);

}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import ai.timefold.solver.core.api.score.constraint.ConstraintMatchTotal;
import ai.timefold.solver.core.api.score.constraint.Indictment;
import ai.timefold.solver.core.api.score.director.ScoreDirector;
import ai.timefold.solver.core.impl.move.director.MoveStreamSession;
import ai.timefold.solver.core.impl.score.constraint.ConstraintMatchPolicy;
import ai.timefold.solver.core.impl.score.director.AbstractScoreDirector;

Expand All @@ -29,9 +30,10 @@ public final class EasyScoreDirector<Solution_, Score_ extends Score<Score_>>
private final EasyScoreCalculator<Solution_, Score_> easyScoreCalculator;

public EasyScoreDirector(EasyScoreDirectorFactory<Solution_, Score_> scoreDirectorFactory,
boolean lookUpEnabled, boolean expectShadowVariablesInCorrectState,
MoveStreamSession<Solution_> moveStreamSession, boolean lookUpEnabled, boolean expectShadowVariablesInCorrectState,
EasyScoreCalculator<Solution_, Score_> easyScoreCalculator) {
super(scoreDirectorFactory, lookUpEnabled, ConstraintMatchPolicy.DISABLED, expectShadowVariablesInCorrectState);
super(scoreDirectorFactory, moveStreamSession, lookUpEnabled, ConstraintMatchPolicy.DISABLED,
expectShadowVariablesInCorrectState);
this.easyScoreCalculator = easyScoreCalculator;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import ai.timefold.solver.core.config.score.director.ScoreDirectorFactoryConfig;
import ai.timefold.solver.core.config.util.ConfigUtils;
import ai.timefold.solver.core.impl.domain.solution.descriptor.SolutionDescriptor;
import ai.timefold.solver.core.impl.move.director.MoveStreamSession;
import ai.timefold.solver.core.impl.score.constraint.ConstraintMatchPolicy;
import ai.timefold.solver.core.impl.score.director.AbstractScoreDirectorFactory;
import ai.timefold.solver.core.impl.score.director.ScoreDirectorFactory;
Expand Down Expand Up @@ -49,9 +50,10 @@ public EasyScoreDirectorFactory(SolutionDescriptor<Solution_> solutionDescriptor
// ************************************************************************

@Override
public EasyScoreDirector<Solution_, Score_> buildScoreDirector(boolean lookUpEnabled,
ConstraintMatchPolicy constraintMatchPolicy, boolean expectShadowVariablesInCorrectState) {
return new EasyScoreDirector<>(this, lookUpEnabled, expectShadowVariablesInCorrectState, easyScoreCalculator);
public EasyScoreDirector<Solution_, Score_> buildScoreDirector(MoveStreamSession<Solution_> moveStreamSession,
boolean lookUpEnabled, ConstraintMatchPolicy constraintMatchPolicy, boolean expectShadowVariablesInCorrectState) {
return new EasyScoreDirector<>(this, moveStreamSession, lookUpEnabled, expectShadowVariablesInCorrectState,
easyScoreCalculator);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import ai.timefold.solver.core.impl.domain.entity.descriptor.EntityDescriptor;
import ai.timefold.solver.core.impl.domain.variable.descriptor.ListVariableDescriptor;
import ai.timefold.solver.core.impl.domain.variable.descriptor.VariableDescriptor;
import ai.timefold.solver.core.impl.move.director.MoveStreamSession;
import ai.timefold.solver.core.impl.score.constraint.ConstraintMatchPolicy;
import ai.timefold.solver.core.impl.score.constraint.DefaultIndictment;
import ai.timefold.solver.core.impl.score.director.AbstractScoreDirector;
Expand All @@ -37,9 +38,10 @@ public final class IncrementalScoreDirector<Solution_, Score_ extends Score<Scor
private final IncrementalScoreCalculator<Solution_, Score_> incrementalScoreCalculator;

public IncrementalScoreDirector(IncrementalScoreDirectorFactory<Solution_, Score_> scoreDirectorFactory,
boolean lookUpEnabled, ConstraintMatchPolicy constraintMatchPolicy, boolean expectShadowVariablesInCorrectState,
MoveStreamSession<Solution_> moveStreamSession, boolean lookUpEnabled, ConstraintMatchPolicy constraintMatchPolicy,
boolean expectShadowVariablesInCorrectState,
IncrementalScoreCalculator<Solution_, Score_> incrementalScoreCalculator) {
super(scoreDirectorFactory, lookUpEnabled,
super(scoreDirectorFactory, moveStreamSession, lookUpEnabled,
determineCorrectPolicy(constraintMatchPolicy, incrementalScoreCalculator),
expectShadowVariablesInCorrectState);
this.incrementalScoreCalculator = incrementalScoreCalculator;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import ai.timefold.solver.core.config.score.director.ScoreDirectorFactoryConfig;
import ai.timefold.solver.core.config.util.ConfigUtils;
import ai.timefold.solver.core.impl.domain.solution.descriptor.SolutionDescriptor;
import ai.timefold.solver.core.impl.move.director.MoveStreamSession;
import ai.timefold.solver.core.impl.score.constraint.ConstraintMatchPolicy;
import ai.timefold.solver.core.impl.score.director.AbstractScoreDirectorFactory;
import ai.timefold.solver.core.impl.score.director.ScoreDirectorFactory;
Expand Down Expand Up @@ -55,10 +56,10 @@ public boolean supportsConstraintMatching() {
}

@Override
public IncrementalScoreDirector<Solution_, Score_> buildScoreDirector(boolean lookUpEnabled,
ConstraintMatchPolicy constraintMatchPolicy, boolean expectShadowVariablesInCorrectState) {
return new IncrementalScoreDirector<>(this, lookUpEnabled, constraintMatchPolicy, expectShadowVariablesInCorrectState,
incrementalScoreCalculatorSupplier.get());
public IncrementalScoreDirector<Solution_, Score_> buildScoreDirector(MoveStreamSession<Solution_> moveStreamSession,
boolean lookUpEnabled, ConstraintMatchPolicy constraintMatchPolicy, boolean expectShadowVariablesInCorrectState) {
return new IncrementalScoreDirector<>(this, moveStreamSession, lookUpEnabled, constraintMatchPolicy,
expectShadowVariablesInCorrectState, incrementalScoreCalculatorSupplier.get());
}

}
Loading

0 comments on commit 94cc32f

Please sign in to comment.