Skip to content

Commit 9ff7122

Browse files
triceoclaude
andcommitted
refactor: remove isIncremental/incrementalAccumulator switch from *ConstraintCollector
*ConstraintCollectorAccumulator now extends the corresponding function type (BiFunction/TriFunction/QuadFunction/PentaFunction) and throws on apply(). Bavet nodes detect incremental via instanceof in toIncremental() instead of isIncremental(). fromIncremental() deleted; all built-in impls return the accumulator type directly as a covariant override of accumulator(). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent ed165d7 commit 9ff7122

65 files changed

Lines changed: 227 additions & 800 deletions

File tree

Some content is hidden

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

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

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -33,23 +33,11 @@ public interface BiConstraintCollector<A, B, ResultContainer_, Result_> {
3333

3434
/**
3535
* As defined by {@link UniConstraintCollector#accumulator()}, but for {@link BiConstraintStream}.
36+
*
37+
* @see BiConstraintCollectorAccumulator An incremental API to be returned instead of the deprecated plain tri-function.
3638
*/
3739
TriFunction<ResultContainer_, A, B, Runnable> accumulator();
3840

39-
/**
40-
* As defined by {@link UniConstraintCollector#incrementalAccumulator()}, but for {@link BiConstraintStream}.
41-
*/
42-
default BiConstraintCollectorAccumulator<ResultContainer_, A, B> incrementalAccumulator() {
43-
throw new UnsupportedOperationException();
44-
}
45-
46-
/**
47-
* As defined by {@link UniConstraintCollector#isIncremental()}, but for {@link BiConstraintStream}.
48-
*/
49-
default boolean isIncremental() {
50-
return false;
51-
}
52-
5341
/**
5442
* As defined by {@link UniConstraintCollector#finisher()}, but for {@link BiConstraintStream}.
5543
*/
Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,36 @@
11
package ai.timefold.solver.core.api.score.stream.bi;
22

3+
import ai.timefold.solver.core.api.function.TriFunction;
34
import ai.timefold.solver.core.api.score.stream.uni.UniConstraintCollectorAccumulator;
45

56
import org.jspecify.annotations.NullMarked;
67

78
/**
89
* As defined by {@link UniConstraintCollectorAccumulator},
910
* only for {@link BiConstraintCollector}.
11+
* <p>
12+
* This interface extends {@link TriFunction} for type compatibility, but {@link #apply(Object, Object, Object)}
13+
* always throws {@link UnsupportedOperationException}. Use {@link #intoGroup(Object)} instead.
1014
*/
1115
@NullMarked
1216
@FunctionalInterface
13-
public interface BiConstraintCollectorAccumulator<ResultContainer_, A, B> {
17+
public interface BiConstraintCollectorAccumulator<ResultContainer_, A, B>
18+
extends TriFunction<ResultContainer_, A, B, Runnable> {
1419

1520
/**
1621
* As defined by {@link UniConstraintCollectorAccumulator#intoGroup(Object)},
1722
* only for {@link BiConstraintCollector}.
1823
*/
1924
BiConstraintCollectorValueHandle<A, B> intoGroup(ResultContainer_ resultContainer);
2025

26+
/**
27+
* @deprecated Use {@link #intoGroup(Object)} instead.
28+
* @throws UnsupportedOperationException always
29+
*/
30+
@Deprecated(since = "2.2.0", forRemoval = true)
31+
@Override
32+
default Runnable apply(ResultContainer_ resultContainer, A a, B b) {
33+
throw new UnsupportedOperationException("Use intoGroup() instead.");
34+
}
35+
2136
}

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

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -35,23 +35,11 @@ public interface QuadConstraintCollector<A, B, C, D, ResultContainer_, Result_>
3535

3636
/**
3737
* As defined by {@link UniConstraintCollector#accumulator()}, but for {@link QuadConstraintStream}.
38+
*
39+
* @see QuadConstraintCollectorAccumulator An incremental API to be returned instead of the deprecated plain penta-function.
3840
*/
3941
PentaFunction<ResultContainer_, A, B, C, D, Runnable> accumulator();
4042

41-
/**
42-
* As defined by {@link UniConstraintCollector#incrementalAccumulator()}, but for {@link QuadConstraintStream}.
43-
*/
44-
default QuadConstraintCollectorAccumulator<ResultContainer_, A, B, C, D> incrementalAccumulator() {
45-
throw new UnsupportedOperationException();
46-
}
47-
48-
/**
49-
* As defined by {@link UniConstraintCollector#isIncremental()}, but for {@link QuadConstraintStream}.
50-
*/
51-
default boolean isIncremental() {
52-
return false;
53-
}
54-
5543
/**
5644
* As defined by {@link UniConstraintCollector#finisher()}, but for {@link QuadConstraintStream}.
5745
*/
Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,36 @@
11
package ai.timefold.solver.core.api.score.stream.quad;
22

3+
import ai.timefold.solver.core.api.function.PentaFunction;
34
import ai.timefold.solver.core.api.score.stream.uni.UniConstraintCollectorAccumulator;
45

56
import org.jspecify.annotations.NullMarked;
67

78
/**
89
* As defined by {@link UniConstraintCollectorAccumulator},
910
* only for {@link QuadConstraintCollector}.
11+
* <p>
12+
* Extends {@link PentaFunction} for detection purposes; {@link #apply} always throws.
13+
* Use {@link #intoGroup} instead.
1014
*/
1115
@NullMarked
1216
@FunctionalInterface
13-
public interface QuadConstraintCollectorAccumulator<ResultContainer_, A, B, C, D> {
17+
public interface QuadConstraintCollectorAccumulator<ResultContainer_, A, B, C, D>
18+
extends PentaFunction<ResultContainer_, A, B, C, D, Runnable> {
1419

1520
/**
1621
* As defined by {@link UniConstraintCollectorAccumulator#intoGroup(Object)},
1722
* only for {@link QuadConstraintCollector}.
1823
*/
1924
QuadConstraintCollectorValueHandle<A, B, C, D> intoGroup(ResultContainer_ resultContainer);
2025

26+
/**
27+
* @deprecated Use {@link #intoGroup(Object)} instead.
28+
* @throws UnsupportedOperationException always
29+
*/
30+
@Deprecated(since = "2.2.0", forRemoval = true)
31+
@Override
32+
default Runnable apply(ResultContainer_ resultContainer, A a, B b, C c, D d) {
33+
throw new UnsupportedOperationException("Use intoGroup() instead.");
34+
}
35+
2136
}

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

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -34,23 +34,11 @@ public interface TriConstraintCollector<A, B, C, ResultContainer_, Result_> {
3434

3535
/**
3636
* As defined by {@link UniConstraintCollector#accumulator()}, but for {@link TriConstraintStream}.
37+
*
38+
* @see TriConstraintCollectorAccumulator An incremental API to be returned instead of the deprecated plain quad-function.
3739
*/
3840
QuadFunction<ResultContainer_, A, B, C, Runnable> accumulator();
3941

40-
/**
41-
* As defined by {@link UniConstraintCollector#incrementalAccumulator()}, but for {@link TriConstraintStream}.
42-
*/
43-
default TriConstraintCollectorAccumulator<ResultContainer_, A, B, C> incrementalAccumulator() {
44-
throw new UnsupportedOperationException();
45-
}
46-
47-
/**
48-
* As defined by {@link UniConstraintCollector#isIncremental()}, but for {@link TriConstraintStream}.
49-
*/
50-
default boolean isIncremental() {
51-
return false;
52-
}
53-
5442
/**
5543
* As defined by {@link UniConstraintCollector#finisher()}, but for {@link TriConstraintStream}.
5644
*/
Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,36 @@
11
package ai.timefold.solver.core.api.score.stream.tri;
22

3+
import ai.timefold.solver.core.api.function.QuadFunction;
34
import ai.timefold.solver.core.api.score.stream.uni.UniConstraintCollectorAccumulator;
45

56
import org.jspecify.annotations.NullMarked;
67

78
/**
89
* As defined by {@link UniConstraintCollectorAccumulator},
910
* only for {@link TriConstraintCollector}.
11+
* <p>
12+
* This interface extends {@link QuadFunction}, but {@link #apply(Object, Object, Object, Object)} always throws
13+
* {@link UnsupportedOperationException}. Use {@link #intoGroup(Object)} instead.
1014
*/
1115
@NullMarked
1216
@FunctionalInterface
13-
public interface TriConstraintCollectorAccumulator<ResultContainer_, A, B, C> {
17+
public interface TriConstraintCollectorAccumulator<ResultContainer_, A, B, C>
18+
extends QuadFunction<ResultContainer_, A, B, C, Runnable> {
1419

1520
/**
1621
* As defined by {@link UniConstraintCollectorAccumulator#intoGroup(Object)},
1722
* only for {@link TriConstraintCollector}.
1823
*/
1924
TriConstraintCollectorValueHandle<A, B, C> intoGroup(ResultContainer_ resultContainer);
2025

26+
/**
27+
* @deprecated Use {@link #intoGroup(Object)} instead.
28+
* @throws UnsupportedOperationException always
29+
*/
30+
@Deprecated(since = "2.2.0", forRemoval = true)
31+
@Override
32+
default Runnable apply(ResultContainer_ resultContainer, A a, B b, C c) {
33+
throw new UnsupportedOperationException("Use intoGroup() instead.");
34+
}
35+
2136
}

core/src/main/java/ai/timefold/solver/core/api/score/stream/uni/UniConstraintCollector.java

Lines changed: 7 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,8 @@
1515
* Usually created with {@link ConstraintCollectors}.
1616
* Used by {@link UniConstraintStream#groupBy(Function, UniConstraintCollector)}, ...
1717
* <p>
18-
* Loosely based on JDK's {@link Collector}, but it returns an undo operation for each accumulation
18+
* Loosely based on JDK's {@link Collector}, but it supports undoing each accumulation
1919
* to enable incremental score calculation in {@link UniConstraintStream#groupBy(UniConstraintCollector)}.
20-
* It has two modes of operation:
21-
* <dl>
22-
* <dt>Insert+Retract</dt>
23-
* <dd>An element cannot be updated - every update is done by full retraction and reinsertion.
24-
* This is simpler to implement and therefore it is the default.
25-
* Provided by {@link #accumulator()}.</dd>
26-
* <dt>Fully incremental</dt>
27-
* <dd>An element may be inserted, updated and retracted,
28-
* allowing for increased performance when a smart update operation is available.
29-
* Enabled by {@link #incrementalAccumulator()}, when {@link #isIncremental()} returns true.</dd>
30-
* </dl>
3120
* <p>
3221
* It is recommended that if two constraint collectors implement the same functionality,
3322
* they should {@link Object#equals(Object) be equal}.
@@ -55,38 +44,19 @@ public interface UniConstraintCollector<A, ResultContainer_, Result_> {
5544
Supplier<ResultContainer_> supplier();
5645

5746
/**
58-
* A lambda that extracts data from the matched fact,
47+
* A type that extracts data from the matched fact,
5948
* accumulates it in the result container
6049
* and returns an undo operation for that accumulation.
50+
* <p>
51+
* Implementations should return a {@link UniConstraintCollectorAccumulator} instead of a plain {@link BiFunction};
52+
* the solver detects this via {@code instanceof} and uses incremental insert/update/retract automatically.
53+
* The declared return type stays {@link BiFunction} for backward compatibility
54+
* and will be removed in a future major version of the solver.
6155
*
6256
* @return the accumulator. Called to insert the match and retract it when it no longer belongs in the group.
63-
* @see #incrementalAccumulator() High-performance alternative, disabled by default.
6457
*/
6558
BiFunction<ResultContainer_, A, Runnable> accumulator();
6659

67-
/**
68-
* A high-performance alternative to {@link #accumulator()} that returns an incremental accumulator.
69-
* This is more difficult to implement and therefore it is optional.
70-
* If implemented, override {@link #isIncremental()} to return true,
71-
* which will cause the solver to use this implementation instead of {@link #accumulator()}.
72-
*
73-
* @return the accumulator. Called to insert the match, reflect changes
74-
* or to revert it when the fact no longer belongs in the group.
75-
* @throws UnsupportedOperationException if {@link #isIncremental()} returns false
76-
*/
77-
default UniConstraintCollectorAccumulator<ResultContainer_, A> incrementalAccumulator() {
78-
throw new UnsupportedOperationException();
79-
}
80-
81-
/**
82-
* Whether the solver should use {@link #incrementalAccumulator()} instead of {@link #accumulator()}.
83-
*
84-
* @return defaults to false. Only override to true if {@link #incrementalAccumulator()} is implemented.
85-
*/
86-
default boolean isIncremental() {
87-
return false;
88-
}
89-
9060
/**
9161
* A lambda that converts the result container into the result.
9262
* The result may be null, typically when there is nothing accumulated
Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,22 @@
11
package ai.timefold.solver.core.api.score.stream.uni;
22

3+
import java.util.function.BiFunction;
4+
35
import org.jspecify.annotations.NullMarked;
46

57
/**
6-
* Used by {@link UniConstraintCollector#incrementalAccumulator()}.
7-
* Allows high-performance accumulation by providing an update operation
8-
* as well as by letting the accumulator decide whether the group was changed as a result of its operations,
9-
* but it is more difficult to implement.
8+
* Used by {@link UniConstraintCollector#accumulator()} for incremental collectors.
9+
* Extends {@link BiFunction} for backward compatibility, but {@link #apply(Object, Object)} always throws;
10+
* use {@link #intoGroup(Object)} instead.
11+
* Allows high-performance accumulation by providing an {@link UniConstraintCollectorValueHandle#replaceWith update} operation.
1012
*
1113
* @param <ResultContainer_>
1214
* @param <A>
1315
*/
1416
@NullMarked
1517
@FunctionalInterface
16-
public interface UniConstraintCollectorAccumulator<ResultContainer_, A> {
18+
public interface UniConstraintCollectorAccumulator<ResultContainer_, A>
19+
extends BiFunction<ResultContainer_, A, Runnable> {
1720

1821
/**
1922
* Created every time a new value enters the group.
@@ -23,11 +26,22 @@ public interface UniConstraintCollectorAccumulator<ResultContainer_, A> {
2326
* {@link UniConstraintCollector#supplier()} will be used to supply a fresh instance of the container;
2427
* otherwise a pre-existing container will be used, carrying the accumulated state.
2528
* {@link UniConstraintCollector#finisher()} will be used to extract the final accumulated value from the container.
26-
* The accumulator factory itself must not hold mutable cross-slot state;
27-
* any mutable state must live in the container or in the returned accumulated value.
28-
* @return the accumulator for the value, which will be used to insert the value to the group,
29+
* The method itself must not maintain any state;
30+
* any container-level state must live in the container,
31+
* and any state required to update or remove the value must live in the handle.
32+
* @return the handle for the value, which will be used to insert the value to the group,
2933
* to update it while in the group, and to remove it from the group.
3034
*/
3135
UniConstraintCollectorValueHandle<A> intoGroup(ResultContainer_ resultContainer);
3236

37+
/**
38+
* @deprecated Use {@link #intoGroup(Object)} instead.
39+
* @throws UnsupportedOperationException always
40+
*/
41+
@Deprecated(since = "2.2.0", forRemoval = true)
42+
@Override
43+
default Runnable apply(ResultContainer_ resultContainer, A a) {
44+
throw new UnsupportedOperationException("Use intoGroup() instead.");
45+
}
46+
3347
}

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,7 @@ protected AbstractGroupBiNode(int groupStoreIndex, int groupAccumulatorIndex,
2828
super(groupStoreIndex, groupKeyFunction, collector.supplier(), collector.finisher(), nextNodesTupleLifecycle,
2929
environmentMode);
3030
this.groupAccumulatorIndex = groupAccumulatorIndex;
31-
this.incrementalAccumulator = collector.isIncremental() ? collector.incrementalAccumulator()
32-
: BiCollectorUtils.toIncremental(collector.accumulator());
31+
this.incrementalAccumulator = BiCollectorUtils.toIncremental(collector.accumulator());
3332
}
3433

3534
protected AbstractGroupBiNode(int groupStoreIndex, Function<BiTuple<OldA, OldB>, GroupKey_> groupKeyFunction,

core/src/main/java/ai/timefold/solver/core/impl/bavet/quad/AbstractGroupQuadNode.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,7 @@ protected AbstractGroupQuadNode(int groupStoreIndex, int groupAccumulatorIndex,
2828
super(groupStoreIndex, groupKeyFunction, collector.supplier(), collector.finisher(), nextNodesTupleLifecycle,
2929
environmentMode);
3030
this.groupAccumulatorIndex = groupAccumulatorIndex;
31-
this.incrementalAccumulator = collector.isIncremental() ? collector.incrementalAccumulator()
32-
: QuadCollectorUtils.toIncremental(collector.accumulator());
31+
this.incrementalAccumulator = QuadCollectorUtils.toIncremental(collector.accumulator());
3332
}
3433

3534
protected AbstractGroupQuadNode(int groupStoreIndex,

0 commit comments

Comments
 (0)