Skip to content

Commit 16e2464

Browse files
triceoCopilot
andauthored
chore: move streams change moves for list variable (#1727)
Introduces `ListChangeMoveProvider` that is capable of generating change moves, assign moves and unassign moves. To do that, several new capabilities are introduced to data streams, which mimic capabilities of constraint streams and use the same underlying Bavet nodes. With the new value range work, nodes and data sessions no longer need initialization, because everything we need, we can now find in the value range manager. --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 790a10d commit 16e2464

File tree

79 files changed

+1961
-689
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

79 files changed

+1961
-689
lines changed

core/src/main/java/ai/timefold/solver/core/impl/bavet/AbstractSession.java

Lines changed: 1 addition & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -2,36 +2,24 @@
22

33
import java.util.IdentityHashMap;
44
import java.util.Map;
5-
import java.util.stream.Stream;
65

7-
import ai.timefold.solver.core.api.domain.solution.PlanningSolution;
86
import ai.timefold.solver.core.impl.bavet.uni.AbstractForEachUniNode;
97
import ai.timefold.solver.core.impl.bavet.uni.AbstractForEachUniNode.LifecycleOperation;
10-
import ai.timefold.solver.core.impl.score.director.SessionContext;
118

12-
public abstract class AbstractSession implements AutoCloseable {
9+
public abstract class AbstractSession {
1310

1411
private final NodeNetwork nodeNetwork;
15-
private final Map<Class<?>, AbstractForEachUniNode.InitializableForEachNode<Object>[]> initializeEffectiveClassToNodeArrayMap;
1612
private final Map<Class<?>, AbstractForEachUniNode<Object>[]> insertEffectiveClassToNodeArrayMap;
1713
private final Map<Class<?>, AbstractForEachUniNode<Object>[]> updateEffectiveClassToNodeArrayMap;
1814
private final Map<Class<?>, AbstractForEachUniNode<Object>[]> retractEffectiveClassToNodeArrayMap;
1915

2016
protected AbstractSession(NodeNetwork nodeNetwork) {
2117
this.nodeNetwork = nodeNetwork;
22-
this.initializeEffectiveClassToNodeArrayMap = new IdentityHashMap<>(nodeNetwork.forEachNodeCount());
2318
this.insertEffectiveClassToNodeArrayMap = new IdentityHashMap<>(nodeNetwork.forEachNodeCount());
2419
this.updateEffectiveClassToNodeArrayMap = new IdentityHashMap<>(nodeNetwork.forEachNodeCount());
2520
this.retractEffectiveClassToNodeArrayMap = new IdentityHashMap<>(nodeNetwork.forEachNodeCount());
2621
}
2722

28-
@SuppressWarnings({ "unchecked", "rawtypes" })
29-
public final void initialize(SessionContext context) {
30-
for (var node : findInitializableNodes()) {
31-
node.initialize(context);
32-
}
33-
}
34-
3523
public final void insert(Object fact) {
3624
var factClass = fact.getClass();
3725
for (var node : findNodes(factClass, LifecycleOperation.INSERT)) {
@@ -57,29 +45,6 @@ private AbstractForEachUniNode<Object>[] findNodes(Class<?> factClass, Lifecycle
5745
return nodeArray;
5846
}
5947

60-
@SuppressWarnings("unchecked")
61-
private AbstractForEachUniNode.InitializableForEachNode<Object>[] findInitializableNodes() {
62-
// There will only be one solution class in the problem.
63-
// Therefore we do not need to know what it is, and using the annotation class will serve as a unique key.
64-
var factClass = PlanningSolution.class;
65-
var effectiveClassToNodeArrayMap = initializeEffectiveClassToNodeArrayMap;
66-
// Map.computeIfAbsent() would have created lambdas on the hot path, this will not.
67-
var nodeArray = effectiveClassToNodeArrayMap.get(factClass);
68-
if (nodeArray == null) {
69-
nodeArray = nodeNetwork.getForEachNodes(factClass)
70-
.flatMap(node -> {
71-
if (node instanceof AbstractForEachUniNode.InitializableForEachNode<?> initializableForEachNode) {
72-
return Stream.of(initializableForEachNode);
73-
} else {
74-
return Stream.empty();
75-
}
76-
})
77-
.toArray(AbstractForEachUniNode.InitializableForEachNode[]::new);
78-
effectiveClassToNodeArrayMap.put(factClass, nodeArray);
79-
}
80-
return nodeArray;
81-
}
82-
8348
public final void update(Object fact) {
8449
var factClass = fact.getClass();
8550
for (var node : findNodes(factClass, LifecycleOperation.UPDATE)) {
@@ -98,13 +63,4 @@ public void settle() {
9863
nodeNetwork.settle();
9964
}
10065

101-
@Override
102-
public final void close() {
103-
for (var node : findInitializableNodes()) {
104-
// Initializable nodes get a supply manager, fair to assume they will be demanding supplies.
105-
// Give them the opportunity to cancel those demands.
106-
node.close();
107-
}
108-
}
109-
11066
}

core/src/main/java/ai/timefold/solver/core/impl/bavet/uni/AbstractForEachUniNode.java

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
import ai.timefold.solver.core.impl.bavet.common.tuple.TupleLifecycle;
1010
import ai.timefold.solver.core.impl.bavet.common.tuple.TupleState;
1111
import ai.timefold.solver.core.impl.bavet.common.tuple.UniTuple;
12-
import ai.timefold.solver.core.impl.score.director.SessionContext;
1312

1413
import org.jspecify.annotations.NullMarked;
1514
import org.jspecify.annotations.Nullable;
@@ -136,13 +135,4 @@ public enum LifecycleOperation {
136135
RETRACT
137136
}
138137

139-
public interface InitializableForEachNode<Solution_> extends AutoCloseable {
140-
141-
void initialize(SessionContext<Solution_> context);
142-
143-
@Override
144-
void close(); // Drop the checked exception.
145-
146-
}
147-
148138
}

core/src/main/java/ai/timefold/solver/core/impl/bavet/uni/ForEachFromSolutionUniNode.java

Lines changed: 0 additions & 83 deletions
This file was deleted.

core/src/main/java/ai/timefold/solver/core/impl/bavet/uni/ForEachIncludingUnassignedUniNode.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,8 @@
77
import org.jspecify.annotations.Nullable;
88

99
@NullMarked
10-
public sealed class ForEachIncludingUnassignedUniNode<A>
11-
extends AbstractForEachUniNode<A>
12-
permits ForEachFromSolutionUniNode {
10+
public final class ForEachIncludingUnassignedUniNode<A>
11+
extends AbstractForEachUniNode<A> {
1312

1413
public ForEachIncludingUnassignedUniNode(Class<A> forEachClass, TupleLifecycle<UniTuple<A>> nextNodesTupleLifecycle,
1514
int outputStoreSize) {

core/src/main/java/ai/timefold/solver/core/impl/domain/solution/descriptor/DefaultPlanningListVariableMetaModel.java

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,9 @@ public String name() {
2727
return variableDescriptor.getVariableName();
2828
}
2929

30-
@Override
31-
public boolean hasValueRangeOnEntity() {
32-
return !variableDescriptor.canExtractValueRangeFromSolution();
33-
}
34-
3530
@Override
3631
public boolean allowsUnassignedValues() {
37-
return false;
32+
return variableDescriptor.allowsUnassignedValues();
3833
}
3934

4035
@Override

core/src/main/java/ai/timefold/solver/core/impl/domain/solution/descriptor/DefaultPlanningVariableMetaModel.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,6 @@ public String name() {
2727
return variableDescriptor.getVariableName();
2828
}
2929

30-
@Override
31-
public boolean hasValueRangeOnEntity() {
32-
return !variableDescriptor.canExtractValueRangeFromSolution();
33-
}
34-
3530
@Override
3631
public boolean allowsUnassigned() {
3732
return variableDescriptor.allowsUnassigned();

core/src/main/java/ai/timefold/solver/core/impl/localsearch/DefaultLocalSearchPhaseFactory.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ Convert your entities (%s) to use @%s instead."""
134134
.formatted(moveProvidersClass, moveProviderList.size()));
135135
}
136136
var moveProvider = moveProviderList.get(0);
137-
var moveStreamFactory = new DefaultMoveStreamFactory<>(solutionDescriptor);
137+
var moveStreamFactory = new DefaultMoveStreamFactory<>(solutionDescriptor, configPolicy.getEnvironmentMode());
138138
var moveProducer = moveProvider.apply(moveStreamFactory);
139139
var moveRepository = new MoveStreamsBasedMoveRepository<>(moveStreamFactory, moveProducer,
140140
pickSelectionOrder() == SelectionOrder.RANDOM);

core/src/main/java/ai/timefold/solver/core/impl/move/MoveStreamsBasedMoveRepository.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,6 @@ public void stepEnded(AbstractStepScope<Solution_> stepScope) {
8989
@Override
9090
public void phaseEnded(AbstractPhaseScope<Solution_> phaseScope) {
9191
if (moveStreamSession != null) {
92-
moveStreamSession.close();
9392
moveStreamSession = null;
9493
}
9594
phaseScope.getScoreDirector().setMoveRepository(null);

core/src/main/java/ai/timefold/solver/core/impl/move/director/MoveDirector.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,12 @@ public final <Entity_, Value_> Value_ getValue(PlanningVariableMetaModel<Solutio
185185
return extractVariableDescriptor(variableMetaModel).getValue(entity);
186186
}
187187

188+
@Override
189+
public <Entity_, Value_> int countValues(PlanningListVariableMetaModel<Solution_, Entity_, Value_> variableMetaModel,
190+
Entity_ entity) {
191+
return extractVariableDescriptor(variableMetaModel).getValue(entity).size();
192+
}
193+
188194
@SuppressWarnings("unchecked")
189195
@Override
190196
public final <Entity_, Value_> Value_ getValueAtIndex(

core/src/main/java/ai/timefold/solver/core/impl/move/streams/DefaultBiFromBiMoveStream.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import java.util.Objects;
44

5-
import ai.timefold.solver.core.impl.move.streams.dataset.BiDataset;
5+
import ai.timefold.solver.core.impl.move.streams.dataset.bi.BiDataset;
66
import ai.timefold.solver.core.impl.move.streams.maybeapi.stream.BiMoveConstructor;
77
import ai.timefold.solver.core.impl.move.streams.maybeapi.stream.BiMoveStream;
88
import ai.timefold.solver.core.impl.move.streams.maybeapi.stream.MoveProducer;

0 commit comments

Comments
 (0)