Skip to content
Merged
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package ai.timefold.solver.core.api.domain.common;

import ai.timefold.solver.core.api.solver.change.ProblemChange;
import ai.timefold.solver.core.preview.api.move.Move;

import org.jspecify.annotations.Nullable;

/**
* Allows to transfer an entity or fact instance (often from another {@link Thread})
* to another working solution.
*/
public interface Lookup {

/**
* Translates an entity or fact instance (often from another {@link Thread})
* to another working solution.
* Useful for {@link Move#rebase(Lookup) move rebasing}
* and in a {@link ProblemChange} and for multi-threaded solving.
* <p>
* Matching uses {@link PlanningId}.
*
* @param problemFactOrPlanningEntity The fact or entity to rebase.
* @return null if problemFactOrPlanningEntity is null
* @throws IllegalArgumentException if there is no working object for the fact or entity,
* if it cannot be looked up,
* or if its class is not supported.
* @throws IllegalStateException if it cannot be looked up
* @param <T> the object type
*/
<T> @Nullable T lookUpWorkingObject(@Nullable T problemFactOrPlanningEntity);

}
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,12 @@
import ai.timefold.solver.core.api.domain.entity.PlanningEntity;
import ai.timefold.solver.core.api.domain.solution.ProblemFactCollectionProperty;
import ai.timefold.solver.core.api.domain.valuerange.ValueRangeProvider;
import ai.timefold.solver.core.api.score.director.ScoreDirector;
import ai.timefold.solver.core.api.solver.change.ProblemChange;
import ai.timefold.solver.core.preview.api.move.Move;

/**
* Specifies that a bean property (or a field) is the id to match
* when {@link ScoreDirector#lookUpWorkingObject(Object) locating}
* when {@link Lookup#lookUpWorkingObject(Object) looking up}
* an externalObject (often from another {@link Thread} or JVM).
* Used during {@link Move} rebasing and in a {@link ProblemChange}.
* <p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,12 @@
import java.util.SortedSet;

import ai.timefold.solver.core.api.domain.entity.PlanningEntity;
import ai.timefold.solver.core.api.score.director.ScoreDirector;

/**
* Specifies that a property (or a field) on a {@link PlanningSolution} class is a {@link Collection} of planning entities.
* <p>
* Every element in the planning entity collection should have the {@link PlanningEntity} annotation.
* Every element in the planning entity collection will be added to the {@link ScoreDirector}.
* Every element in the planning entity collection will be registered with the solver.
* <p>
* For solver reproducibility, the collection must have a deterministic, stable iteration order.
* It is recommended to use a {@link List}, {@link LinkedHashSet} or {@link SortedSet}.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,12 @@
import java.lang.annotation.Target;

import ai.timefold.solver.core.api.domain.entity.PlanningEntity;
import ai.timefold.solver.core.api.score.director.ScoreDirector;

/**
* Specifies that a property (or a field) on a {@link PlanningSolution} class is a planning entity.
* <p>
* The planning entity should have the {@link PlanningEntity} annotation.
* The planning entity will be added to the {@link ScoreDirector}.
* The planning entity will be registered with the solver.
*/
@Target({ METHOD, FIELD })
@Retention(RUNTIME)
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
package ai.timefold.solver.core.api.solver.change;

import java.util.Optional;
import java.util.function.Consumer;

import ai.timefold.solver.core.api.domain.common.PlanningId;
import ai.timefold.solver.core.api.domain.common.Lookup;
import ai.timefold.solver.core.api.domain.entity.PlanningEntity;
import ai.timefold.solver.core.api.domain.solution.PlanningSolution;
import ai.timefold.solver.core.api.domain.variable.PlanningVariable;
import ai.timefold.solver.core.api.domain.variable.ShadowVariable;

import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;

