Skip to content
Draft
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 @@ -7,17 +7,22 @@
import java.util.Map;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.function.Predicate;

import ai.timefold.solver.core.impl.bavet.common.tuple.Tuple;
import ai.timefold.solver.core.impl.bavet.common.tuple.TupleLifecycle;
import ai.timefold.solver.core.impl.bavet.common.tuple.TupleState;

import org.jspecify.annotations.Nullable;

public abstract class AbstractFlattenNode<InTuple_ extends Tuple, OutTuple_ extends Tuple, FlattenedItem_>
extends AbstractSingleInputNode<InTuple_> {

private final int flattenStoreIndex;
private final StaticPropagationQueue<OutTuple_> propagationQueue;
private final Consumer<OutTuple_> removeTupleConsumer = this::removeTuple;
private final Predicate<FlattenItemBag<FlattenedItem_, OutTuple_>> removeExtrasPredicate =
bag -> bag.removeExtras(removeTupleConsumer);

protected AbstractFlattenNode(int flattenStoreIndex, TupleLifecycle<OutTuple_> nextNodesTupleLifecycle) {
super(nextNodesTupleLifecycle);
Expand Down Expand Up @@ -64,10 +69,15 @@ public final void insert(InTuple_ tuple) {

private void addTuple(InTuple_ originalTuple, FlattenedItem_ item,
FlattenBagByItem<FlattenedItem_, OutTuple_> bagByItem) {
var outTupleBag = bagByItem.getBag(item);
outTupleBag.add(() -> createTuple(originalTuple, outTupleBag.value),
propagationQueue::insert,
propagationQueue::update);
var bag = bagByItem.getBag(item);
var reuse = bag.reuseOrAdvance();
if (reuse == null) {
var created = createTuple(originalTuple, bag.value);
bag.append(created);
propagationQueue.insert(created);
} else {
propagationQueue.update(reuse);
}
}

protected abstract OutTuple_ createTuple(InTuple_ originalTuple, FlattenedItem_ item);
Expand All @@ -86,7 +96,7 @@ public final void update(InTuple_ tuple) {
addTuple(tuple, item, bagByItem);
}
bagByItem.getAllBags()
.removeIf(bag -> bag.removeExtras(this::removeTuple));
.removeIf(removeExtrasPredicate);
}

protected abstract Iterable<FlattenedItem_> extractIterable(InTuple_ tuple);
Expand All @@ -98,7 +108,7 @@ public final void retract(InTuple_ tuple) {
// No fail fast if null because we don't track which tuples made it through the filter predicate(s)
return;
}
bagByItem.applyToAll(this::removeTuple);
bagByItem.applyToAll(removeTupleConsumer);
}

private void removeTuple(OutTuple_ outTuple) {
Expand Down Expand Up @@ -165,21 +175,19 @@ private static final class FlattenItemBag<FlattenedItem_, OutTuple_> {

/**
* Increments {@link #newCount}.
* If the updated {@link #newCount} is less than or equal to the size of {@link #outTupleList},
* the {@code updateConsumer} is called with the corresponding element from
* {@link #outTupleList}.
* Otherwise, the {@code insertConsumer} is called with a new tuple created
* with {@code outTupleSupplier}, and that tuple is added to {@link #outTupleList}.
*
* @return the existing tuple to reuse if {@link #newCount} is within the current size of
* {@link #outTupleList}, or {@code null} if a new tuple must be created and passed
* to {@link #append}.
*/
void add(Supplier<OutTuple_> outTupleSupplier, Consumer<OutTuple_> insertConsumer, Consumer<OutTuple_> updateConsumer) {
@Nullable
OutTuple_ reuseOrAdvance() {
var listIndex = newCount++;
if (newCount > outTupleList.size()) {
var inserted = outTupleSupplier.get();
outTupleList.add(inserted);
insertConsumer.accept(inserted);
} else {
updateConsumer.accept(outTupleList.get(listIndex));
}
return newCount > outTupleList.size() ? null : outTupleList.get(listIndex);
}

void append(OutTuple_ created) {
outTupleList.add(created);
}

/**
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@
import ai.timefold.solver.core.impl.domain.variable.descriptor.ListVariableDescriptor;
import ai.timefold.solver.core.impl.heuristic.selector.AbstractSelector;
import ai.timefold.solver.core.impl.heuristic.selector.common.iterator.ConcatenatingIterator;
import ai.timefold.solver.core.impl.heuristic.selector.common.iterator.MappingIterator;
import ai.timefold.solver.core.impl.heuristic.selector.entity.EntitySelector;
import ai.timefold.solver.core.impl.heuristic.selector.value.IterableValueSelector;
import ai.timefold.solver.core.impl.heuristic.selector.value.decorator.FilteringValueSelector;
import ai.timefold.solver.core.impl.solver.scope.SolverScope;
import ai.timefold.solver.core.impl.util.MappingIterator;
import ai.timefold.solver.core.preview.api.domain.metamodel.ElementPosition;
import ai.timefold.solver.core.preview.api.domain.metamodel.PositionInList;

Expand Down
Loading
Loading