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
4 changes: 2 additions & 2 deletions benchmark/src/main/resources/benchmark.xsd
Original file line number Diff line number Diff line change
Expand Up @@ -2387,7 +2387,7 @@
</xs:choice>


<xs:element minOccurs="0" name="moveProviderClass" type="xs:string"/>
<xs:element minOccurs="0" name="neighborhoodProviderClass" type="xs:string"/>


<xs:element minOccurs="0" name="acceptor" type="tns:localSearchAcceptorConfig"/>
Expand Down Expand Up @@ -2615,7 +2615,7 @@
<xs:enumeration value="PLANNING_SOLUTION_DIFF"/>


<xs:enumeration value="MOVE_STREAMS"/>
<xs:enumeration value="NEIGHBORHOODS"/>


</xs:restriction>
Expand Down
4 changes: 2 additions & 2 deletions core/src/build/revapi-differences.json
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,8 @@
"annotationType": "jakarta.xml.bind.annotation.XmlType",
"attribute": "propOrder",
"oldValue": "{\"localSearchType\", \"moveSelectorConfig\", \"acceptorConfig\", \"foragerConfig\"}",
"newValue": "{\"localSearchType\", \"moveSelectorConfig\", \"moveProviderClass\", \"acceptorConfig\", \"foragerConfig\"}",
"justification": "Move Streams arrives as a preview feature."
"newValue": "{\"localSearchType\", \"moveSelectorConfig\", \"neighborhoodProviderClass\", \"acceptorConfig\", \"foragerConfig\"}",
"justification": "Neighborhoods API arrives as a preview feature."
},
{
"ignore": true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
* The property must be an object type. Primitive types (such as int, double, long) are not allowed.
* <p>
* It is specified on a getter of a java bean property (or directly on a field) of a {@link PlanningEntity} class.
* <p>
* It is sometimes referred to as the "basic" planning variable,
* to distinguish it from a {@link PlanningListVariable list variable}.
*/
@Target({ METHOD, FIELD })
@Retention(RUNTIME)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,15 @@
import ai.timefold.solver.core.config.phase.PhaseConfig;
import ai.timefold.solver.core.config.solver.PreviewFeature;
import ai.timefold.solver.core.config.util.ConfigUtils;
import ai.timefold.solver.core.impl.move.streams.maybeapi.stream.MoveProvider;
import ai.timefold.solver.core.impl.neighborhood.maybeapi.NeighborhoodProvider;

import org.jspecify.annotations.NonNull;
import org.jspecify.annotations.Nullable;

@XmlType(propOrder = {
"localSearchType",
"moveSelectorConfig",
"moveProviderClass",
"neighborhoodProviderClass",
"acceptorConfig",
"foragerConfig"
})
Expand Down Expand Up @@ -77,7 +77,7 @@ public class LocalSearchPhaseConfig extends PhaseConfig<LocalSearchPhaseConfig>
@XmlElement(name = UnionMoveSelectorConfig.XML_ELEMENT_NAME, type = UnionMoveSelectorConfig.class)
})
private MoveSelectorConfig moveSelectorConfig = null;
private Class<? extends MoveProvider> moveProviderClass = null;
private Class<? extends NeighborhoodProvider> neighborhoodProviderClass = null;
@XmlElement(name = "acceptor")
private LocalSearchAcceptorConfig acceptorConfig = null;
@XmlElement(name = "forager")
Expand All @@ -104,19 +104,19 @@ public void setMoveSelectorConfig(@Nullable MoveSelectorConfig moveSelectorConfi
}

