Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -184,26 +184,33 @@ public final void update(InTuple_ tuple) {
Runnable undoAccumulator = tuple.getStore(undoStoreIndex);
undoAccumulator.run();
}

var oldUserSuppliedGroupKey = hasMultipleGroups ? extractUserSuppliedKey(oldGroup.getGroupKey()) : null;
var newUserSuppliedGroupKey = hasMultipleGroups ? groupKeyFunction.apply(tuple) : null;
if (Objects.equals(newUserSuppliedGroupKey, oldUserSuppliedGroupKey)) {
// No need to change parentCount because it is the same group
var outTuple = accumulate(tuple, oldGroup);
switch (outTuple.state) {
case CREATING, UPDATING -> {
// Already in the correct state.
}
case OK -> propagationQueue.update(oldGroup);
default -> throw new IllegalStateException("Impossible state: The group (" + oldGroup + ") in node (" + this
+ ") is in an unexpected state (" + outTuple.state + ").");
}
if (!hasMultipleGroups) {
updateGroup(tuple, oldGroup);
return;
}
var oldUserSuppliedGroupKey = extractUserSuppliedKey(oldGroup.getGroupKey());
var newUserSuppliedGroupKey = groupKeyFunction.apply(tuple);
if (Objects.equals(oldUserSuppliedGroupKey, newUserSuppliedGroupKey)) {
updateGroup(tuple, oldGroup);
} else {
killTuple(oldGroup);
createTuple(tuple, newUserSuppliedGroupKey);
}
}

private void updateGroup(InTuple_ tuple, Group<OutTuple_, ResultContainer_> oldGroup) { // No need to change parentCount because it is the same group
var outTuple = accumulate(tuple, oldGroup);
switch (outTuple.state) {
case CREATING, UPDATING -> {
// Already in the correct state.
}
case OK -> propagationQueue.update(oldGroup);
default ->
throw new IllegalStateException("Impossible state: The group (%s) in node (%s) is in an unexpected state (%s)."
.formatted(oldGroup, this, outTuple.state));
}
}

