Skip to content

Commit b438f8b

Browse files
chore: merge fixes
1 parent a1b0cb5 commit b438f8b

8 files changed

Lines changed: 101 additions & 102 deletions

File tree

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

Lines changed: 48 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -105,53 +105,54 @@ protected void killCounterLeft(ExistsCounter<LeftTuple_> counter) {
105105
}
106106
}
107107

108-
protected void incrementCounterRightWithoutIndictment(ExistsCounter<LeftTuple_> counter) {
108+
protected void incrementCounterRight(ExistsCounter<LeftTuple_> counter) {
109109
if (counter.countRight == 0) {
110110
if (shouldExist) {
111111
doInsertCounter(counter);
112112
} else {
113113
doRetractCounter(counter);
114114
}
115115
} // Else do not even propagate an update
116-
// NOTE: By not propagating here, the left tuple's indicted objects can be stale
117-
// if an element is removed.
118116
counter.countRight++;
119117
}
120118

121119
protected void incrementCounterRightUpdatingIndictment(ExistsCounter<LeftTuple_> counter, UniTuple<Right_> rightTuple) {
120+
IndictmentSource.addSupport(getId(), counter.getTuple(), rightTuple);
122121
if (counter.countRight == 0) {
123122
if (shouldExist) {
124123
doInsertCounter(counter);
125124
} else {
126125
doRetractCounter(counter);
127126
}
128-
} else {
129-
// count != 0, so only propagate if we are in an `ifExists`
130-
if (shouldExist) {
131-
doUpdateCounter(counter);
132-
}
133-
} // Else do not even propagate an update
134-
// NOTE: By not propagating here, the left tuple's indicted objects can be stale
135-
// if an element is removed.
136-
IndictmentSource.addSupport(getId(), counter.leftTuple, rightTuple);
127+
} else if (shouldExist) {
128+
doUpdateCounter(counter);
129+
}
137130
counter.countRight++;
138131
}
139132

140133
protected void decrementCounterRight(ExistsCounter<LeftTuple_> counter) {
141134
counter.countRight--;
142-
IndictmentSource.removeSupport(getId(), counter.leftTuple, rightTuple);
143135
if (counter.countRight == 0) {
144136
if (shouldExist) {
145137
doRetractCounter(counter);
146138
} else {
147139
doInsertCounter(counter);
148140
}
149-
} else {
150-
// count != 0, so only propagate if we are in an `ifExists`
141+
} // Else do not even propagate an update
142+
}
143+
144+
protected void decrementCounterRightUpdatingIndictment(ExistsCounter<LeftTuple_> counter, UniTuple<Right_> rightTuple) {
145+
IndictmentSource.removeSupport(getId(), counter.getTuple(), rightTuple);
146+
counter.countRight--;
147+
if (counter.countRight == 0) {
151148
if (shouldExist) {
152-
doUpdateCounter(counter);
149+
doRetractCounter(counter);
150+
} else {
151+
doInsertCounter(counter);
153152
}
154-
} // Else do not even propagate an update
153+
} else if (shouldExist) {
154+
doUpdateCounter(counter);
155+
}
155156
}
156157

157158
// Clears the left tracker list rooted at leftTuple's inputStoreIndexLeftTrackerList slot,
@@ -192,18 +193,18 @@ private void removeRight(FilteringTracker<LeftTuple_> tracker) {
192193
// Walk safety: removeFromLeft only touches left-side links, so rightNext is stable across the call.
193194
protected void clearRightTrackerList(UniTuple<Right_> rightTuple) {
194195
FilteringTracker<LeftTuple_> tracker = rightTuple.removeStore(inputStoreIndexRightTrackerList);
195-
if (rightTuple.getIndictmentSource() != IndictmentSource.DISABLED) {
196+
if (rightTuple.getIndictmentSource() == IndictmentSource.DISABLED) {
196197
while (tracker != null) {
197198
var next = tracker.rightNext;
198-
decrementCounterRightUpdatingIndictment(tracker.counter, rightTuple);
199-
removeFromLeft(tracker);
199+
decrementCounterRight(tracker.counter);
200+
removeLeft(tracker);
200201
tracker = next;
201202
}
202203
} else {
203204
while (tracker != null) {
204205
var next = tracker.rightNext;
205-
decrementCounterRightWithoutIndictment(tracker.counter);
206-
removeFromLeft(tracker);
206+
decrementCounterRightUpdatingIndictment(tracker.counter, rightTuple);
207+
removeLeft(tracker);
207208
tracker = next;
208209
}
209210
}
@@ -233,13 +234,17 @@ protected void updateCounterLeft(ExistsCounter<LeftTuple_> counter, UniTuple<Rig
233234
// which happens when the right input's node sits in a higher layer than the left input's,
234235
// so the left's inserts and updates are delivered before the right's retracts are.
235236
// Skipping is safe, as the pending retract will not have a tracker to clear for this pair.
237+
IndictmentSource.removeSupport(getId(), counter.getTuple(), rightTuple);
236238
return;
237239
}
238240
if (testFiltering(counter.leftTuple, rightTuple)) {
239241
counter.countRight++;
242+
IndictmentSource.addSupport(getId(), counter.getTuple(), rightTuple);
240243
var tracker = new FilteringTracker<>(counter, rightTuple);
241244
linkLeft(tracker);
242245
linkRight(tracker);
246+
} else {
247+
IndictmentSource.removeSupport(getId(), counter.getTuple(), rightTuple);
243248
}
244249
}
245250

@@ -284,13 +289,17 @@ protected void updateCounterRight(ExistsCounter<LeftTuple_> counter, UniTuple<Ri
284289
// The left tuple can be inactive here because its node sits in a higher layer than the right's:
285290
// the right's inserts and updates are delivered before the left's retracts are.
286291
// The mirror case is possible too, see updateCounterLeft(...).
292+
IndictmentSource.removeSupport(getId(), counter.getTuple(), rightTuple);
287293
return;
288294
}
289295
if (testFiltering(leftTuple, rightTuple)) {
290-
incrementCounterRightUpdatingIndictment(counter, rightTuple);
296+
incrementCounterRight(counter);
297+
IndictmentSource.addSupport(getId(), counter.getTuple(), rightTuple);
291298
var tracker = new FilteringTracker<>(counter, rightTuple);
292299
linkLeft(tracker);
293300
linkRight(tracker);
301+
} else {
302+
IndictmentSource.removeSupport(getId(), counter.getTuple(), rightTuple);
294303
}
295304
}
296305

@@ -303,6 +312,21 @@ private void doInsertCounter(ExistsCounter<LeftTuple_> counter) {
303312
}
304313
}
305314

