Skip to content

Commit 7614e17

Browse files
committed
update the nodes to the new API
1 parent a656894 commit 7614e17

8 files changed

Lines changed: 140 additions & 116 deletions

File tree

core/src/main/java/ai/timefold/solver/core/api/score/stream/bi/BiConstraintCollectorAccumulator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,6 @@ public interface BiConstraintCollectorAccumulator<ResultContainer_, A, B> {
1616
* As defined by {@link UniConstraintCollectorAccumulator#intoGroup(Object)},
1717
* only for {@link BiConstraintCollector}.
1818
*/
19-
BiConstraintCollectorAccumulatedValue<A, B> startGroup(ResultContainer_ resultContainer);
19+
BiConstraintCollectorAccumulatedValue<A, B> intoGroup(ResultContainer_ resultContainer);
2020

2121
}

core/src/main/java/ai/timefold/solver/core/api/score/stream/quad/QuadConstraintCollectorAccumulator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,6 @@ public interface QuadConstraintCollectorAccumulator<ResultContainer_, A, B, C, D
1616
* As defined by {@link UniConstraintCollectorAccumulator#intoGroup(Object)},
1717
* only for {@link QuadConstraintCollector}.
1818
*/
19-
QuadConstraintCollectorAccumulatedValue<A, B, C, D> startGroup(ResultContainer_ resultContainer);
19+
QuadConstraintCollectorAccumulatedValue<A, B, C, D> intoGroup(ResultContainer_ resultContainer);
2020

2121
}

core/src/main/java/ai/timefold/solver/core/api/score/stream/tri/TriConstraintCollectorAccumulator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,6 @@ public interface TriConstraintCollectorAccumulator<ResultContainer_, A, B, C> {
1616
* As defined by {@link UniConstraintCollectorAccumulator#intoGroup(Object)},
1717
* only for {@link TriConstraintCollector}.
1818
*/
19-
TriConstraintCollectorAccumulatedValue<A, B, C> startGroup(ResultContainer_ resultContainer);
19+
TriConstraintCollectorAccumulatedValue<A, B, C> intoGroup(ResultContainer_ resultContainer);
2020

2121
}

core/src/main/java/ai/timefold/solver/core/impl/bavet/bi/AbstractGroupBiNode.java

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,12 @@
1717
abstract class AbstractGroupBiNode<OldA, OldB, OutTuple_ extends Tuple, GroupKey_, ResultContainer_, Result_>
1818
extends AbstractGroupNode<BiTuple<OldA, OldB>, OutTuple_, GroupKey_, ResultContainer_, Result_> {
1919

20-
private final int undoStoreIndex;
20+
private final int groupAccumulatorIndex;
2121
private final @Nullable TriFunction<ResultContainer_, OldA, OldB, Runnable> accumulator;
2222
private final @Nullable BiConstraintCollectorAccumulator<ResultContainer_, OldA, OldB> incrementalAccumulator;
2323
private final boolean useIncrementalAccumulator;
2424

25-
protected AbstractGroupBiNode(int groupStoreIndex, int undoStoreIndex,
25+
protected AbstractGroupBiNode(int groupStoreIndex, int groupAccumulatorIndex,
2626
Function<BiTuple<OldA, OldB>, GroupKey_> groupKeyFunction,
2727
BiConstraintCollector<OldA, OldB, ResultContainer_, Result_> collector,
2828
TupleLifecycle<OutTuple_> nextNodesTupleLifecycle, EnvironmentMode environmentMode) {
@@ -31,7 +31,7 @@ protected AbstractGroupBiNode(int groupStoreIndex, int undoStoreIndex,
3131
collector == null ? null : collector.finisher(),
3232
nextNodesTupleLifecycle, environmentMode);
3333
var hasCollector = collector != null;
34-
this.undoStoreIndex = hasCollector ? undoStoreIndex : -1;
34+
this.groupAccumulatorIndex = hasCollector ? groupAccumulatorIndex : -1;
3535
accumulator = hasCollector ? (collector.isIncremental() ? null : collector.accumulator()) : null;
3636
incrementalAccumulator = hasCollector ? (collector.isIncremental() ? collector.incrementalAccumulator() : null) : null;
3737
useIncrementalAccumulator = hasCollector && incrementalAccumulator != null;
@@ -40,42 +40,43 @@ protected AbstractGroupBiNode(int groupStoreIndex, int undoStoreIndex,
4040
protected AbstractGroupBiNode(int groupStoreIndex, Function<BiTuple<OldA, OldB>, GroupKey_> groupKeyFunction,
4141
TupleLifecycle<OutTuple_> nextNodesTupleLifecycle, EnvironmentMode environmentMode) {
4242
super(groupStoreIndex, groupKeyFunction, nextNodesTupleLifecycle, environmentMode);
43-
undoStoreIndex = -1;
43+
groupAccumulatorIndex = -1;
4444
accumulator = null;
4545
incrementalAccumulator = null;
4646
useIncrementalAccumulator = false;
4747
}
4848

4949
@Override
50-
protected void groupInsert(ResultContainer_ resultContainer, BiTuple<OldA, OldB> tuple) {
50+
protected boolean groupInsert(ResultContainer_ resultContainer, BiTuple<OldA, OldB> tuple) {
5151
if (useIncrementalAccumulator) {
52-
var undoAccumulator = incrementalAccumulator.startGroup(resultContainer);
53-
undoAccumulator.add(tuple.getA(), tuple.getB());
54-
tuple.setStore(undoStoreIndex, undoAccumulator);
52+
var groupContents = incrementalAccumulator.intoGroup(resultContainer);
53+
tuple.setStore(groupAccumulatorIndex, groupContents);
54+
return groupContents.add(tuple.getA(), tuple.getB());
5555
} else {
56-
var undoAccumulator = accumulator.apply(resultContainer, tuple.getA(), tuple.getB());
57-
tuple.setStore(undoStoreIndex, undoAccumulator);
56+
tuple.setStore(groupAccumulatorIndex, accumulator.apply(resultContainer, tuple.getA(), tuple.getB()));
57+
return true;
5858
}
5959
}
6060

6161
@Override
6262
protected boolean groupUpdate(ResultContainer_ resultContainer, BiTuple<OldA, OldB> tuple) {
6363
if (useIncrementalAccumulator) {
64-
BiConstraintCollectorAccumulatedValue<OldA, OldB> undoAccumulator = tuple.getStore(undoStoreIndex);
65-
return undoAccumulator.update(tuple.getA(), tuple.getB());
64+
BiConstraintCollectorAccumulatedValue<OldA, OldB> groupContents = tuple.getStore(groupAccumulatorIndex);
65+
return groupContents.update(tuple.getA(), tuple.getB());
6666
} else {
6767
return super.groupUpdate(resultContainer, tuple);
6868
}
6969
}
7070

7171
@Override
72-
protected void groupRetract(ResultContainer_ resultContainer, BiTuple<OldA, OldB> tuple) {
72+
protected boolean groupRetract(BiTuple<OldA, OldB> tuple) {
7373
if (useIncrementalAccumulator) {
74-
BiConstraintCollectorAccumulatedValue<OldA, OldB> undoAccumulator = tuple.removeStore(undoStoreIndex);
75-
undoAccumulator.remove();
74+
BiConstraintCollectorAccumulatedValue<OldA, OldB> groupContents = tuple.removeStore(groupAccumulatorIndex);
75+
return groupContents.remove();
7676
} else {
77-
Runnable undoAccumulator = tuple.removeStore(undoStoreIndex);
78-
undoAccumulator.run();
77+
Runnable undo = tuple.removeStore(groupAccumulatorIndex);
78+
undo.run();
79+
return true;
7980
}
8081
}
8182
}

core/src/main/java/ai/timefold/solver/core/impl/bavet/common/AbstractGroupNode.java

Lines changed: 62 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,9 @@ public abstract class AbstractGroupNode<InTuple_ extends Tuple, OutTuple_ extend
5353
private final boolean useAssertingGroupKey;
5454

5555
protected AbstractGroupNode(int groupStoreIndex, Function<InTuple_, GroupKey_> groupKeyFunction,
56-
Supplier<ResultContainer_> supplier,
57-
Function<ResultContainer_, Result_> finisher,
58-
TupleLifecycle<OutTuple_> nextNodesTupleLifecycle, EnvironmentMode environmentMode) {
56+
Supplier<ResultContainer_> supplier,
57+
Function<ResultContainer_, Result_> finisher,
58+
TupleLifecycle<OutTuple_> nextNodesTupleLifecycle, EnvironmentMode environmentMode) {
5959
this.groupStoreIndex = groupStoreIndex;
6060
this.groupKeyFunction = groupKeyFunction;
6161
this.supplier = supplier;
@@ -80,8 +80,8 @@ protected AbstractGroupNode(int groupStoreIndex, Function<InTuple_, GroupKey_> g
8080
}
8181

8282
protected AbstractGroupNode(int groupStoreIndex,
83-
Function<InTuple_, GroupKey_> groupKeyFunction, TupleLifecycle<OutTuple_> nextNodesTupleLifecycle,
84-
EnvironmentMode environmentMode) {
83+
Function<InTuple_, GroupKey_> groupKeyFunction, TupleLifecycle<OutTuple_> nextNodesTupleLifecycle,
84+
EnvironmentMode environmentMode) {
8585
this(groupStoreIndex,
8686
groupKeyFunction, null, null, nextNodesTupleLifecycle,
8787
environmentMode);
@@ -95,33 +95,35 @@ public StreamKind getStreamKind() {
9595
@Override
9696
public final void insert(InTuple_ tuple) {
9797
if (tuple.getStore(groupStoreIndex) != null) {
98-
throw new IllegalStateException("Impossible state: the input for the tuple (" + tuple
99-
+ ") was already added in the tupleStore.");
98+
throw new IllegalStateException(
99+
"Impossible state: the input for the tuple (%s) was already added in the tupleStore."
100+
.formatted(tuple));
100101
}
101102
var userSuppliedKey = hasMultipleGroups ? groupKeyFunction.apply(tuple) : null;
102103
createTuple(tuple, userSuppliedKey);
103104
}
104105

105106
private void createTuple(InTuple_ tuple, GroupKey_ userSuppliedKey) {
106-
var newGroup = getOrCreateGroup(userSuppliedKey);
107-
var outTuple = accumulate(tuple, newGroup);
107+
var group = getOrCreateGroup(userSuppliedKey);
108+
var needsPropagation = group.parentCount == 1; // Fresh outTuple.
109+
if (hasCollector) {
110+
needsPropagation = groupInsert(group.getResultContainer(), tuple) || needsPropagation;
111+
}
112+
tuple.setStore(groupStoreIndex, group);
113+
if (!needsPropagation) {
114+
return;
115+
}
116+
var outTuple = group.getTuple();
108117
switch (outTuple.getState()) {
109118
case CREATING, UPDATING -> {
110119
// Already in the correct state.
111120
}
112-
case OK, DYING -> propagationQueue.update(newGroup);
113-
case ABORTING -> propagationQueue.insert(newGroup);
114-
default -> throw new IllegalStateException("Impossible state: The group (" + newGroup + ") in node (" + this
115-
+ ") is in an unexpected state (" + outTuple.getState() + ").");
116-
}
117-
}
118-
119-
private OutTuple_ accumulate(InTuple_ tuple, Group<OutTuple_, ResultContainer_> group) {
120-
if (hasCollector) {
121-
groupInsert(group.getResultContainer(), tuple);
121+
case OK, DYING -> propagationQueue.update(group);
122+
case ABORTING -> propagationQueue.insert(group);
123+
default -> throw new IllegalStateException(
124+
"Impossible state: The group (%s) in node (%s) is in an unexpected state (%s)."
125+
.formatted(group, this, outTuple.getState()));
122126
}
123-
tuple.setStore(groupStoreIndex, group);
124-
return group.getTuple();
125127
}
126128

127129
private Group<OutTuple_, ResultContainer_> getOrCreateGroup(GroupKey_ userSuppliedKey) {
@@ -150,16 +152,17 @@ private Group<OutTuple_, ResultContainer_> createGroupWithGroupKey(Object groupM
150152
var userSuppliedKey = extractUserSuppliedKey(groupMapKey);
151153
var outTuple = createOutTuple(userSuppliedKey);
152154
var group = hasCollector ? Group.create(groupMapKey, supplier.get(), outTuple)
153-
: Group.<OutTuple_, ResultContainer_> createWithoutAccumulate(groupMapKey, outTuple);
155+
: Group.<OutTuple_, ResultContainer_>createWithoutAccumulate(groupMapKey, outTuple);
154156
propagationQueue.insert(group);
155157
return group;
156158
}
157159

158160
private Group<OutTuple_, ResultContainer_> createGroupWithoutGroupKey() {
159161
var outTuple = createOutTuple(null);
160162
if (!hasCollector) {
161-
throw new IllegalStateException("Impossible state: The node (" + this + ") has no collector, "
162-
+ "but it is still trying to create a group without a group key.");
163+
throw new IllegalStateException(
164+
"Impossible state: The node (%s) has no collector, but it is still trying to create a group without a group key."
165+
.formatted(this));
163166
}
164167
var group = Group.createWithoutGroupKey(supplier.get(), outTuple);
165168
propagationQueue.insert(group);
@@ -188,10 +191,12 @@ public final void update(InTuple_ tuple) {
188191
if (Objects.equals(oldUserSuppliedGroupKey, newUserSuppliedGroupKey)) {
189192
updateGroup(tuple, oldGroup);
190193
} else {
194+
var needsPropagation = false;
191195
if (hasCollector) {
192-
groupRetract(oldGroup.getResultContainer(), tuple);
196+
needsPropagation = groupRetract(tuple);
193197
}
194-
killTuple(oldGroup);
198+
var newParentCount = --oldGroup.parentCount;
199+
killOutTuple(oldGroup, newParentCount == 0, needsPropagation);
195200
createTuple(tuple, newUserSuppliedGroupKey);
196201
}
197202
}
@@ -204,28 +209,38 @@ private void updateGroup(InTuple_ tuple, Group<OutTuple_, ResultContainer_> oldG
204209
return;
205210
}
206211
}
212+
// TODO if it has no collectors, maybe skip the propagation?
207213
var outTuple = oldGroup.getTuple();
208214
switch (outTuple.getState()) {
209215
case CREATING, UPDATING -> {
210216
// Already in the correct state.
211217
}
212218
case OK -> propagationQueue.update(oldGroup);
213-
default ->
214-
throw new IllegalStateException("Impossible state: The group (%s) in node (%s) is in an unexpected state (%s)."
215-
.formatted(oldGroup, this, outTuple.getState()));
219+
default -> throw new IllegalStateException(
220+
"Impossible state: The group (%s) in node (%s) is in an unexpected state (%s)."
221+
.formatted(oldGroup, this, outTuple.getState()));
216222
}
217223
}
218224

219-
private void killTuple(Group<OutTuple_, ResultContainer_> group) {
220-
var newParentCount = --group.parentCount;
221-
var killGroup = (newParentCount == 0);
225+
/**
226+
*
227+
* @param group the group which created the outTuple
228+
* @param killGroup true if the group should be removed from downstream nodes
229+
* @param propagateUpdate may be true while killGroup is also true;
230+
* propagating updates is irrelevant if the group is already being killed
231+
*/
232+
private void killOutTuple(Group<OutTuple_, ResultContainer_> group, boolean killGroup, boolean propagateUpdate) {
233+
if (!killGroup && !propagateUpdate) { // Nothing has changed.
234+
return;
235+
}
222236
if (killGroup) {
223237
var groupKey = hasMultipleGroups ? group.getGroupKey() : null;
224238
var oldGroup = removeGroup(groupKey);
225239
if (oldGroup == null) {
226-
throw new IllegalStateException("Impossible state: the group for the groupKey ("
227-
+ groupKey + ") doesn't exist in the groupMap.\n" +
228-
"Maybe groupKey hashcode changed while it shouldn't have?");
240+
throw new IllegalStateException("""
241+
Impossible state: the group for the groupKey (%s) doesn't exist in the groupMap.
242+
Maybe groupKey hashcode changed while it shouldn't have?"""
243+
.formatted(groupKey));
229244
}
230245
}
231246
var outTuple = group.getTuple();
@@ -247,8 +262,9 @@ private void killTuple(Group<OutTuple_, ResultContainer_> group) {
247262
propagationQueue.update(group);
248263
}
249264
}
250-
default -> throw new IllegalStateException("Impossible state: The group (" + group + ") in node (" + this
251-
+ ") is in an unexpected state (" + outTuple.getState() + ").");
265+
default -> throw new IllegalStateException(
266+
"Impossible state: The group (%s) in node (%s) is in an unexpected state (%s)."
267+
.formatted(group, this, outTuple.getState()));
252268
}
253269
}
254270

@@ -269,21 +285,23 @@ public final void retract(InTuple_ tuple) {
269285
// No fail fast if null because we don't track which tuples made it through the filter predicate(s)
270286
return;
271287
}
288+
var needsPropagation = false;
272289
if (hasCollector) {
273-
groupRetract(group.getResultContainer(), tuple);
290+
needsPropagation = groupRetract(tuple);
274291
}
275-
killTuple(group);
292+
var newParentCount = --group.parentCount;
293+
killOutTuple(group, newParentCount == 0, needsPropagation);
276294
}
277295

278-
protected abstract void groupInsert(ResultContainer_ resultContainer, InTuple_ tuple);
296+
protected abstract boolean groupInsert(ResultContainer_ resultContainer, InTuple_ tuple);
279297

280298
protected boolean groupUpdate(ResultContainer_ resultContainer, InTuple_ tuple) {
281-
groupRetract(resultContainer, tuple);
282-
groupInsert(resultContainer, tuple);
283-
return true;
299+
var retractNeedsPropagation = groupRetract(tuple);
300+
var insertNeedsPropagation = groupInsert(resultContainer, tuple);
301+
return retractNeedsPropagation || insertNeedsPropagation;
284302
}
285303

286-
protected abstract void groupRetract(ResultContainer_ resultContainer, InTuple_ tuple);
304+
protected abstract boolean groupRetract(InTuple_ tuple);
287305

288306
@Override
289307
public Propagator getPropagator() {

0 commit comments

Comments
 (0)