Skip to content

Commit

Permalink
Finishing touches
Browse files Browse the repository at this point in the history
  • Loading branch information
triceo committed Nov 7, 2024
1 parent 22c7afa commit e48cff2
Show file tree
Hide file tree
Showing 13 changed files with 59 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,9 @@
* non-empty if constraint has matches.
* This is a {@link List} to simplify access to individual elements,
* but it contains no duplicates just like {@link HashSet} wouldn't.
* @param matchCount -1 if {@link #matches()} is null, 0 if {@link #matches()} is empty,
* and {@link #matches() matches() size} otherwise.
* @param matchCount -1 if analysis not available,
* 0 if constraint has no matches,
* positive if constraint has matches.
*/
public record ConstraintAnalysis<Score_ extends Score<Score_>>(@NonNull ConstraintRef constraintRef, @NonNull Score_ weight,
@NonNull Score_ score, @Nullable List<MatchAnalysis<Score_>> matches, int matchCount) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* All classes used as constraint justifications must implement this interface.
*
* <p>
* Implementing classes ("implementations") may decide to implement {@link Comparable}
* Implementations may decide to implement {@link Comparable}
* to preserve order of instances when displayed in user interfaces, logs etc.
* This is entirely optional.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,31 @@

import ai.timefold.solver.core.api.solver.ScoreAnalysisFetchPolicy;

import org.jspecify.annotations.NullMarked;

/**
* Determines whether constraint match is enabled and whether constraint match justification is enabled.
*
* @see ai.timefold.solver.core.api.score.constraint.ConstraintMatch
* @see ai.timefold.solver.core.api.score.stream.ConstraintJustification
*/
@NullMarked
public enum ConstraintMatchPolicy {

DISABLED(false, false),
ENABLED_WITHOUT_JUSTIFICATIONS(true, false),
ENABLED(true, true);

/**
* To achieve the most performance out of the underlying solver,
* the policy should match whatever policy was used for score analysis.
* For example, if the fetch policy specifies that only match counts are necessary and not matches themselves
* ({@link ScoreAnalysisFetchPolicy#FETCH_MATCH_COUNT}),
* we can configure the solver to not produce justifications ({@link #ENABLED_WITHOUT_JUSTIFICATIONS}).
*
* @param scoreAnalysisFetchPolicy
* @return Match policy best suited for the given fetch policy.
*/
public static ConstraintMatchPolicy match(ScoreAnalysisFetchPolicy scoreAnalysisFetchPolicy) {
return switch (scoreAnalysisFetchPolicy) {
case FETCH_MATCH_COUNT, FETCH_SHALLOW -> ENABLED_WITHOUT_JUSTIFICATIONS;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,6 @@ public void setTrackingWorkingSolution(boolean trackingWorkingSolution) {
// Complex methods
// ************************************************************************

@Override
public InnerScoreDirector<Solution_, Score_> buildScoreDirector() {
return buildScoreDirector(false, ConstraintMatchPolicy.DISABLED);
}

@Override
public void assertScoreFromScratch(Solution_ solution) {
// Get the score before uncorruptedScoreDirector.calculateScore() modifies it
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,37 +27,12 @@ public interface InnerScoreDirectorFactory<Solution_, Score_ extends Score<Score
ScoreDefinition<Score_> getScoreDefinition();

@Override
InnerScoreDirector<Solution_, Score_> buildScoreDirector();

/**
* Like {@link #buildScoreDirector()}, but optionally enables {@link ConstraintMatch} tracking and look up
* where possible and necessary.
*
* @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}es
* @return never null
* @see InnerScoreDirector#getConstraintMatchPolicy()
* @see InnerScoreDirector#getConstraintMatchTotalMap()
*/
default InnerScoreDirector<Solution_, Score_> buildScoreDirector(boolean lookUpEnabled,
ConstraintMatchPolicy constraintMatchPolicy) {
return buildScoreDirector(lookUpEnabled, constraintMatchPolicy, true);
}

/**
* Like {@link #buildScoreDirector()}, but optionally enables {@link ConstraintMatch} tracking and look up
* where possible and necessary.
*
* @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} implementation do {@link ConstraintMatch}, if at all.
* @param expectShadowVariablesInCorrectState true, unless you have an exceptional reason.
* See {@link InnerScoreDirector#expectShadowVariablesInCorrectState()} for details.
* @return never null
* @see InnerScoreDirector#getConstraintMatchPolicy()
* @see InnerScoreDirector#getConstraintMatchTotalMap()
*/
@Override
InnerScoreDirector<Solution_, Score_> buildScoreDirector(boolean lookUpEnabled, ConstraintMatchPolicy constraintMatchPolicy,
boolean expectShadowVariablesInCorrectState);

Expand All @@ -76,7 +51,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, true);
return buildScoreDirector(lookUpEnabled, constraintMatchPolicy);
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package ai.timefold.solver.core.impl.score.director;

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.score.constraint.ConstraintMatchPolicy;

/**
* Builds a {@link ScoreDirector}.
Expand All @@ -11,10 +13,19 @@
public interface ScoreDirectorFactory<Solution_> {

/**
* Creates a new {@link ScoreDirector} instance.
* Like {@link #buildScoreDirector(boolean, ConstraintMatchPolicy, boolean)},
* with the final parameter set to true.
*
* @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
*/
ScoreDirector<Solution_> buildScoreDirector();
default ScoreDirector<Solution_> buildScoreDirector(boolean lookUpEnabled, ConstraintMatchPolicy constraintMatchPolicy) {
return buildScoreDirector(lookUpEnabled, constraintMatchPolicy, true);
}

ScoreDirector<Solution_> buildScoreDirector(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.buildin.simple.SimpleScore;
import ai.timefold.solver.core.api.score.director.ScoreDirector;
import ai.timefold.solver.core.impl.domain.variable.descriptor.GenuineVariableDescriptor;
import ai.timefold.solver.core.impl.score.constraint.ConstraintMatchPolicy;
import ai.timefold.solver.core.impl.score.director.ScoreDirectorFactory;
import ai.timefold.solver.core.impl.score.director.easy.EasyScoreDirectorFactory;
import ai.timefold.solver.core.impl.testdata.domain.TestdataEntity;
Expand Down Expand Up @@ -60,7 +61,8 @@ void doMove() {
ScoreDirectorFactory<TestdataEntityProvidingSolution> scoreDirectorFactory =
new EasyScoreDirectorFactory<>(TestdataEntityProvidingSolution.buildSolutionDescriptor(),
solution -> SimpleScore.ZERO);
ScoreDirector<TestdataEntityProvidingSolution> scoreDirector = scoreDirectorFactory.buildScoreDirector();
ScoreDirector<TestdataEntityProvidingSolution> scoreDirector =
scoreDirectorFactory.buildScoreDirector(false, ConstraintMatchPolicy.DISABLED);

GenuineVariableDescriptor<TestdataEntityProvidingSolution> variableDescriptor =
TestdataEntityProvidingEntity.buildVariableDescriptorForValue();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import ai.timefold.solver.core.api.score.buildin.simple.SimpleScore;
import ai.timefold.solver.core.api.score.director.ScoreDirector;
import ai.timefold.solver.core.impl.domain.variable.descriptor.GenuineVariableDescriptor;
import ai.timefold.solver.core.impl.score.constraint.ConstraintMatchPolicy;
import ai.timefold.solver.core.impl.score.director.ScoreDirectorFactory;
import ai.timefold.solver.core.impl.score.director.easy.EasyScoreDirectorFactory;
import ai.timefold.solver.core.impl.testdata.domain.TestdataEntity;
Expand Down Expand Up @@ -75,7 +76,8 @@ void doMove() {
ScoreDirectorFactory<TestdataEntityProvidingSolution> scoreDirectorFactory =
new EasyScoreDirectorFactory<>(TestdataEntityProvidingSolution.buildSolutionDescriptor(),
solution -> SimpleScore.ZERO);
ScoreDirector<TestdataEntityProvidingSolution> scoreDirector = scoreDirectorFactory.buildScoreDirector();
ScoreDirector<TestdataEntityProvidingSolution> scoreDirector =
scoreDirectorFactory.buildScoreDirector(false, ConstraintMatchPolicy.DISABLED);
GenuineVariableDescriptor<TestdataEntityProvidingSolution> variableDescriptor = TestdataEntityProvidingEntity
.buildVariableDescriptorForValue();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import ai.timefold.solver.core.api.score.director.ScoreDirector;
import ai.timefold.solver.core.impl.domain.entity.descriptor.EntityDescriptor;
import ai.timefold.solver.core.impl.domain.variable.descriptor.GenuineVariableDescriptor;
import ai.timefold.solver.core.impl.score.constraint.ConstraintMatchPolicy;
import ai.timefold.solver.core.impl.score.director.ScoreDirectorFactory;
import ai.timefold.solver.core.impl.score.director.easy.EasyScoreDirectorFactory;
import ai.timefold.solver.core.impl.testdata.domain.TestdataEntity;
Expand Down Expand Up @@ -128,7 +129,8 @@ void doMove() {
ScoreDirectorFactory<TestdataEntityProvidingSolution> scoreDirectorFactory =
new EasyScoreDirectorFactory<>(TestdataEntityProvidingSolution.buildSolutionDescriptor(),
solution -> SimpleScore.ZERO);
ScoreDirector<TestdataEntityProvidingSolution> scoreDirector = scoreDirectorFactory.buildScoreDirector();
ScoreDirector<TestdataEntityProvidingSolution> scoreDirector =
scoreDirectorFactory.buildScoreDirector(false, ConstraintMatchPolicy.DISABLED);
List<GenuineVariableDescriptor<TestdataEntityProvidingSolution>> variableDescriptorList = TestdataEntityProvidingEntity
.buildEntityDescriptor().getGenuineVariableDescriptorList();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import ai.timefold.solver.core.api.score.director.ScoreDirector;
import ai.timefold.solver.core.impl.domain.entity.descriptor.EntityDescriptor;
import ai.timefold.solver.core.impl.domain.variable.descriptor.GenuineVariableDescriptor;
import ai.timefold.solver.core.impl.score.constraint.ConstraintMatchPolicy;
import ai.timefold.solver.core.impl.score.director.ScoreDirectorFactory;
import ai.timefold.solver.core.impl.score.director.easy.EasyScoreDirectorFactory;
import ai.timefold.solver.core.impl.testdata.domain.TestdataEntity;
Expand Down Expand Up @@ -103,7 +104,8 @@ void doMove() {
ScoreDirectorFactory<TestdataEntityProvidingSolution> scoreDirectorFactory =
new EasyScoreDirectorFactory<>(TestdataEntityProvidingSolution.buildSolutionDescriptor(),
solution -> SimpleScore.ZERO);
ScoreDirector<TestdataEntityProvidingSolution> scoreDirector = scoreDirectorFactory.buildScoreDirector();
ScoreDirector<TestdataEntityProvidingSolution> scoreDirector =
scoreDirectorFactory.buildScoreDirector(false, ConstraintMatchPolicy.DISABLED);
EntityDescriptor<TestdataEntityProvidingSolution> entityDescriptor = TestdataEntityProvidingEntity
.buildEntityDescriptor();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import ai.timefold.solver.core.api.score.stream.ConstraintStreamImplType;
import ai.timefold.solver.core.config.score.director.ScoreDirectorFactoryConfig;
import ai.timefold.solver.core.config.solver.EnvironmentMode;
import ai.timefold.solver.core.impl.score.constraint.ConstraintMatchPolicy;
import ai.timefold.solver.core.impl.score.director.incremental.IncrementalScoreDirector;
import ai.timefold.solver.core.impl.score.director.stream.BavetConstraintStreamScoreDirectorFactory;
import ai.timefold.solver.core.impl.testdata.domain.TestdataSolution;
Expand All @@ -36,7 +37,8 @@ void incrementalScoreCalculatorWithCustomProperties() {

ScoreDirectorFactory<TestdataSolution> scoreDirectorFactory = buildTestdataScoreDirectoryFactory(config);
IncrementalScoreDirector<TestdataSolution, ?> scoreDirector =
(IncrementalScoreDirector<TestdataSolution, ?>) scoreDirectorFactory.buildScoreDirector();
(IncrementalScoreDirector<TestdataSolution, ?>) scoreDirectorFactory.buildScoreDirector(false,
ConstraintMatchPolicy.DISABLED);
TestCustomPropertiesIncrementalScoreCalculator scoreCalculator =
(TestCustomPropertiesIncrementalScoreCalculator) scoreDirector
.getIncrementalScoreCalculator();
Expand All @@ -59,7 +61,8 @@ void buildWithAssertionScoreDirectorFactory() {
ScoreDirectorFactory<TestdataSolution> assertionScoreDirectorFactory =
scoreDirectorFactory.getAssertionScoreDirectorFactory();
IncrementalScoreDirector<TestdataSolution, ?> assertionScoreDirector =
(IncrementalScoreDirector<TestdataSolution, ?>) assertionScoreDirectorFactory.buildScoreDirector();
(IncrementalScoreDirector<TestdataSolution, ?>) assertionScoreDirectorFactory.buildScoreDirector(false,
ConstraintMatchPolicy.DISABLED);
IncrementalScoreCalculator<TestdataSolution, ?> assertionScoreCalculator =
assertionScoreDirector.getIncrementalScoreCalculator();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import ai.timefold.solver.core.config.score.director.ScoreDirectorFactoryConfig;
import ai.timefold.solver.core.config.solver.EnvironmentMode;
import ai.timefold.solver.core.impl.domain.solution.descriptor.SolutionDescriptor;
import ai.timefold.solver.core.impl.score.constraint.ConstraintMatchPolicy;
import ai.timefold.solver.core.impl.score.director.AbstractScoreDirectorSemanticsTest;
import ai.timefold.solver.core.impl.score.director.InnerScoreDirectorFactory;
import ai.timefold.solver.core.impl.score.director.ScoreDirectorFactory;
Expand Down Expand Up @@ -70,7 +71,8 @@ void easyScoreCalculatorWithCustomProperties() {
config.setEasyScoreCalculatorCustomProperties(customProperties);

EasyScoreDirector<TestdataSolution, ?> scoreDirector =
(EasyScoreDirector<TestdataSolution, ?>) buildTestdataScoreDirectoryFactory(config).buildScoreDirector();
(EasyScoreDirector<TestdataSolution, ?>) buildTestdataScoreDirectoryFactory(config)
.buildScoreDirector(false, ConstraintMatchPolicy.DISABLED);
TestCustomPropertiesEasyScoreCalculator scoreCalculator =
(TestCustomPropertiesEasyScoreCalculator) scoreDirector
.getEasyScoreCalculator();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ void shadowVariableCorruption() {
(solution_) -> SimpleScore.of(0));
scoreDirectorFactory
.setInitializingScoreTrend(InitializingScoreTrend.buildUniformTrend(InitializingScoreTrendLevel.ONLY_DOWN, 1));
try (var scoreDirector = scoreDirectorFactory.buildScoreDirector(false, ConstraintMatchPolicy.DISABLED, true)) {
try (var scoreDirector = scoreDirectorFactory.buildScoreDirector(false, ConstraintMatchPolicy.DISABLED)) {
var solution = new TestdataCorruptedShadowedSolution("s1");
var v1 = new TestdataValue("v1");
var v2 = new TestdataValue("v2");
Expand Down

0 comments on commit e48cff2

Please sign in to comment.