Skip to content

Commit 478bcd2

Browse files
chore: make getIndictedObjects() return null when indictments disabled, add notes about stale ifExists indictments
1 parent 83b2569 commit 478bcd2

5 files changed

Lines changed: 19 additions & 9 deletions

File tree

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,8 @@ protected void incrementCounterRightWithoutIndictment(ExistsCounter<LeftTuple_>
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.
116118
counter.countRight++;
117119
}
118120

@@ -124,6 +126,8 @@ protected void incrementCounterRightUpdatingIndictment(ExistsCounter<LeftTuple_>
124126
doRetractCounter(counter);
125127
}
126128
} // Else do not even propagate an update
129+
// NOTE: By not propagating here, the left tuple's indicted objects can be stale
130+
// if an element is removed.
127131
IndictmentSource.addSupport(getId(), counter.leftTuple, rightTuple);
128132
counter.countRight++;
129133
}

core/src/main/java/ai/timefold/solver/core/impl/score/constraint/ConstraintMatch.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public final class ConstraintMatch<Score_ extends Score<Score_>> implements Comp
3030

3131
private final ConstraintRef constraintRef;
3232
private final @Nullable ConstraintJustification justification;
33-
private final List<Object> indictedObjects;
33+
private final @Nullable List<Object> indictedObjects;
3434
private final Score_ score;
3535

3636
/**
@@ -39,7 +39,7 @@ public final class ConstraintMatch<Score_ extends Score<Score_>> implements Comp
3939
* @param score penalty or reward associated with the constraint match
4040
*/
4141
public ConstraintMatch(ConstraintRef constraintRef, @Nullable ConstraintJustification justification,
42-
List<Object> indictedObjects, Score_ score) {
42+
@Nullable List<Object> indictedObjects, Score_ score) {
4343
this.constraintRef = requireNonNull(constraintRef);
4444
this.justification = justification;
4545
this.indictedObjects = indictedObjects;
@@ -67,7 +67,7 @@ public ConstraintRef getConstraintRef() {
6767
return (Justification_) justification;
6868
}
6969

70-
public List<Object> getIndictedObjects() {
70+
public @Nullable List<Object> getIndictedObjects() {
7171
return indictedObjects;
7272
}
7373

core/src/main/java/ai/timefold/solver/core/impl/score/constraint/ConstraintMatchTotal.java

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

33
import static java.util.Objects.requireNonNull;
44

5-
import java.util.Collections;
65
import java.util.LinkedHashSet;
76
import java.util.List;
87
import java.util.Set;
@@ -94,7 +93,7 @@ public ConstraintMatch<Score_> addConstraintMatch(ConstraintJustification justif
9493
* @return never null
9594
*/
9695
public ConstraintMatch<Score_> addConstraintMatch(ConstraintJustification justification, Score_ score) {
97-
var constraintMatch = new ConstraintMatch<>(constraintRef, justification, Collections.emptyList(), score);
96+
var constraintMatch = new ConstraintMatch<>(constraintRef, justification, null, score);
9897
addConstraintMatch(constraintMatch);
9998
return constraintMatch;
10099
}

core/src/main/java/ai/timefold/solver/core/impl/score/stream/common/inliner/ConstraintMatchSupplier.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import ai.timefold.solver.core.impl.score.stream.common.AbstractConstraint;
2626

2727
import org.jspecify.annotations.NullMarked;
28+
import org.jspecify.annotations.Nullable;
2829

2930
/**
3031
* Allows creating {@link ConstraintMatch} instances lazily if and only if they are required by the end user.
@@ -42,9 +43,9 @@
4243
public interface ConstraintMatchSupplier<Score_ extends Score<Score_>>
4344
extends BiFunction<Constraint, Score_, ConstraintMatch<Score_>> {
4445

45-
static List<Object> collectIndictments(Constraint constraint, Tuple tuple) {
46+
static @Nullable List<Object> collectIndictments(Constraint constraint, Tuple tuple) {
4647
if (tuple.getIndictmentSource() == IndictmentSource.DISABLED) {
47-
return Collections.emptyList();
48+
return null;
4849
}
4950
var out = new LinkedHashSet<>();
5051
var abstractConstraint = (AbstractConstraint<?, ?, ?>) constraint;

core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/uni/AbstractUniConstraintStreamTest.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -478,13 +478,19 @@ public void ifExists_0Joiner0Filter() {
478478
scoreDirector.beforeProblemFactRemoved(entityGroup);
479479
solution.getEntityGroupList().remove(entityGroup);
480480
scoreDirector.afterProblemFactRemoved(entityGroup);
481+
482+
// Note: entityGroup is still indicted, despite being removed,
483+
// since only the right tuple of the ifExists changed, so no
484+
// change was propagated.
481485
assertScore(scoreDirector,
482486
assertMatch(solution.getFirstValueGroup()).withIndictedObjects(
483487
solution.getFirstValueGroup(),
484-
solution.getFirstEntityGroup()),
488+
solution.getFirstEntityGroup(),
489+
entityGroup),
485490
assertMatch(valueGroup).withIndictedObjects(
486491
valueGroup,
487-
solution.getFirstEntityGroup()));
492+
solution.getFirstEntityGroup(),
493+
entityGroup));
488494
}
489495

490496
@Override

0 commit comments

Comments
 (0)