315+
private void doUpdateCounter(ExistsCounter<LeftTuple_> counter) {
316+
switch (counter.state) {
317+
case CREATING, UPDATING -> {
318+
// Do nothing
319+
}
320+
case OK -> {
321+
propagationQueue.update(counter);
322+
}
323+
case DYING, DEAD, ABORTING -> {
324+
throw new IllegalStateException("Impossible state: The counter (%s) has an impossible retract state (%s)."
325+
.formatted(counter, counter.state));
326+
}
327+
}
328+
}
329+
306330
private void doRetractCounter(ExistsCounter<LeftTuple_> counter) {
307331
switch (counter.state) {
308332
case CREATING -> // Kill it before it propagates.
@@ -315,15 +339,6 @@ private void doRetractCounter(ExistsCounter<LeftTuple_> counter) {
315339
}
316340
}
317341

318-
private void doUpdateCounter(ExistsCounter<LeftTuple_> counter) {
319-
switch (counter.state) {
320-
case DYING, OK, UPDATING, CREATING -> propagationQueue.update(counter);
321-
case DEAD, ABORTING -> propagationQueue.insert(counter);
322-
default -> throw new IllegalStateException("Impossible state: the counter (%s) has an impossible insert state (%s)."
323-
.formatted(counter, counter.state));
324-
}
325-
}
326-
327342
@Override
328343
protected boolean canProduceTuples() {
329344
// The left input must produce tuples no matter what,
@@ -369,4 +384,4 @@ protected static final class FilteringTracker<LeftTuple_ extends Tuple> {
369384

370385
}
371386

372-
}
387+
}

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