private void killTuple(Group<OutTuple_, ResultContainer_> group) {
var newParentCount = --group.parentCount;
var killGroup = (newParentCount == 0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.function.Consumer;

import ai.timefold.solver.core.impl.bavet.common.tuple.AbstractTuple;
import ai.timefold.solver.core.impl.bavet.common.tuple.TupleLifecycle;
Expand Down Expand Up @@ -73,10 +74,8 @@ public void retract(Tuple_ carrier, TupleState state) {

@Override
public void propagateRetracts() {
if (retractQueue.isEmpty()) {
return;
}
for (var tuple : retractQueue) {
while (!retractQueue.isEmpty()) {
var tuple = retractQueue.poll();
switch (tuple.state) {
case DYING -> {
// Change state before propagation, so that the next node can't make decisions on the original state.
Expand All @@ -86,43 +85,34 @@ public void propagateRetracts() {
case ABORTING -> tuple.state = TupleState.DEAD;
}
}
retractQueue.clear();
}

@Override
public void propagateUpdates() {
processAndClear(updateQueue);
processAndClear(updateQueue, nextNodesTupleLifecycle::update);
}

private void processAndClear(Deque<Tuple_> dirtyQueue) {
if (dirtyQueue.isEmpty()) {
return;
}
for (var tuple : dirtyQueue) {
private static <Tuple_ extends AbstractTuple> void processAndClear(Deque<Tuple_> dirtyQueue,
Consumer<Tuple_> tupleLifecycle) {
while (!dirtyQueue.isEmpty()) {
var tuple = dirtyQueue.poll();
if (tuple.state == TupleState.DEAD) {
/*
* DEAD signifies the tuple was both in insert/update and retract queues.
* This happens when a tuple was inserted/updated and subsequently retracted, all before propagation.
* We can safely ignore the later insert/update,
* as by this point the more recent retract has already been processed,
* setting the state to DEAD.
*/
// DEAD signifies the tuple was both in insert/update and retract queues.
// This happens when a tuple was inserted/updated and subsequently retracted, all before propagation.
// We can safely ignore the later insert/update,
// as by this point the more recent retract has already been processed,
// setting the state to DEAD.
continue;
}
// Change state before propagation, so that the next node can't make decisions on the original state.
tuple.state = TupleState.OK;
if (dirtyQueue == updateQueue) {
nextNodesTupleLifecycle.update(tuple);
} else {
nextNodesTupleLifecycle.insert(tuple);
}
tupleLifecycle.accept(tuple);
}
dirtyQueue.clear();
}

@Override
public void propagateInserts() {
processAndClear(insertQueue);
processAndClear(insertQueue, nextNodesTupleLifecycle::insert);
if (!retractQueue.isEmpty()) {
throw new IllegalStateException("Impossible state: The retract queue (%s) is not empty."
.formatted(retractQueue));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,9 @@ public boolean isNeverEnding() {

@Override
public long getSize() {
long size = 0L;
for (MoveSelector<Solution_> moveSelector : childMoveSelectorList) {
long childSize = moveSelector.getSize();
var size = 0L;
for (var moveSelector : childMoveSelectorList) {
var childSize = moveSelector.getSize();
if (childSize == 0L) {
if (!ignoreEmptyChildIterators) {
return 0L;
Expand Down Expand Up @@ -82,98 +82,104 @@ public Iterator<Move<Solution_>> iterator() {

public class OriginalCartesianProductMoveIterator extends UpcomingSelectionIterator<Move<Solution_>> {

private List<Iterator<Move<Solution_>>> moveIteratorList;
private final List<Iterator<Move<Solution_>>> moveIteratorList;

private Move<Solution_>[] subSelections;

public OriginalCartesianProductMoveIterator() {
moveIteratorList = new ArrayList<>(childMoveSelectorList.size());
for (int i = 0; i < childMoveSelectorList.size(); i++) {
for (var i = 0; i < childMoveSelectorList.size(); i++) {
moveIteratorList.add(null);
}
subSelections = null;
}

@Override
protected Move<Solution_> createUpcomingSelection() {
int childSize = moveIteratorList.size();
var childSize = moveIteratorList.size();
int startingIndex;
Move<Solution_>[] moveList = new Move[childSize];
if (subSelections == null) {
startingIndex = -1;
} else {
startingIndex = childSize - 1;
while (startingIndex >= 0) {
Iterator<Move<Solution_>> moveIterator = moveIteratorList.get(startingIndex);
if (moveIterator.hasNext()) {
break;
}
startingIndex--;
}
startingIndex = findStartingIndex(childSize);
if (startingIndex < 0) {
return noUpcomingSelection();
}
// Clone to avoid CompositeMove corruption
System.arraycopy(subSelections, 0, moveList, 0, startingIndex);
moveList[startingIndex] = moveIteratorList.get(startingIndex).next(); // Increment the 4 in 004999
}
for (int i = startingIndex + 1; i < childSize; i++) { // Increment the 9s in 004999
Iterator<Move<Solution_>> moveIterator = childMoveSelectorList.get(i).iterator();
for (var i = startingIndex + 1; i < childSize; i++) { // Increment the 9s in 004999
var moveIterator = childMoveSelectorList.get(i).iterator();
moveIteratorList.set(i, moveIterator);
Move<Solution_> next;
if (!moveIterator.hasNext()) { // in case a moveIterator is empty
if (ignoreEmptyChildIterators) {
next = (Move<Solution_>) EMPTY_MARK;
moveList[i] = (Move<Solution_>) EMPTY_MARK;
} else {
return noUpcomingSelection();
}
} else {
next = moveIterator.next();
moveList[i] = moveIterator.next();
}
moveList[i] = next;
}
// No need to clone to avoid CompositeMove corruption because subSelections's elements never change
subSelections = moveList;
if (ignoreEmptyChildIterators) {
// Clone because EMPTY_MARK should survive in subSelections
Move<Solution_>[] newMoveList = new Move[childSize];
int newSize = 0;
for (int i = 0; i < childSize; i++) {
if (moveList[i] != EMPTY_MARK) {
newMoveList[newSize] = moveList[i];
newSize++;
}
return buildMove(moveList, childSize);
}

private int findStartingIndex(int childSize) {
var startingIndex = childSize - 1;
while (startingIndex >= 0) {
var moveIterator = moveIteratorList.get(startingIndex);
if (moveIterator.hasNext()) {
break;
}
if (newSize == 0) {
return noUpcomingSelection();
} else if (newSize == 1) {
return newMoveList[0];
startingIndex--;
}
return startingIndex;
}

private Move<Solution_> buildMove(Move<Solution_>[] moveList, int childSize) {
if (!ignoreEmptyChildIterators) {
return CompositeMove.buildMove(moveList);
}
// Clone because EMPTY_MARK should survive in subSelections
Move<Solution_>[] newMoveList = new Move[childSize];
var newSize = 0;
for (var i = 0; i < childSize; i++) {
if (moveList[i] != EMPTY_MARK) {
newMoveList[newSize] = moveList[i];
newSize++;
}
moveList = Arrays.copyOfRange(newMoveList, 0, newSize);
}
return CompositeMove.buildMove(moveList);
return switch (newSize) {
case 0 -> noUpcomingSelection();
case 1 -> newMoveList[0];
default -> CompositeMove.buildMove(Arrays.copyOfRange(newMoveList, 0, newSize));
};
}

}

public class RandomCartesianProductMoveIterator extends SelectionIterator<Move<Solution_>> {

private List<Iterator<Move<Solution_>>> moveIteratorList;
private final List<Iterator<Move<Solution_>>> moveIteratorList;
private Boolean empty;

public RandomCartesianProductMoveIterator() {
moveIteratorList = new ArrayList<>(childMoveSelectorList.size());
empty = null;
for (MoveSelector<Solution_> moveSelector : childMoveSelectorList) {
for (var moveSelector : childMoveSelectorList) {
moveIteratorList.add(moveSelector.iterator());
}
}

@Override
public boolean hasNext() {
if (empty == null) { // Only done in the first call
int emptyCount = 0;
for (Iterator<Move<Solution_>> moveIterator : moveIteratorList) {
var emptyCount = 0;
for (var moveIterator : moveIteratorList) {
if (!moveIterator.hasNext()) {
emptyCount++;
if (!ignoreEmptyChildIterators) {
Expand All @@ -189,11 +195,11 @@ public boolean hasNext() {
@Override
public Move<Solution_> next() {
List<Move<Solution_>> moveList = new ArrayList<>(moveIteratorList.size());
for (int i = 0; i < moveIteratorList.size(); i++) {
Iterator<Move<Solution_>> moveIterator = moveIteratorList.get(i);
boolean skip = false;
for (var i = 0; i < moveIteratorList.size(); i++) {
var moveIterator = moveIteratorList.get(i);
var skip = false;
if (!moveIterator.hasNext()) {
MoveSelector<Solution_> moveSelector = childMoveSelectorList.get(i);
var moveSelector = childMoveSelectorList.get(i);
moveIterator = moveSelector.iterator();
moveIteratorList.set(i, moveIterator);
if (!moveIterator.hasNext()) {
Expand Down
Loading
Loading