Skip to content
Merged
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 @@ -56,6 +56,24 @@ public void externalize(NextElementShadowVariableDescriptor<Solution_> shadowVar
nextExternalized = true;
}

@Override
public int getIndexOrFail(Object planningValue) {
var index = listVariableState.getIndex(planningValue);
if (index < 0) {
throw new IllegalStateException("The element (%s) is not assigned to any list variable.");
}
return index;
}

@Override
public int getIndexOrElse(Object planningValue, int defaultValue) {
var index = listVariableState.getIndex(planningValue);
if (index < 0) {
return defaultValue;
}
return index;
}

@Override
public void resetWorkingSolution(InnerScoreDirector<Solution_, ?> scoreDirector) {
workingSolution = scoreDirector.getWorkingSolution();
Expand Down Expand Up @@ -106,11 +124,6 @@ public ElementPosition getElementPosition(Object planningValue) {
return listVariableState.getElementPosition(planningValue);
}

@Override
public @Nullable Integer getIndex(Object planningValue) {
return listVariableState.getIndex(planningValue);
}

@Override
public @Nullable Object getInverseSingleton(Object planningValue) {
return listVariableState.getInverseSingleton(planningValue);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ private ChangeType processElementPosition(Object entity, Object element, int ind
return ChangeType.BOTH;
}
var oldIndex = getIndex(element);
if (oldIndex == null) { // Technically impossible, but we handle it anyway.
if (oldIndex < 0) { // Technically impossible, but we handle it anyway.
return ChangeType.BOTH;
}
return comparePositions(entity, oldEntity, index, oldIndex);
Expand Down Expand Up @@ -261,15 +261,16 @@ public ElementPosition getElementPosition(Object planningValue) {
}
}

public Integer getIndex(Object planningValue) {
public int getIndex(Object planningValue) {
if (externalizedIndexProcessor == null) {
var position = elementPositionMap.get(planningValue);
if (position == null) {
return null;
return -1;
}
return position.getIndex();
}
return externalizedIndexProcessor.getIndex(planningValue);
var indexOrNull = externalizedIndexProcessor.getIndex(planningValue);
return indexOrNull == null ? -1 : indexOrNull;
}

public Object getInverseSingleton(Object planningValue) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package ai.timefold.solver.core.impl.domain.variable;

import ai.timefold.solver.core.api.domain.variable.IndexShadowVariable;
import ai.timefold.solver.core.api.domain.variable.InverseRelationShadowVariable;
import ai.timefold.solver.core.api.domain.variable.NextElementShadowVariable;
import ai.timefold.solver.core.api.domain.variable.PlanningListVariable;
import ai.timefold.solver.core.api.domain.variable.PreviousElementShadowVariable;
import ai.timefold.solver.core.impl.domain.variable.descriptor.ListVariableDescriptor;
import ai.timefold.solver.core.impl.domain.variable.inverserelation.InverseRelationShadowVariableDescriptor;
import ai.timefold.solver.core.impl.domain.variable.listener.SourcedListVariableListener;
Expand All @@ -23,10 +27,8 @@
* it means that there is a field on an entity holding the value of the shadow variable.
* In this case, we will attempt to use that value.
* Otherwise, we will keep an internal track of all the possible shadow variables
* ({@link ai.timefold.solver.core.api.domain.variable.IndexShadowVariable},
* {@link ai.timefold.solver.core.api.domain.variable.InverseRelationShadowVariable},
* {@link ai.timefold.solver.core.api.domain.variable.PreviousElementShadowVariable},
* {@link ai.timefold.solver.core.api.domain.variable.NextElementShadowVariable}),
* ({@link IndexShadowVariable}, {@link InverseRelationShadowVariable},
* {@link PreviousElementShadowVariable}, {@link NextElementShadowVariable}),
* and use values from this internal representation.
*
* @param <Solution_>
Expand All @@ -50,26 +52,20 @@ public interface ListVariableStateSupply<Solution_, Entity_, Element_>
* Get {@code planningValue}'s index in the {@link PlanningListVariable list variable} it is an element of.
*
* @param planningValue never null
* @return {@code planningValue}'s index in the list variable it is an element of or {@code null} if the value is unassigned
* @return {@code planningValue}'s index in the list variable it is an element of
* @throws IllegalStateException if the value is unassigned
*/
@Nullable
Integer getIndex(Object planningValue);

default int getIndexOrFail(Object planningValue) {
var index = getIndex(planningValue);
if (index == null) {
throw new IllegalStateException("The element (%s) is not assigned to any list variable.");
}
return index;
}

default int getIndexOrElse(Object planningValue, int defaultValue) {
var index = getIndex(planningValue);
if (index == null) {
return defaultValue;
}
return index;
}
int getIndexOrFail(Object planningValue);

/**
* Get {@code planningValue}'s index in the {@link PlanningListVariable list variable} it is an element of.
*
* @param planningValue never null
* @param defaultValue the value to return if {@code planningValue} is unassigned
* @return {@code planningValue}'s index in the list variable it is an element of or {@code defaultValue} if the value is
* unassigned
*/
int getIndexOrElse(Object planningValue, int defaultValue);

/**
* If entity1.varA = x then the inverse of x is entity1.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package ai.timefold.solver.core.impl.heuristic.selector.move.generic.list.kopt;

import java.util.function.Function;
import java.util.function.ToIntFunction;

import ai.timefold.solver.core.impl.domain.variable.IndexShadowVariableDescriptor;
import ai.timefold.solver.core.impl.domain.variable.ListElementsChangeEvent;
Expand All @@ -17,7 +17,9 @@

@NullMarked
record DelegatingListVariableStateSupply<Solution_>(ListVariableStateSupply<Solution_, Object, Object> delegate,
Function<Object, @Nullable Integer> indexFunction) implements ListVariableStateSupply<Solution_, Object, Object> {
ToIntFunction<Object> indexFunction)
implements
ListVariableStateSupply<Solution_, Object, Object> {

@Override
public void externalize(IndexShadowVariableDescriptor<Solution_> shadowVariableDescriptor) {
Expand All @@ -40,8 +42,21 @@ public void externalize(NextElementShadowVariableDescriptor<Solution_> shadowVar
}

@Override
public @Nullable Integer getIndex(Object planningValue) {
return indexFunction.apply(planningValue);
public int getIndexOrFail(Object planningValue) {
var index = indexFunction.applyAsInt(planningValue);
if (index < 0) {
throw new IllegalStateException("The element (%s) is not assigned to any list variable.");
}
return index;
}

@Override
public int getIndexOrElse(Object planningValue, int defaultValue) {
var index = indexFunction.applyAsInt(planningValue);
if (index < 0) {
return defaultValue;
}
return index;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ private KOptMoveInfo setupValidOddSequentialKOptMove(KOptListMoveIteratorMockDat
when(mocks.listVariableStateSupply.getElementPosition(entityList.get(i)))
.thenReturn(ElementPosition.of(entity, i));
when(mocks.listVariableStateSupply.getInverseSingleton(entityList.get(i))).thenReturn(entity);
when(mocks.listVariableStateSupply.getIndex(entityList.get(i))).thenReturn(i);
when(mocks.listVariableStateSupply.getIndexOrFail(entityList.get(i))).thenReturn(i);
when(mocks.listVariableStateSupply.getSourceVariableDescriptor()).thenReturn(mocks.listVariableDescriptor);
}
when(mocks.listVariableDescriptor.getListSize(entity)).thenReturn(entityList.size());
Expand Down Expand Up @@ -251,7 +251,7 @@ private KOptMoveInfo setupValidNonsequential4OptMove(KOptListMoveIteratorMockDat
when(mocks.listVariableStateSupply.getElementPosition(entityList.get(i)))
.thenReturn(ElementPosition.of(entity, i));
when(mocks.listVariableStateSupply.getInverseSingleton(entityList.get(i))).thenReturn(entity);
when(mocks.listVariableStateSupply.getIndex(entityList.get(i))).thenReturn(i);
when(mocks.listVariableStateSupply.getIndexOrFail(entityList.get(i))).thenReturn(i);
when(mocks.listVariableStateSupply.getSourceVariableDescriptor()).thenReturn(mocks.listVariableDescriptor);
}
when(mocks.listVariableDescriptor.getListSize(entity)).thenReturn(entityList.size());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -260,8 +260,8 @@ private static KOptDescriptor<TestdataListValue> fromRemovedAndAddedEdges(
return new KOptDescriptor<>(tourArray,
incl,
item -> originalTour.get((originalTour.indexOf(item) + 1) % originalTour.size()),
getBetweenPredicate(
new DelegatingListVariableStateSupply<>(mock(ListVariableStateSupply.class), originalTour::indexOf)));
getBetweenPredicate(new DelegatingListVariableStateSupply<Object>(mock(ListVariableStateSupply.class),
originalTour::indexOf)));
}

private static int identityIndexOf(List<TestdataListValue> sourceList, TestdataListValue query) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -718,7 +718,7 @@ private static <Node_> Function<Node_, Node_> getSuccessorFunction(ListVariableD
return node -> {
var entity = listVariableStateSupply.getInverseSingleton(node);
var valueList = (List<Node_>) listVariableDescriptor.getValue(entity);
var index = listVariableStateSupply.getIndex(node);
var index = listVariableStateSupply.getIndexOrFail(node);
if (index == valueList.size() - 1) {
var firstUnpinnedIndex = listVariableDescriptor.getFirstUnpinnedIndex(entity);
return valueList.get(firstUnpinnedIndex);
Expand Down
Loading