Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions core/src/build/revapi-differences.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,17 @@
"old": "method void ai.timefold.solver.core.api.solver.ProblemSizeStatistics::<init>(long, long, long, double)",
"new": "method void ai.timefold.solver.core.api.solver.ProblemSizeStatistics::<init>(long, java.util.SequencedMap<java.lang.Class<?>, java.lang.Long>, long, long, java.util.SequencedMap<java.lang.Class<?>, java.util.SequencedMap<java.lang.String, java.lang.Long>>, double)",
"justification": "Type is not supposed to be constructed by user; safe."
},
{
"ignore": true,
"code": "java.annotation.attributeValueChanged",
"old": "class ai.timefold.solver.core.config.phase.PhaseConfig<Config_ extends ai.timefold.solver.core.config.phase.PhaseConfig<Config_>>",
"new": "class ai.timefold.solver.core.config.phase.PhaseConfig<Config_ extends ai.timefold.solver.core.config.phase.PhaseConfig<Config_>>",
"annotationType": "jakarta.xml.bind.annotation.XmlType",
"attribute": "propOrder",
"oldValue": "{\"terminationConfig\"}",
"newValue": "{\"environmentMode\", \"terminationConfig\"}",
"justification": "Environment mode per phase"
}
]
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@
import jakarta.xml.bind.annotation.XmlSeeAlso;
import jakarta.xml.bind.annotation.XmlType;

import ai.timefold.solver.core.api.solver.SolverFactory;
import ai.timefold.solver.core.config.AbstractConfig;
import ai.timefold.solver.core.config.constructionheuristic.ConstructionHeuristicPhaseConfig;
import ai.timefold.solver.core.config.exhaustivesearch.ExhaustiveSearchPhaseConfig;
import ai.timefold.solver.core.config.localsearch.LocalSearchPhaseConfig;
import ai.timefold.solver.core.config.partitionedsearch.PartitionedSearchPhaseConfig;
import ai.timefold.solver.core.config.phase.custom.CustomPhaseConfig;
import ai.timefold.solver.core.config.solver.EnvironmentMode;
import ai.timefold.solver.core.config.solver.termination.TerminationConfig;
import ai.timefold.solver.core.config.util.ConfigUtils;

