Skip to content

Commit 1c29f34

Browse files
committed
chore: refactor CS index to support random access
1 parent c363e4c commit 1c29f34

File tree

46 files changed

+1230
-781
lines changed

Some content is hidden

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

46 files changed

+1230
-781
lines changed

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

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,30 +5,18 @@
55
import ai.timefold.solver.core.impl.bavet.common.index.IndexerFactory;
66
import ai.timefold.solver.core.impl.bavet.common.tuple.BiTuple;
77
import ai.timefold.solver.core.impl.bavet.common.tuple.TupleLifecycle;
8+
import ai.timefold.solver.core.impl.bavet.common.tuple.TupleStorePositionTracker;
89
import ai.timefold.solver.core.impl.bavet.common.tuple.UniTuple;
910

1011
public final class IndexedIfExistsBiNode<A, B, C> extends AbstractIndexedIfExistsNode<BiTuple<A, B>, C> {
1112

1213
private final TriPredicate<A, B, C> filtering;
1314

1415
public IndexedIfExistsBiNode(boolean shouldExist, IndexerFactory<C> indexerFactory,
15-
int inputStoreIndexLeftKeys, int inputStoreIndexLeftCounterEntry,
16-
int inputStoreIndexRightKeys, int inputStoreIndexRightEntry,
17-
TupleLifecycle<BiTuple<A, B>> nextNodesTupleLifecycle) {
18-
this(shouldExist, indexerFactory,
19-
inputStoreIndexLeftKeys, inputStoreIndexLeftCounterEntry, -1,
20-
inputStoreIndexRightKeys, inputStoreIndexRightEntry, -1,
21-
nextNodesTupleLifecycle, null);
22-
}
23-
24-
public IndexedIfExistsBiNode(boolean shouldExist, IndexerFactory<C> indexerFactory,
25-
int inputStoreIndexLeftKeys, int inputStoreIndexLeftCounterEntry, int inputStoreIndexLeftTrackerList,
26-
int inputStoreIndexRightKeys, int inputStoreIndexRightEntry, int inputStoreIndexRightTrackerList,
16+
TupleStorePositionTracker leftTupleStorePositionTracker, TupleStorePositionTracker rightTupleStorePositionTracker,
2717
TupleLifecycle<BiTuple<A, B>> nextNodesTupleLifecycle, TriPredicate<A, B, C> filtering) {
28-
super(shouldExist, indexerFactory.buildBiLeftKeysExtractor(), indexerFactory,
29-
inputStoreIndexLeftKeys, inputStoreIndexLeftCounterEntry, inputStoreIndexLeftTrackerList,
30-
inputStoreIndexRightKeys, inputStoreIndexRightEntry, inputStoreIndexRightTrackerList,
31-
nextNodesTupleLifecycle, filtering != null);
18+
super(shouldExist, indexerFactory.buildBiLeftKeysExtractor(), indexerFactory, leftTupleStorePositionTracker,
19+
rightTupleStorePositionTracker, nextNodesTupleLifecycle, filtering != null);
3220
this.filtering = filtering;
3321
}
3422

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

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,31 +5,26 @@
55
import ai.timefold.solver.core.impl.bavet.common.AbstractIndexedJoinNode;
66
import ai.timefold.solver.core.impl.bavet.common.index.IndexerFactory;
77
import ai.timefold.solver.core.impl.bavet.common.tuple.BiTuple;
8+
import ai.timefold.solver.core.impl.bavet.common.tuple.OutputStoreSizeTracker;
89
import ai.timefold.solver.core.impl.bavet.common.tuple.TupleLifecycle;
10+
import ai.timefold.solver.core.impl.bavet.common.tuple.TupleStorePositionTracker;
911
import ai.timefold.solver.core.impl.bavet.common.tuple.UniTuple;
1012

1113
public final class IndexedJoinBiNode<A, B> extends AbstractIndexedJoinNode<UniTuple<A>, B, BiTuple<A, B>> {
1214

1315
private final BiPredicate<A, B> filtering;
14-
private final int outputStoreSize;
15-
16-
public IndexedJoinBiNode(IndexerFactory<B> indexerFactory,
17-
int inputStoreIndexA, int inputStoreIndexEntryA, int inputStoreIndexOutTupleListA,
18-
int inputStoreIndexB, int inputStoreIndexEntryB, int inputStoreIndexOutTupleListB,
19-
TupleLifecycle<BiTuple<A, B>> nextNodesTupleLifecycle, BiPredicate<A, B> filtering,
20-
int outputStoreSize, int outputStoreIndexOutEntryA, int outputStoreIndexOutEntryB) {
21-
super(indexerFactory.buildUniLeftKeysExtractor(), indexerFactory,
22-
inputStoreIndexA, inputStoreIndexEntryA, inputStoreIndexOutTupleListA,
23-
inputStoreIndexB, inputStoreIndexEntryB, inputStoreIndexOutTupleListB,
24-
nextNodesTupleLifecycle, filtering != null,
25-
outputStoreIndexOutEntryA, outputStoreIndexOutEntryB);
16+
17+
public IndexedJoinBiNode(IndexerFactory<B> indexerFactory, TupleStorePositionTracker leftTupleStorePositionTracker,
18+
TupleStorePositionTracker rightTupleStorePositionTracker, OutputStoreSizeTracker outputStoreSizeTracker,
19+
TupleLifecycle<BiTuple<A, B>> nextNodesTupleLifecycle, BiPredicate<A, B> filtering) {
20+
super(indexerFactory.buildUniLeftKeysExtractor(), indexerFactory, leftTupleStorePositionTracker,
21+
rightTupleStorePositionTracker, outputStoreSizeTracker, nextNodesTupleLifecycle, filtering != null);
2622
this.filtering = filtering;
27-
this.outputStoreSize = outputStoreSize;
2823
}
2924

3025
@Override
3126
protected BiTuple<A, B> createOutTuple(UniTuple<A> leftTuple, UniTuple<B> rightTuple) {
32-
return new BiTuple<>(leftTuple.factA, rightTuple.factA, outputStoreSize);
27+
return new BiTuple<>(leftTuple.factA, rightTuple.factA, outputStoreSizeTracker.computeOutputStoreSize());
3328
}
3429

3530
@Override

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

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,29 +4,19 @@
44
import ai.timefold.solver.core.impl.bavet.common.AbstractUnindexedIfExistsNode;
55
import ai.timefold.solver.core.impl.bavet.common.tuple.BiTuple;
66
import ai.timefold.solver.core.impl.bavet.common.tuple.TupleLifecycle;
7+
import ai.timefold.solver.core.impl.bavet.common.tuple.TupleStorePositionTracker;
78
import ai.timefold.solver.core.impl.bavet.common.tuple.UniTuple;
89

910
public final class UnindexedIfExistsBiNode<A, B, C> extends AbstractUnindexedIfExistsNode<BiTuple<A, B>, C> {
1011

1112
private final TriPredicate<A, B, C> filtering;
1213

1314
public UnindexedIfExistsBiNode(boolean shouldExist,
14-
int inputStoreIndexLeftCounterEntry, int inputStoreIndexRightEntry,
15-
TupleLifecycle<BiTuple<A, B>> nextNodesTupleLifecycle) {
16-
this(shouldExist,
17-
inputStoreIndexLeftCounterEntry, -1, inputStoreIndexRightEntry, -1,
18-
nextNodesTupleLifecycle, null);
19-
}
20-
21-
public UnindexedIfExistsBiNode(boolean shouldExist,
22-
int inputStoreIndexLeftCounterEntry, int inputStoreIndexLeftTrackerList, int inputStoreIndexRightEntry,
23-
int inputStoreIndexRightTrackerList,
15+
TupleStorePositionTracker leftTupleStorePositionTracker, TupleStorePositionTracker rightTupleStorePositionTracker,
2416
TupleLifecycle<BiTuple<A, B>> nextNodesTupleLifecycle,
2517
TriPredicate<A, B, C> filtering) {
26-
super(shouldExist,
27-
inputStoreIndexLeftCounterEntry, inputStoreIndexLeftTrackerList, inputStoreIndexRightEntry,
28-
inputStoreIndexRightTrackerList,
29-
nextNodesTupleLifecycle, filtering != null);
18+
super(shouldExist, leftTupleStorePositionTracker, rightTupleStorePositionTracker, nextNodesTupleLifecycle,
19+
filtering != null);
3020
this.filtering = filtering;
3121
}
3222

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

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,32 +4,27 @@
44

55
import ai.timefold.solver.core.impl.bavet.common.AbstractUnindexedJoinNode;
66
import ai.timefold.solver.core.impl.bavet.common.tuple.BiTuple;
7+
import ai.timefold.solver.core.impl.bavet.common.tuple.OutputStoreSizeTracker;
78
import ai.timefold.solver.core.impl.bavet.common.tuple.TupleLifecycle;
9+
import ai.timefold.solver.core.impl.bavet.common.tuple.TupleStorePositionTracker;
810
import ai.timefold.solver.core.impl.bavet.common.tuple.UniTuple;
911

1012
public final class UnindexedJoinBiNode<A, B>
1113
extends AbstractUnindexedJoinNode<UniTuple<A>, B, BiTuple<A, B>> {
1214

1315
private final BiPredicate<A, B> filtering;
14-
private final int outputStoreSize;
15-
16-
public UnindexedJoinBiNode(
17-
int inputStoreIndexLeftEntry, int inputStoreIndexLeftOutTupleList,
18-
int inputStoreIndexRightEntry, int inputStoreIndexRightOutTupleList,
19-
TupleLifecycle<BiTuple<A, B>> nextNodesTupleLifecycle, BiPredicate<A, B> filtering,
20-
int outputStoreSize,
21-
int outputStoreIndexLeftOutEntry, int outputStoreIndexRightOutEntry) {
22-
super(inputStoreIndexLeftEntry, inputStoreIndexLeftOutTupleList,
23-
inputStoreIndexRightEntry, inputStoreIndexRightOutTupleList,
24-
nextNodesTupleLifecycle, filtering != null,
25-
outputStoreIndexLeftOutEntry, outputStoreIndexRightOutEntry);
16+
17+
public UnindexedJoinBiNode(TupleStorePositionTracker leftTupleStorePositionTracker,
18+
TupleStorePositionTracker rightTupleStorePositionTracker, OutputStoreSizeTracker outputStoreSizeTracker,
19+
TupleLifecycle<BiTuple<A, B>> nextNodesTupleLifecycle, BiPredicate<A, B> filtering) {
20+
super(leftTupleStorePositionTracker, rightTupleStorePositionTracker, outputStoreSizeTracker, nextNodesTupleLifecycle,
21+
filtering != null);
2622
this.filtering = filtering;
27-
this.outputStoreSize = outputStoreSize;
2823
}
2924

3025
@Override
3126
protected BiTuple<A, B> createOutTuple(UniTuple<A> leftTuple, UniTuple<B> rightTuple) {
32-
return new BiTuple<>(leftTuple.factA, rightTuple.factA, outputStoreSize);
27+
return new BiTuple<>(leftTuple.factA, rightTuple.factA, outputStoreSizeTracker.computeOutputStoreSize());
3328
}
3429

3530
@Override

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

Lines changed: 33 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
package ai.timefold.solver.core.impl.bavet.common;
22

3+
import ai.timefold.solver.core.impl.bavet.common.index.IndexedSet;
34
import ai.timefold.solver.core.impl.bavet.common.tuple.AbstractTuple;
45
import ai.timefold.solver.core.impl.bavet.common.tuple.TupleLifecycle;
56
import ai.timefold.solver.core.impl.bavet.common.tuple.TupleState;
7+
import ai.timefold.solver.core.impl.bavet.common.tuple.TupleStorePositionTracker;
68
import ai.timefold.solver.core.impl.bavet.common.tuple.UniTuple;
7-
import ai.timefold.solver.core.impl.util.ElementAwareList;
8-
import ai.timefold.solver.core.impl.util.ElementAwareListEntry;
99

1010
/**
1111
* This class has two direct children: {@link AbstractIndexedIfExistsNode} and {@link AbstractUnindexedIfExistsNode}.
@@ -20,20 +20,17 @@ public abstract class AbstractIfExistsNode<LeftTuple_ extends AbstractTuple, Rig
2020
extends AbstractTwoInputNode<LeftTuple_, UniTuple<Right_>> {
2121

2222
protected final boolean shouldExist;
23-
24-
protected final int inputStoreIndexLeftTrackerList; // -1 if !isFiltering
25-
protected final int inputStoreIndexRightTrackerList; // -1 if !isFiltering
26-
23+
protected final int inputStoreIndexLeftTrackerSet; // -1 if !isFiltering
24+
protected final int inputStoreIndexRightTrackerSet; // -1 if !isFiltering
2725
protected final boolean isFiltering;
2826
private final DynamicPropagationQueue<LeftTuple_, ExistsCounter<LeftTuple_>> propagationQueue;
2927

30-
protected AbstractIfExistsNode(boolean shouldExist,
31-
int inputStoreIndexLeftTrackerList, int inputStoreIndexRightTrackerList,
32-
TupleLifecycle<LeftTuple_> nextNodesTupleLifecycle,
28+
protected AbstractIfExistsNode(boolean shouldExist, TupleStorePositionTracker leftTupleStorePositionTracker,
29+
TupleStorePositionTracker rightTupleStorePositionTracker, TupleLifecycle<LeftTuple_> nextNodesTupleLifecycle,
3330
boolean isFiltering) {
3431
this.shouldExist = shouldExist;
35-
this.inputStoreIndexLeftTrackerList = inputStoreIndexLeftTrackerList;
36-
this.inputStoreIndexRightTrackerList = inputStoreIndexRightTrackerList;
32+
this.inputStoreIndexLeftTrackerSet = isFiltering ? leftTupleStorePositionTracker.reserveNextAvailablePosition() : -1;
33+
this.inputStoreIndexRightTrackerSet = isFiltering ? rightTupleStorePositionTracker.reserveNextAvailablePosition() : -1;
3734
this.isFiltering = isFiltering;
3835
this.propagationQueue = new DynamicPropagationQueue<>(nextNodesTupleLifecycle);
3936
}
@@ -66,8 +63,9 @@ protected void updateCounterLeft(ExistsCounter<LeftTuple_> counter) {
6663
}
6764
case OK, DYING -> propagationQueue.update(counter);
6865
case DEAD, ABORTING -> propagationQueue.insert(counter);
69-
default -> throw new IllegalStateException("Impossible state: the counter (" + counter
70-
+ ") has an impossible insert state (" + state + ").");
66+
default ->
67+
throw new IllegalStateException("Impossible state: the counter (%s) has an impossible insert state (%s)."
68+
.formatted(counter, state));
7169
}
7270
} else {
7371
// Retract or remain dead
@@ -80,8 +78,9 @@ protected void updateCounterLeft(ExistsCounter<LeftTuple_> counter) {
8078
propagationQueue.retract(counter, TupleState.ABORTING);
8179
case OK, UPDATING -> // Kill the original propagation.
8280
propagationQueue.retract(counter, TupleState.DYING);
83-
default -> throw new IllegalStateException("Impossible state: The counter (" + counter
84-
+ ") has an impossible retract state (" + state + ").");
81+
default ->
82+
throw new IllegalStateException("Impossible state: The counter (%s) has an impossible retract state (%s)."
83+
.formatted(counter, state));
8584

8685
}
8786
}
@@ -115,27 +114,26 @@ protected void decrementCounterRight(ExistsCounter<LeftTuple_> counter) {
115114
} // Else do not even propagate an update
116115
}
117116

118-
protected ElementAwareList<FilteringTracker<LeftTuple_>> updateRightTrackerList(UniTuple<Right_> rightTuple) {
119-
ElementAwareList<FilteringTracker<LeftTuple_>> rightTrackerList = rightTuple.getStore(inputStoreIndexRightTrackerList);
120-
for (FilteringTracker<LeftTuple_> tuple : rightTrackerList) {
117+
IndexedSet<ExistsCounterHandle<LeftTuple_>> updateRightTrackerSet(UniTuple<Right_> rightTuple) {
118+
IndexedSet<ExistsCounterHandle<LeftTuple_>> rightTrackerSet = rightTuple.getStore(inputStoreIndexRightTrackerSet);
119+
rightTrackerSet.forEach(tuple -> {
121120
decrementCounterRight(tuple.counter);
122121
tuple.remove();
123-
}
124-
return rightTrackerList;
122+
});
123+
return rightTrackerSet;
125124
}
126125

127-
protected void updateCounterFromLeft(LeftTuple_ leftTuple, UniTuple<Right_> rightTuple, ExistsCounter<LeftTuple_> counter,
128-
ElementAwareList<FilteringTracker<LeftTuple_>> leftTrackerList) {
126+
void updateCounterFromLeft(LeftTuple_ leftTuple, UniTuple<Right_> rightTuple, ExistsCounter<LeftTuple_> counter,
127+
IndexedSet<ExistsCounterHandle<LeftTuple_>> leftTrackerSet) {
129128
if (testFiltering(leftTuple, rightTuple)) {
130129
counter.countRight++;
131-
ElementAwareList<FilteringTracker<LeftTuple_>> rightTrackerList =
132-
rightTuple.getStore(inputStoreIndexRightTrackerList);
133-
new FilteringTracker<>(counter, leftTrackerList, rightTrackerList);
130+
IndexedSet<ExistsCounterHandle<LeftTuple_>> rightTrackerSet = rightTuple.getStore(inputStoreIndexRightTrackerSet);
131+
new ExistsCounterHandle<>(counter, leftTrackerSet, rightTrackerSet);
134132
}
135133
}
136134

137-
protected void updateCounterFromRight(UniTuple<Right_> rightTuple, ExistsCounter<LeftTuple_> counter,
138-
ElementAwareList<FilteringTracker<LeftTuple_>> rightTrackerList) {
135+
void updateCounterFromRight(UniTuple<Right_> rightTuple, ExistsCounter<LeftTuple_> counter,
136+
IndexedSet<ExistsCounterHandle<LeftTuple_>> rightTrackerSet) {
139137
var leftTuple = counter.leftTuple;
140138
if (!leftTuple.state.isActive()) {
141139
// Assume the following scenario:
@@ -156,18 +154,18 @@ protected void updateCounterFromRight(UniTuple<Right_> rightTuple, ExistsCounter
156154
}
157155
if (testFiltering(counter.leftTuple, rightTuple)) {
158156
incrementCounterRight(counter);
159-
ElementAwareList<FilteringTracker<LeftTuple_>> leftTrackerList =
160-
counter.leftTuple.getStore(inputStoreIndexLeftTrackerList);
161-
new FilteringTracker<>(counter, leftTrackerList, rightTrackerList);
157+
IndexedSet<ExistsCounterHandle<LeftTuple_>> leftTrackerSet =
158+
counter.leftTuple.getStore(inputStoreIndexLeftTrackerSet);
159+
new ExistsCounterHandle<>(counter, leftTrackerSet, rightTrackerSet);
162160
}
163161
}
164162

165163
private void doInsertCounter(ExistsCounter<LeftTuple_> counter) {
166164
switch (counter.state) {
167165
case DYING -> propagationQueue.update(counter);
168166
case DEAD, ABORTING -> propagationQueue.insert(counter);
169-
default -> throw new IllegalStateException("Impossible state: the counter (" + counter
170-
+ ") has an impossible insert state (" + counter.state + ").");
167+
default -> throw new IllegalStateException("Impossible state: the counter (%s) has an impossible insert state (%s)."
168+
.formatted(counter, counter.state));
171169
}
172170
}
173171

@@ -177,8 +175,9 @@ private void doRetractCounter(ExistsCounter<LeftTuple_> counter) {
177175
propagationQueue.retract(counter, TupleState.ABORTING);
178176
case OK, UPDATING -> // Kill the original propagation.
179177
propagationQueue.retract(counter, TupleState.DYING);
180-
default -> throw new IllegalStateException("Impossible state: The counter (" + counter
181-
+ ") has an impossible retract state (" + counter.state + ").");
178+
default ->
179+
throw new IllegalStateException("Impossible state: The counter (%s) has an impossible retract state (%s)."
180+
.formatted(counter, counter.state));
182181
}
183182
}
184183

@@ -187,23 +186,4 @@ public Propagator getPropagator() {
187186
return propagationQueue;
188187
}
189188

190-
protected static final class FilteringTracker<LeftTuple_ extends AbstractTuple> {
191-
final ExistsCounter<LeftTuple_> counter;
192-
private final ElementAwareListEntry<FilteringTracker<LeftTuple_>> leftTrackerEntry;
193-
private final ElementAwareListEntry<FilteringTracker<LeftTuple_>> rightTrackerEntry;
194-
195-
FilteringTracker(ExistsCounter<LeftTuple_> counter, ElementAwareList<FilteringTracker<LeftTuple_>> leftTrackerList,
196-
ElementAwareList<FilteringTracker<LeftTuple_>> rightTrackerList) {
197-
this.counter = counter;
198-
leftTrackerEntry = leftTrackerList.add(this);
199-
rightTrackerEntry = rightTrackerList.add(this);
200-
}
201-
202-
public void remove() {
203-
leftTrackerEntry.remove();
204-
rightTrackerEntry.remove();
205-
}
206-
207-
}
208-
209189
}

0 commit comments

Comments
 (0)