/**
* Allows external changes to the {@link PlanningSolution working solution}. If the changes are not applied through
Expand All @@ -19,9 +17,12 @@
* never notified about them, resulting to inconsistencies in the {@link PlanningSolution working solution}.
* Should be used only from a {@link ProblemChange} implementation.
* To see an example implementation, please refer to the {@link ProblemChange} Javadoc.
*
* @see Lookup You may need to perform lookups of working objects from external objects.
*/
@NullMarked
public interface ProblemChangeDirector {
public interface ProblemChangeDirector
extends Lookup {

/**
* Add a new {@link PlanningEntity} instance into the {@link PlanningSolution working solution}.
Expand All @@ -35,7 +36,7 @@ public interface ProblemChangeDirector {
/**
* Remove an existing {@link PlanningEntity} instance from the {@link PlanningSolution working solution}.
* Translates the entity to a working planning entity by performing a lookup as defined by
* {@link #lookUpWorkingObjectOrFail(Object)}.
* {@link #lookUpWorkingObject(Object)}.
*
* @param entity the {@link PlanningEntity} instance
* @param entityConsumer removes the working entity from the {@link PlanningSolution working solution}
Expand All @@ -45,7 +46,7 @@ public interface ProblemChangeDirector {

/**
* Change a {@link PlanningVariable} value of a {@link PlanningEntity}. Translates the entity to a working
* planning entity by performing a lookup as defined by {@link #lookUpWorkingObjectOrFail(Object)}.
* planning entity by performing a lookup as defined by {@link #lookUpWorkingObject(Object)}.
*
* @param entity the {@link PlanningEntity} instance
* @param variableName name of the {@link PlanningVariable}
Expand All @@ -66,7 +67,7 @@ public interface ProblemChangeDirector {

/**
* Remove an existing problem fact from the {@link PlanningSolution working solution}. Translates the problem fact
* to a working problem fact by performing a lookup as defined by {@link #lookUpWorkingObjectOrFail(Object)}.
* to a working problem fact by performing a lookup as defined by {@link #lookUpWorkingObject(Object)}.
*
* @param problemFact the problem fact instance
* @param problemFactConsumer removes the working problem fact from the
Expand All @@ -78,7 +79,7 @@ public interface ProblemChangeDirector {
/**
* Change a property of either a {@link PlanningEntity} or a problem fact. Translates the entity or the problem fact
* to its {@link PlanningSolution working solution} counterpart by performing a lookup as defined by
* {@link #lookUpWorkingObjectOrFail(Object)}.
* {@link #lookUpWorkingObject(Object)}.
*
* @param problemFactOrEntity the {@link PlanningEntity} or the problem fact instance
* @param problemFactOrEntityConsumer updates the property of the {@link PlanningEntity}
Expand All @@ -88,32 +89,6 @@ public interface ProblemChangeDirector {
<EntityOrProblemFact> void changeProblemProperty(EntityOrProblemFact problemFactOrEntity,
Consumer<EntityOrProblemFact> problemFactOrEntityConsumer);

/**
* Translate an entity or fact instance (often from another {@link Thread} or JVM)
* to this {@link ProblemChangeDirector}'s internal working instance.
* <p>
* Matching uses {@link PlanningId}.
*
* @return null if externalObject is null
* @throws IllegalArgumentException if there is no workingObject for externalObject, if it cannot be looked up
* or if the externalObject's class is not supported
* @throws IllegalStateException if it cannot be looked up
* @param <EntityOrProblemFact> the object type
*/
<EntityOrProblemFact> @Nullable EntityOrProblemFact lookUpWorkingObjectOrFail(@Nullable EntityOrProblemFact externalObject);

/**
* As defined by {@link #lookUpWorkingObjectOrFail(Object)},
* but doesn't fail fast if no workingObject was ever added for the externalObject.
* It's recommended to use {@link #lookUpWorkingObjectOrFail(Object)} instead.
*
* @return {@link Optional#empty()} if there is no workingObject for externalObject, or if externalObject is null
* @throws IllegalArgumentException if it cannot be looked up or if the externalObject's class is not supported
* @throws IllegalStateException if it cannot be looked up
* @param <EntityOrProblemFact> the object type
*/
<EntityOrProblemFact> Optional<EntityOrProblemFact> lookUpWorkingObject(@Nullable EntityOrProblemFact externalObject);

/**
* Calls variable listeners on the external changes submitted so far.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -1,46 +1,34 @@
package ai.timefold.solver.core.api.solver.phase;

import java.util.function.BooleanSupplier;

import ai.timefold.solver.core.api.domain.solution.PlanningSolution;
import ai.timefold.solver.core.api.score.Score;
import ai.timefold.solver.core.api.score.director.ScoreDirector;
import ai.timefold.solver.core.api.solver.Solver;
import ai.timefold.solver.core.api.solver.change.ProblemChange;
import ai.timefold.solver.core.impl.phase.Phase;
import ai.timefold.solver.core.impl.score.director.InnerScoreDirector;
import ai.timefold.solver.core.preview.api.move.Move;

import org.jspecify.annotations.NullMarked;

/**
* Runs a custom algorithm as a {@link Phase} of the {@link Solver} that changes the planning variables.
* To change problem facts, use {@link Solver#addProblemChange(ProblemChange)} instead.
* <p>
* To add custom properties, configure custom properties and add public setters for them.
* To change problem facts and to add or remove entities, use {@link Solver#addProblemChange(ProblemChange)} instead.
*
* @param <Solution_> the solution type, the class with the {@link PlanningSolution} annotation
*/
@NullMarked
public interface PhaseCommand<Solution_> {

/**
* Changes {@link PlanningSolution working solution} of {@link ScoreDirector#getWorkingSolution()}.
* When the {@link PlanningSolution working solution} is modified,
* the {@link ScoreDirector} must be correctly notified
* (through {@link ScoreDirector#beforeVariableChanged(Object, String)} and
* {@link ScoreDirector#afterVariableChanged(Object, String)}),
* otherwise calculated {@link Score}s will be corrupted.
* Changes the current {@link PhaseCommandContext#getWorkingSolution() working solution}.
* The solver is notified of the changes through {@link PhaseCommandContext},
* specifically through {@link PhaseCommandContext#executeAndCalculateScore(Move)}.
* Any other modifications to the working solution are strictly forbidden
* and will likely cause the solver to be in an inconsistent state and throw an exception later on.
* <p>
* Don't forget to call {@link ScoreDirector#triggerVariableListeners()} after each set of changes
* (especially before every {@link InnerScoreDirector#calculateScore()} call)
* to ensure all shadow variables are updated.
* Don't forget to check {@link PhaseCommandContext#isPhaseTerminated() termination status} frequently
* to allow the solver to gracefully terminate when necessary.
*
* @param scoreDirector the {@link ScoreDirector} that needs to get notified of the changes.
* @param isPhaseTerminated long-running command implementations should check this periodically
* and terminate early if it returns true.
* Otherwise the terminations configured by the user will have no effect,
* as the solver can only terminate itself when a command has ended.
* @param context the context of the command, providing access to the working solution and allowing move execution
*/
void changeWorkingSolution(ScoreDirector<Solution_> scoreDirector, BooleanSupplier isPhaseTerminated);
void changeWorkingSolution(PhaseCommandContext<Solution_> context);

}
Loading
Loading