Expand All @@ -24,20 +26,51 @@
PartitionedSearchPhaseConfig.class
})
@XmlType(propOrder = {
"environmentMode",
"terminationConfig"
})
public abstract class PhaseConfig<Config_ extends PhaseConfig<Config_>> extends AbstractConfig<Config_> {

// Warning: all fields are null (and not defaulted) because they can be inherited
// and also because the input config file should match the output config file

// Per phase environment
protected EnvironmentMode environmentMode = null;

@XmlElement(name = "termination")
protected TerminationConfig terminationConfig = null;

// ************************************************************************
// Constructors and simple getters/setters
// ************************************************************************

/**
* @return null when this phase runs in the solver's {@link EnvironmentMode}
* @see #setEnvironmentMode(EnvironmentMode)
*/
public @Nullable EnvironmentMode getEnvironmentMode() {
return environmentMode;
}

/**
* Overrides the solver's {@link EnvironmentMode} for this phase only.
* <p>
* Null, the default, means the phase runs in the solver's environment mode.
* A non-null value must obey two rules, both checked when the
* {@link SolverFactory SolverFactory} is built,
* so that a violation fails there rather than during solving:
* <ul>
* <li>it may not be less strict than the solver's environment mode;</li>
* <li>it may not be set at all when the solver's environment mode is
* {@link EnvironmentMode#NON_REPRODUCIBLE}.</li>
* </ul>
*
* @param environmentMode null to run this phase in the solver's environment mode
*/
public void setEnvironmentMode(@Nullable EnvironmentMode environmentMode) {
this.environmentMode = environmentMode;
}

public @Nullable TerminationConfig getTerminationConfig() {
return terminationConfig;
}
Expand All @@ -50,13 +83,19 @@ public void setTerminationConfig(@Nullable TerminationConfig terminationConfig)
// With methods
// ************************************************************************

public @NonNull Config_ withEnvironmentMode(@NonNull EnvironmentMode environmentMode) {
this.setEnvironmentMode(environmentMode);
return (Config_) this;
}

public @NonNull Config_ withTerminationConfig(@NonNull TerminationConfig terminationConfig) {
this.setTerminationConfig(terminationConfig);
return (Config_) this;
}

@Override
public @NonNull Config_ inherit(@NonNull Config_ inheritedConfig) {
environmentMode = ConfigUtils.inheritOverwritableProperty(environmentMode, inheritedConfig.getEnvironmentMode());
terminationConfig = ConfigUtils.inheritConfig(terminationConfig, inheritedConfig.getTerminationConfig());
return (Config_) this;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,11 +191,12 @@ public void phaseEnded(ConstructionHeuristicPhaseScope<Solution_> phaseScope) {
if (decider.isLoggingEnabled() && logger.isInfoEnabled()) {
logger.info(
"""
{}Construction Heuristic phase ({}) ended: time spent ({}), best score ({}), \
{}Construction Heuristic phase ({}) ended: time spent ({}), environment mode ({}), best score ({}), \
{}move evaluation speed ({}/sec), step total ({}).""",
logIndentation,
phaseIndex,
phaseScope.calculateSolverTimeMillisSpentUpToNow(),
environmentMode.name(),
phaseScope.getBestScore().raw(),
// Multithreaded solving uses "effective" move evaluation speed, since not all evaluated moves
// are foraged
Expand Down Expand Up @@ -227,25 +228,19 @@ public void solvingError(SolverScope<Solution_> solverScope, Exception exception
}

public static class DefaultConstructionHeuristicPhaseBuilder<Solution_>
extends AbstractPossiblyInitializingPhaseBuilder<Solution_> {
extends AbstractPossiblyInitializingPhaseBuilder<Solution_, DefaultConstructionHeuristicPhase<Solution_>> {

private final EntityPlacer<Solution_> entityPlacer;
private final ConstructionHeuristicDecider<Solution_> decider;

public DefaultConstructionHeuristicPhaseBuilder(int phaseIndex, boolean lastInitializingPhase, String logIndentation,
PhaseTermination<Solution_> phaseTermination, EntityPlacer<Solution_> entityPlacer,
ConstructionHeuristicDecider<Solution_> decider) {
super(phaseIndex, lastInitializingPhase, logIndentation, phaseTermination);
public DefaultConstructionHeuristicPhaseBuilder(int phaseIndex, boolean lastInitializingPhase,
EnvironmentMode environmentMode, String logIndentation, PhaseTermination<Solution_> phaseTermination,
EntityPlacer<Solution_> entityPlacer, ConstructionHeuristicDecider<Solution_> decider) {
super(phaseIndex, lastInitializingPhase, environmentMode, logIndentation, phaseTermination);
this.entityPlacer = entityPlacer;
this.decider = decider;
}

@Override
public DefaultConstructionHeuristicPhaseBuilder<Solution_> enableAssertions(EnvironmentMode environmentMode) {
super.enableAssertions(environmentMode);
return this;
}

public EntityPlacer<Solution_> getEntityPlacer() {
return entityPlacer;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,9 @@ public final DefaultConstructionHeuristicPhaseBuilder<Solution_> getBuilder(int
constructionHeuristicType_.getDefaultEntitySorterManner());
var valueSorterManner = Objects.requireNonNullElse(phaseConfig.getValueSorterManner(),
constructionHeuristicType_.getDefaultValueSorterManner());
var environmentMode = resolveEnvironmentMode(solverConfigPolicy);
var phaseConfigPolicy = solverConfigPolicy.cloneBuilder()
.withEnvironmentMode(environmentMode)
.withReinitializeVariableFilterEnabled(true)
.withUnassignedValuesAllowed(true)
.withEntitySorterManner(entitySorterManner)
Expand All @@ -70,9 +72,9 @@ protected DefaultConstructionHeuristicPhaseBuilder<Solution_> createBuilder(
boolean lastInitializingPhase, EntityPlacer<Solution_> entityPlacer) {
var phaseTermination = buildPhaseTermination(phaseConfigPolicy, solverTermination);
return new DefaultConstructionHeuristicPhaseBuilder<>(phaseIndex, lastInitializingPhase,
phaseConfigPolicy.getLogIndentation(), phaseTermination, entityPlacer,
phaseConfigPolicy.getEnvironmentMode(), phaseConfigPolicy.getLogIndentation(), phaseTermination, entityPlacer,
buildDecider(phaseConfigPolicy, phaseTermination))
.enableAssertions(phaseConfigPolicy.getEnvironmentMode());
.enableAssertions();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ public static <Solution_> ShadowVariableSupport<Solution_> create(InnerScoreDire

public void linkShadowVariables() {
if (listVariableDescriptor != null) {
listVariableChangeHandlerList.add(demand(listVariableDescriptor.getStateDemand()));
listVariableChangeHandlerList.add(scoreDirector.getListVariableStateSupply(listVariableDescriptor));
}
scoreDirector.getSolutionDescriptor().getEntityDescriptors().stream()
.map(EntityDescriptor::getDeclaredShadowVariableDescriptors)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -273,12 +273,18 @@ private List<BasicVariableDescriptor<Solution_>> fetchBasicDescriptors(EntityDes
private static class InternalScoreDirectorFactory<Solution_, Score_ extends Score<Score_>>
extends AbstractScoreDirectorFactory<Solution_, Score_, InternalScoreDirectorFactory<Solution_, Score_>> {

public InternalScoreDirectorFactory(SolutionDescriptor<Solution_> solutionDescriptor, EnvironmentMode environmentMode) {
super(solutionDescriptor, environmentMode);
public InternalScoreDirectorFactory(SolutionDescriptor<Solution_> solutionDescriptor,
EnvironmentMode globalEnvironmentMode) {
super(solutionDescriptor, globalEnvironmentMode);
}

/**
* Score directors are built directly through {@link InternalScoreDirector.Builder},
* never through this factory; the inherited no-arg variant funnels into this one.
*/
@Override
public AbstractScoreDirector.AbstractScoreDirectorBuilder<Solution_, Score_, ?, ?> createScoreDirectorBuilder() {
public AbstractScoreDirector.AbstractScoreDirectorBuilder<Solution_, Score_, ?, ?>
createScoreDirectorBuilder(EnvironmentMode environmentMode) {
throw new UnsupportedOperationException();
}
}
Expand Down Expand Up @@ -319,7 +325,8 @@ public static final class Builder<Solution_, Score_ extends Score<Score_>>

public Builder(SolutionDescriptor<Solution_> solutionDescriptor) {
// We use PHASE_ASSERT by default
super(new InternalScoreDirectorFactory<>(solutionDescriptor, EnvironmentMode.PHASE_ASSERT));
super(new InternalScoreDirectorFactory<>(solutionDescriptor, EnvironmentMode.PHASE_ASSERT),
EnvironmentMode.PHASE_ASSERT);
withConstraintMatchPolicy(DISABLED);
withLookUpEnabled(false);
withExpectShadowVariablesInCorrectState(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,11 +101,12 @@ private void phaseEnded(ExhaustiveSearchPhaseScope<Solution_> phaseScope) {
decider.phaseEnded(phaseScope);
phaseScope.endingNow();
logger.info("""
{}Exhaustive Search phase ({}) ended: time spent ({}), best score ({}),\
{}Exhaustive Search phase ({}) ended: time spent ({}), environment mode ({}), best score ({}),\
move evaluation speed ({}/sec), step total ({}).""",
logIndentation,
phaseIndex,
phaseScope.calculateSolverTimeMillisSpentUpToNow(),
environmentMode.name(),
phaseScope.getBestScore().raw(),
phaseScope.getPhaseMoveEvaluationSpeed(),
phaseScope.getNextStepIndex());
Expand Down Expand Up @@ -133,28 +134,30 @@ private void stepEnded(ExhaustiveSearchStepScope<Solution_> stepScope) {
}
}

public static class Builder<Solution_> extends AbstractPhaseBuilder<Solution_> {
public static class Builder<Solution_> extends AbstractPhaseBuilder<Solution_, DefaultExhaustiveSearchPhase<Solution_>> {

private final Comparator<ExhaustiveSearchNode<Solution_>> nodeComparator;
private final AbstractExhaustiveSearchDecider<Solution_, ? extends Score<?>> decider;

private boolean assertWorkingSolutionScoreFromScratch = false;
private boolean assertExpectedWorkingSolutionScore = false;

public Builder(int phaseIndex, String logIndentation, PhaseTermination<Solution_> phaseTermination,
Comparator<ExhaustiveSearchNode<Solution_>> nodeComparator,
public Builder(int phaseIndex, EnvironmentMode environmentMode, String logIndentation,
PhaseTermination<Solution_> phaseTermination, Comparator<ExhaustiveSearchNode<Solution_>> nodeComparator,
AbstractExhaustiveSearchDecider<Solution_, ? extends Score<?>> decider) {
super(phaseIndex, logIndentation, phaseTermination);
super(phaseIndex, environmentMode, logIndentation, phaseTermination);
this.nodeComparator = nodeComparator;
this.decider = decider;
}

@SuppressWarnings("unchecked")
@Override
public Builder<Solution_> enableAssertions(EnvironmentMode environmentMode) {
super.enableAssertions(environmentMode);
public <Builder_ extends AbstractPhaseBuilder<Solution_, DefaultExhaustiveSearchPhase<Solution_>>> Builder_
enableAssertions() {
super.enableAssertions();
assertWorkingSolutionScoreFromScratch = environmentMode.isFullyAsserted();
assertExpectedWorkingSolutionScore = environmentMode.isIntrusivelyAsserted();
Comment thread
zepfred marked this conversation as resolved.
return this;
return (Builder_) this;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
import ai.timefold.solver.core.config.heuristic.selector.move.generic.ChangeMoveSelectorConfig;
import ai.timefold.solver.core.config.heuristic.selector.move.generic.list.ListChangeMoveSelectorConfig;
import ai.timefold.solver.core.config.heuristic.selector.value.ValueSelectorConfig;
import ai.timefold.solver.core.config.solver.EnvironmentMode;
import ai.timefold.solver.core.config.util.ConfigUtils;
import ai.timefold.solver.core.impl.domain.entity.descriptor.EntityDescriptor;
import ai.timefold.solver.core.impl.domain.solution.descriptor.SolutionDescriptor;
Expand Down Expand Up @@ -61,7 +60,9 @@ public ExhaustiveSearchPhase<Solution_> buildPhase(int phaseIndex, boolean lastI
var valueSorterManner = Objects.requireNonNullElse(
phaseConfig.getValueSorterManner(),
exhaustiveSearchType.getDefaultValueSorterManner());
var environmentMode = resolveEnvironmentMode(solverConfigPolicy);
var phaseConfigPolicy = solverConfigPolicy.cloneBuilder()
.withEnvironmentMode(environmentMode)
.withReinitializeVariableFilterEnabled(true)
.withEntitySorterManner(entitySorterManner)
.withValueSorterManner(valueSorterManner)
Expand All @@ -75,9 +76,8 @@ public ExhaustiveSearchPhase<Solution_> buildPhase(int phaseIndex, boolean lastI
var basicVarEntitySelectorConfig = buildEntitySelectorConfig(phaseConfigPolicy, false);
var basicVarEntitySelector = EntitySelectorFactory.<Solution_> create(basicVarEntitySelectorConfig)
.buildEntitySelector(phaseConfigPolicy, SelectionCacheType.PHASE, SelectionOrder.ORIGINAL);
var basicVarDecider =
buildDecider(phaseConfigPolicy, basicVarEntitySelector, bestSolutionRecaller, phaseTermination,
scoreBounderEnabled, false);
var basicVarDecider = buildDecider(phaseConfigPolicy, basicVarEntitySelector, bestSolutionRecaller,
phaseTermination, scoreBounderEnabled, false);
var listVarEntitySelectorConfig = buildEntitySelectorConfig(phaseConfigPolicy, true);
var listVarEntitySelector = EntitySelectorFactory.<Solution_> create(listVarEntitySelectorConfig)
.buildEntitySelector(phaseConfigPolicy, SelectionCacheType.PHASE, SelectionOrder.ORIGINAL);
Expand All @@ -93,9 +93,9 @@ EntitySelectorFactory.<Solution_> create(entitySelectorConfig)
decider = buildDecider(phaseConfigPolicy, entitySelector, bestSolutionRecaller, phaseTermination,
scoreBounderEnabled, isListVariable);
}
return new DefaultExhaustiveSearchPhase.Builder<>(phaseIndex, solverConfigPolicy.getLogIndentation(), phaseTermination,
nodeExplorationType.buildNodeComparator(scoreBounderEnabled), decider)
.enableAssertions(phaseConfigPolicy.getEnvironmentMode()).build();
return new DefaultExhaustiveSearchPhase.Builder<>(phaseIndex, environmentMode, solverConfigPolicy.getLogIndentation(),
phaseTermination, nodeExplorationType.buildNodeComparator(scoreBounderEnabled), decider)
.enableAssertions().build();
}

private static NodeExplorationType getNodeExplorationType(ExhaustiveSearchType exhaustiveSearchType,
Expand Down Expand Up @@ -200,13 +200,7 @@ protected EntityDescriptor<Solution_> deduceEntityDescriptor(SolutionDescriptor<
new MoveSelectorBasedMoveRepository<>(moveSelector), scoreBounderEnabled, scoreBounder);

}
EnvironmentMode environmentMode = configPolicy.getEnvironmentMode();
if (environmentMode.isFullyAsserted()) {
decider.setAssertMoveScoreFromScratch(true);
}
if (environmentMode.isIntrusivelyAsserted()) {
decider.setAssertExpectedUndoMoveScore(true);
}
decider.enableAssertions(configPolicy.getEnvironmentMode());
return decider;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.util.ArrayList;

import ai.timefold.solver.core.api.score.Score;
import ai.timefold.solver.core.config.solver.EnvironmentMode;
import ai.timefold.solver.core.impl.exhaustivesearch.event.ExhaustiveSearchPhaseLifecycleListener;
import ai.timefold.solver.core.impl.exhaustivesearch.node.ExhaustiveSearchLayer;
import ai.timefold.solver.core.impl.exhaustivesearch.node.ExhaustiveSearchNode;
Expand Down Expand Up @@ -57,19 +58,16 @@ public abstract sealed class AbstractExhaustiveSearchDecider<Solution_, Score_ e
this.scoreBounder = scoreBounder;
}

public void enableAssertions(EnvironmentMode environmentMode) {
this.assertMoveScoreFromScratch = environmentMode.isFullyAsserted();
this.assertExpectedUndoMoveScore = environmentMode.isIntrusivelyAsserted();
}

@SuppressWarnings("unchecked")
public ScoreBounder<Score_> getScoreBounder() {
return (ScoreBounder<Score_>) scoreBounder;
}

public void setAssertMoveScoreFromScratch(boolean assertMoveScoreFromScratch) {
this.assertMoveScoreFromScratch = assertMoveScoreFromScratch;
}

public void setAssertExpectedUndoMoveScore(boolean assertExpectedUndoMoveScore) {
this.assertExpectedUndoMoveScore = assertExpectedUndoMoveScore;
}

protected void enableAcceptUninitializedSolutions() {
acceptUninitializedSolutions = true;
}
Expand Down
Loading