/**
* Part of {@link PreviewFeature#MOVE_STREAMS}.
* Part of {@link PreviewFeature#NEIGHBORHOODS}.
*/
@SuppressWarnings("unchecked")
public @Nullable <Solution_> Class<? extends MoveProvider<Solution_>> getMoveProviderClass() {
return (Class<? extends MoveProvider<Solution_>>) moveProviderClass;
public @Nullable <Solution_> Class<? extends NeighborhoodProvider<Solution_>> getNeighborhoodProviderClass() {
return (Class<? extends NeighborhoodProvider<Solution_>>) neighborhoodProviderClass;
}

/**
* Part of {@link PreviewFeature#MOVE_STREAMS}.
* Part of {@link PreviewFeature#NEIGHBORHOODS}.
*/
@SuppressWarnings("rawtypes")
public void setMoveProviderClass(@Nullable Class<? extends MoveProvider> moveProviderClass) {
this.moveProviderClass = moveProviderClass;
public void setNeighborhoodProviderClass(@Nullable Class<? extends NeighborhoodProvider> neighborhoodProviderClass) {
this.neighborhoodProviderClass = neighborhoodProviderClass;
}

public @Nullable LocalSearchAcceptorConfig getAcceptorConfig() {
Expand Down Expand Up @@ -150,10 +150,11 @@ public void setForagerConfig(@Nullable LocalSearchForagerConfig foragerConfig) {
}

/**
* Part of {@link PreviewFeature#MOVE_STREAMS}.
* Part of {@link PreviewFeature#NEIGHBORHOODS}.
*/
public @NonNull LocalSearchPhaseConfig withMoveProviderClass(@NonNull Class<? extends MoveProvider<?>> moveProviderClass) {
this.moveProviderClass = moveProviderClass;
public @NonNull LocalSearchPhaseConfig
withMoveProviderClass(@NonNull Class<? extends NeighborhoodProvider<?>> moveProviderClass) {
this.neighborhoodProviderClass = moveProviderClass;
return this;
}

Expand All @@ -174,8 +175,8 @@ public void setForagerConfig(@Nullable LocalSearchForagerConfig foragerConfig) {
inheritedConfig.getLocalSearchType());
setMoveSelectorConfig(ConfigUtils.inheritOverwritableProperty(
getMoveSelectorConfig(), inheritedConfig.getMoveSelectorConfig()));
setMoveProviderClass(ConfigUtils.inheritOverwritableProperty(getMoveProviderClass(),
inheritedConfig.getMoveProviderClass()));
setNeighborhoodProviderClass(ConfigUtils.inheritOverwritableProperty(getNeighborhoodProviderClass(),
inheritedConfig.getNeighborhoodProviderClass()));
acceptorConfig = ConfigUtils.inheritConfig(acceptorConfig, inheritedConfig.getAcceptorConfig());
foragerConfig = ConfigUtils.inheritConfig(foragerConfig, inheritedConfig.getForagerConfig());
return this;
Expand All @@ -194,8 +195,8 @@ public void visitReferencedClasses(@NonNull Consumer<Class<?>> classVisitor) {
if (moveSelectorConfig != null) {
moveSelectorConfig.visitReferencedClasses(classVisitor);
}
if (moveProviderClass != null) {
classVisitor.accept(moveProviderClass);
if (neighborhoodProviderClass != null) {
classVisitor.accept(neighborhoodProviderClass);
}
if (acceptorConfig != null) {
acceptorConfig.visitReferencedClasses(classVisitor);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,31 +7,31 @@
* Any class, method, or field related to these features may change or be removed without prior notice,
* although we will strive to avoid this as much as possible.
* We encourage you to try these preview features and give us feedback on your experience with them.
* Please direct your feedback to our Github Discussions.
* Please direct your feedback to
* <a href="https://github.com/TimefoldAI/timefold-solver/discussions">Timefold Solver Github</a>
* or to <a href="https://discord.com/channels/1413420192213631086/1414521616955605003">Timefold Discord</a>.
*
* <p>
* This list is not constant and is evolving over time,
* with items being added and removed without warning.
* It should not be treated as part of our public API,
* just like the preview features themselves.
*
* @see <a href="https://github.com/TimefoldAI/timefold-solver/discussions">Timefold Solver Github Discussions</a>
*/
public enum PreviewFeature {

DIVERSIFIED_LATE_ACCEPTANCE,
PLANNING_SOLUTION_DIFF,
/**
* Unlike other preview features, Move Streams are an active research project.
* Unlike other preview features, Neighborhoods are an active research project.
* It is intended to simplify the creation of custom moves, eventually replacing move selectors.
* The component is under heavy development, entirely undocumented, and many key features are yet to be delivered.
* Neither the API nor the feature set are complete, and any part can change or be removed at any time.
*
* Move Streams will eventually stabilize and be promoted from a research project to a true preview feature.
* Neighborhoods will eventually stabilize and be promoted from a research project to a true preview feature.
* We only expose it now to be able to use it for experimentation and testing.
* As such, it is an exception to the rule;
* this preview feature is not finished, and it is not yet ready for feedback.
*/
MOVE_STREAMS
NEIGHBORHOODS

}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
import ai.timefold.solver.core.impl.localsearch.decider.LocalSearchDecider;
import ai.timefold.solver.core.impl.localsearch.decider.acceptor.Acceptor;
import ai.timefold.solver.core.impl.localsearch.decider.forager.LocalSearchForager;
import ai.timefold.solver.core.impl.move.MoveRepository;
import ai.timefold.solver.core.impl.neighborhood.MoveRepository;
import ai.timefold.solver.core.impl.partitionedsearch.PartitionedSearchPhase;
import ai.timefold.solver.core.impl.solver.termination.PhaseTermination;
import ai.timefold.solver.core.impl.solver.termination.SolverTermination;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import ai.timefold.solver.core.impl.constructionheuristic.placer.EntityPlacer;
import ai.timefold.solver.core.impl.constructionheuristic.scope.ConstructionHeuristicPhaseScope;
import ai.timefold.solver.core.impl.constructionheuristic.scope.ConstructionHeuristicStepScope;
import ai.timefold.solver.core.impl.move.PlacerBasedMoveRepository;
import ai.timefold.solver.core.impl.neighborhood.PlacerBasedMoveRepository;
import ai.timefold.solver.core.impl.phase.AbstractPossiblyInitializingPhase;
import ai.timefold.solver.core.impl.solver.scope.SolverScope;
import ai.timefold.solver.core.impl.solver.termination.PhaseTermination;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@
import ai.timefold.solver.core.impl.heuristic.selector.common.decorator.SelectionSorter;
import ai.timefold.solver.core.impl.heuristic.selector.common.decorator.WeightFactorySelectionSorter;
import ai.timefold.solver.core.impl.move.director.MoveDirector;
import ai.timefold.solver.core.impl.move.streams.maybeapi.UniDataFilter;
import ai.timefold.solver.core.impl.neighborhood.maybeapi.stream.enumerating.function.UniEnumeratingFilter;
import ai.timefold.solver.core.impl.util.CollectionUtils;
import ai.timefold.solver.core.impl.util.MutableInt;
import ai.timefold.solver.core.preview.api.domain.metamodel.PlanningEntityMetaModel;
Expand Down Expand Up @@ -133,7 +133,7 @@ public class EntityDescriptor<Solution_> {
private List<GenuineVariableDescriptor<Solution_>> effectiveGenuineVariableDescriptorList;
private List<ListVariableDescriptor<Solution_>> effectiveGenuineListVariableDescriptorList;

private final UniDataFilter<Solution_, Object> entityMovablePredicate =
private final UniEnumeratingFilter<Solution_, Object> entityMovablePredicate =
(solutionView, entity) -> {
var moveDirector = (MoveDirector<Solution_, ?>) solutionView;
return !moveDirector.isPinned(this, entity);
Expand Down Expand Up @@ -639,8 +639,8 @@ public BiPredicate<Solution_, Object> getEffectiveMovableEntityFilter() {
}

@SuppressWarnings("unchecked")
public <A> UniDataFilter<Solution_, A> getEntityMovablePredicate() {
return (UniDataFilter<Solution_, A>) entityMovablePredicate;
public <A> UniEnumeratingFilter<Solution_, A> getEntityMovablePredicate() {
return (UniEnumeratingFilter<Solution_, A>) entityMovablePredicate;
}

public SelectionSorter<Solution_, Object> getDecreasingDifficultySorter() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import java.util.Objects;
import java.util.stream.Collectors;

import ai.timefold.solver.core.preview.api.domain.metamodel.GenuineVariableMetaModel;
import ai.timefold.solver.core.preview.api.domain.metamodel.PlanningEntityMetaModel;
import ai.timefold.solver.core.preview.api.domain.solution.diff.PlanningEntityDiff;
import ai.timefold.solver.core.preview.api.domain.solution.diff.PlanningSolutionDiff;
Expand Down Expand Up @@ -75,7 +76,8 @@ private static String entityDiffToString(PlanningEntityDiff<?, ?> entityDiff) {
}
return variableDiffs.stream()
.map(diff -> " %s (%s): %s -> %s".formatted(diff.variableMetaModel().name(),
diff.variableMetaModel().isGenuine() ? "genuine" : "shadow", diff.oldValue(), diff.newValue()))
diff.variableMetaModel() instanceof GenuineVariableMetaModel<?, ?, ?> ? "genuine" : "shadow",
diff.oldValue(), diff.newValue()))
.collect(Collectors.joining(System.lineSeparator()));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import java.util.Set;
import java.util.stream.Collectors;

import ai.timefold.solver.core.preview.api.domain.metamodel.GenuineVariableMetaModel;
import ai.timefold.solver.core.preview.api.domain.metamodel.PlanningSolutionMetaModel;
import ai.timefold.solver.core.preview.api.domain.solution.diff.PlanningEntityDiff;
import ai.timefold.solver.core.preview.api.domain.solution.diff.PlanningSolutionDiff;
Expand Down Expand Up @@ -152,7 +153,8 @@ static String entityDiffToString(PlanningEntityDiff<?, ?> entityDiff) {
}
var variableDiffString = variableDiffs.stream()
.map(diff -> " %s (%s): %s -> %s".formatted(diff.variableMetaModel().name(),
diff.variableMetaModel().isGenuine() ? "genuine" : "shadow", diff.oldValue(), diff.newValue()))
diff.variableMetaModel() instanceof GenuineVariableMetaModel<?, ?, ?> ? "genuine" : "shadow",
diff.oldValue(), diff.newValue()))
.collect(Collectors.joining(System.lineSeparator()));
return """
%s:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.util.Objects;

import ai.timefold.solver.core.preview.api.domain.metamodel.GenuineVariableMetaModel;
import ai.timefold.solver.core.preview.api.domain.metamodel.VariableMetaModel;
import ai.timefold.solver.core.preview.api.domain.solution.diff.PlanningEntityDiff;
import ai.timefold.solver.core.preview.api.domain.solution.diff.PlanningVariableDiff;
Expand All @@ -27,7 +28,10 @@ public String toString() {
Old value: %s
New value: %s
"""
.formatted(variableMetaModel.isGenuine() ? "genuine" : "shadow", variableMetaModel.name(),
.formatted(
variableMetaModel instanceof GenuineVariableMetaModel<Solution_, Entity_, Value_> ? "genuine"
: "shadow",
variableMetaModel.name(),
entityDiff.entity(), entityDiff.entityMetaModel().type(), oldValue, newValue);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,8 @@
* which doesn't care whether the variable is internal or externalized.
*/
@NullMarked
public interface ListVariableStateSupply<Solution_, Entity_, Element_> extends
SourcedListVariableListener<Solution_, Entity_, Element_>,
SingletonInverseVariableSupply,
IndexVariableSupply {
public interface ListVariableStateSupply<Solution_, Entity_, Element_>
extends SourcedListVariableListener<Solution_, Entity_, Element_>, SingletonInverseVariableSupply, IndexVariableSupply {

void externalize(IndexShadowVariableDescriptor<Solution_> shadowVariableDescriptor);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import ai.timefold.solver.core.impl.domain.variable.ListVariableStateDemand;
import ai.timefold.solver.core.impl.domain.variable.inverserelation.InverseRelationShadowVariableDescriptor;
import ai.timefold.solver.core.impl.move.director.MoveDirector;
import ai.timefold.solver.core.impl.move.streams.maybeapi.BiDataFilter;
import ai.timefold.solver.core.impl.neighborhood.maybeapi.stream.enumerating.function.BiEnumeratingFilter;

public final class ListVariableDescriptor<Solution_> extends GenuineVariableDescriptor<Solution_> {

Expand All @@ -24,7 +24,7 @@ public final class ListVariableDescriptor<Solution_> extends GenuineVariableDesc
var list = getValue(entity);
return list.contains(element);
};
private final BiDataFilter<Solution_, Object, Object> entityContainsPinnedValuePredicate =
private final BiEnumeratingFilter<Solution_, Object, Object> entityContainsPinnedValuePredicate =
(solutionView, value, entity) -> {
var moveDirector = (MoveDirector<Solution_, ?>) solutionView;
return moveDirector.isPinned(this, value);
Expand All @@ -47,8 +47,8 @@ public <A> BiPredicate<A, Object> getInListPredicate() {
}

@SuppressWarnings("unchecked")
public <A, B> BiDataFilter<Solution_, A, B> getEntityContainsPinnedValuePredicate() {
return (BiDataFilter<Solution_, A, B>) entityContainsPinnedValuePredicate;
public <A, B> BiEnumeratingFilter<Solution_, A, B> getEntityContainsPinnedValuePredicate() {
return (BiEnumeratingFilter<Solution_, A, B>) entityContainsPinnedValuePredicate;
}

public boolean allowsUnassignedValues() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
import ai.timefold.solver.core.impl.exhaustivesearch.scope.ExhaustiveSearchPhaseScope;
import ai.timefold.solver.core.impl.exhaustivesearch.scope.ExhaustiveSearchStepScope;
import ai.timefold.solver.core.impl.heuristic.selector.entity.EntitySelector;
import ai.timefold.solver.core.impl.move.streams.maybeapi.generic.Moves;
import ai.timefold.solver.core.impl.neighborhood.maybeapi.move.Moves;
import ai.timefold.solver.core.impl.phase.AbstractPhase;
import ai.timefold.solver.core.impl.solver.scope.SolverScope;
import ai.timefold.solver.core.impl.solver.termination.PhaseTermination;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
import ai.timefold.solver.core.impl.heuristic.selector.entity.mimic.ManualEntityMimicRecorder;
import ai.timefold.solver.core.impl.heuristic.selector.move.MoveSelector;
import ai.timefold.solver.core.impl.heuristic.selector.move.MoveSelectorFactory;
import ai.timefold.solver.core.impl.move.MoveSelectorBasedMoveRepository;
import ai.timefold.solver.core.impl.neighborhood.MoveSelectorBasedMoveRepository;
import ai.timefold.solver.core.impl.phase.AbstractPhaseFactory;
import ai.timefold.solver.core.impl.solver.recaller.BestSolutionRecaller;
import ai.timefold.solver.core.impl.solver.termination.PhaseTermination;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import ai.timefold.solver.core.impl.exhaustivesearch.scope.ExhaustiveSearchPhaseScope;
import ai.timefold.solver.core.impl.exhaustivesearch.scope.ExhaustiveSearchStepScope;
import ai.timefold.solver.core.impl.heuristic.selector.entity.mimic.ManualEntityMimicRecorder;
import ai.timefold.solver.core.impl.move.MoveRepository;
import ai.timefold.solver.core.impl.neighborhood.MoveRepository;
import ai.timefold.solver.core.impl.phase.scope.SolverLifecyclePoint;
import ai.timefold.solver.core.impl.score.director.InnerScore;
import ai.timefold.solver.core.impl.solver.recaller.BestSolutionRecaller;
Expand Down
Loading
Loading