Lines changed: 8 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ private void updateCounterRight(LeftTuple_ leftTuple, Object compositeKey, Exist
101101
if (!isFiltering) {
102102
counter.countRight = rightSize(leftTuple, compositeKey);
103103
if (leftTuple.getIndictmentSource() != IndictmentSource.DISABLED) {
104-
IndictmentSource.clearSupport(getId(), leftTuple);
104+
leftTuple.getIndictmentSource().getSupportForNodeId(getId()).clear();
105105
forEachRightFromLeft(leftTuple, compositeKey, rightTuple -> {
106106
IndictmentSource.addSupport(getId(), leftTuple, rightTuple);
107107
});
@@ -140,12 +140,6 @@ public final void updateLeft(LeftTuple_ leftTuple) {
140140
// The indexers contain counters in the DEAD state, to track the rightCount.
141141
if (!isFiltering) {
142142
updateUnchangedCounterLeft(counter);
143-
if (leftTuple.getIndictmentSource() != IndictmentSource.DISABLED) {
144-
IndictmentSource.clearSupport(getId(), leftTuple);
145-
forEachRightFromLeft(leftTuple, newCompositeKey, rightTuple -> {
146-
IndictmentSource.addSupport(getId(), leftTuple, rightTuple);
147-
});
148-
}
149143
} else {
150144
// Call filtering for the leftTuple and rightTuple combinations again
151145
clearLeftTrackerList(leftTuple);
@@ -216,10 +210,8 @@ public final void insertRight(UniTuple<Right_> rightTuple) {
216210

217211
private void updateCounterLeft(UniTuple<Right_> rightTuple, Object compositeKey) {
218212
if (!isFiltering) {
219-
// To prevent creating a dynamic lambda on the hot path,
220-
// only call the 2-args version when indictments are enabled
221213
if (rightTuple.getIndictmentSource() == IndictmentSource.DISABLED) {
222-
forEachLeftCounter(rightTuple, compositeKey, this::incrementCounterRightWithoutIndictment);
214+
forEachLeftCounter(rightTuple, compositeKey, this::incrementCounterRight);
223215
} else {
224216
forEachLeftCounter(rightTuple, compositeKey,
225217
counter -> incrementCounterRightUpdatingIndictment(counter, rightTuple));
@@ -262,14 +254,7 @@ public final void updateRight(UniTuple<Right_> rightTuple) {
262254
indexerRight.remove(oldCompositeKey, entry);
263255
}
264256
if (!isFiltering) {
265-
// To prevent creating a dynamic lambda on the hot path,
266-
// only call the 2-args version when indictments are enabled
267-
if (rightTuple.getIndictmentSource() == IndictmentSource.DISABLED) {
268-
forEachLeftCounter(rightTuple, oldCompositeKey, this::decrementCounterRightWithoutIndictment);
269-
} else {
270-
forEachLeftCounter(rightTuple, oldCompositeKey,
271-
counter -> decrementCounterRightUpdatingIndictment(counter, rightTuple));
272-
}
257+
forEachLeftCounter(rightTuple, oldCompositeKey, this::decrementCounterRight);
273258
} else {
274259
clearRightTrackerList(rightTuple);
275260
}
@@ -292,26 +277,19 @@ public final void retractRight(UniTuple<Right_> rightTuple) {
292277
bucket.removeRight(compositeKey, entry);
293278
fusedEqualIndex.removeBucketIfEmpty(compositeKey, bucket);
294279
if (!isFiltering) {
295-
// To prevent creating a dynamic lambda on the hot path,
296-
// only call the 2-args version when indictments are enabled
297280
if (rightTuple.getIndictmentSource() == IndictmentSource.DISABLED) {
298-
bucket.forEachLeft(compositeKey, this::decrementCounterRightWithoutIndictment);
281+
bucket.forEachLeft(compositeKey, this::decrementCounterRight);
299282
} else {
300-
bucket.forEachLeft(compositeKey, counter -> decrementCounterRightUpdatingIndictment(counter, rightTuple));
283+
bucket.forEachLeft(compositeKey,
284+
leftTuple -> decrementCounterRightUpdatingIndictment(leftTuple, rightTuple));
301285
}
302286
} else {
303287
clearRightTrackerList(rightTuple);
304288
}
305289
} else {
306290
indexerRight.remove(compositeKey, entry);
307291
if (!isFiltering) {
308-
// To prevent creating a dynamic lambda on the hot path,
309-
// only call the 2-args version when indictments are enabled
310-
if (rightTuple.getIndictmentSource() == IndictmentSource.DISABLED) {
311-
indexerLeft.forEach(compositeKey, this::decrementCounterRightWithoutIndictment);
312-
} else {
313-
indexerLeft.forEach(compositeKey, counter -> decrementCounterRightUpdatingIndictment(counter, rightTuple));
314-
}
292+
indexerLeft.forEach(compositeKey, this::decrementCounterRight);
315293
} else {
316294
clearRightTrackerList(rightTuple);
317295
}
@@ -381,4 +359,4 @@ private void forEachLeftCounter(UniTuple<Right_> rightTuple, Object compositeKey
381359
}
382360
}
383361

384-
}
362+
}

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

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -104,10 +104,8 @@ public final void insertRight(UniTuple<Right_> rightTuple) {
104104
}
105105
rightTuple.setStore(inputStoreIndexRightEntry, rightTupleList.add(rightTuple));
106106
if (!isFiltering) {
107-
// To prevent creating a dynamic lambda on the hot path,
108-
// only call the 2-args version when indictments are enabled
109107
if (rightTuple.getIndictmentSource() == IndictmentSource.DISABLED) {
110-
counterList.forEach(this::incrementCounterRightWithoutIndictment);
108+
counterList.forEach(this::incrementCounterRight);
111109
} else {
112110
counterList.forEach(counter -> incrementCounterRightUpdatingIndictment(counter, rightTuple));
113111
}
@@ -145,10 +143,8 @@ public final void retractRight(UniTuple<Right_> rightTuple) {
145143
}
146144
rightEntry.remove();
147145
if (!isFiltering) {
148-
// To prevent creating a dynamic lambda on the hot path,
149-
// only call the 2-args version when indictments are enabled
150146
if (rightTuple.getIndictmentSource() == IndictmentSource.DISABLED) {
151-
counterList.forEach(this::decrementCounterRightWithoutIndictment);
147+
counterList.forEach(this::decrementCounterRight);
152148
} else {
153149
counterList.forEach(counter -> decrementCounterRightUpdatingIndictment(counter, rightTuple));
154150
}
@@ -157,4 +153,4 @@ public final void retractRight(UniTuple<Right_> rightTuple) {
157153
}
158154
}
159155

160-
}
156+
}

0 commit comments

Comments
 (0)