Skip to content

Commit

Permalink
Bye bye partial
Browse files Browse the repository at this point in the history
  • Loading branch information
triceo committed Jan 22, 2024
1 parent 0d5713b commit 1d4850c
Show file tree
Hide file tree
Showing 30 changed files with 345 additions and 342 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
*
* @see PlanningVariable#allowsUnassigned() Basic planning value equivalent.
*/
boolean partial() default false;
boolean allowsUnassignedValues() default false;

String[] valueRangeProviderRefs() default {};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
* Allowing unassigned is not compatible with {@link PlanningVariableGraphType#CHAINED} true.
* Allowing unassigned is not compatible with a primitive property type.
*
* @see PlanningListVariable#partial() Partial list variable is a list variable equivalent.
* @see PlanningListVariable#allowsUnassignedValues()
* @return true if null is a valid value for this planning variable
*/
boolean allowsUnassigned() default false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public interface ConstraintFactory {
* {@link UniConstraintStream#filter(Predicate) filtered} to only contain entities
* for which each genuine {@link PlanningVariable} (of the sourceClass or a superclass thereof) has a non-null value.
* <p>
* This does not apply to {@link PlanningListVariable#partial()}.
* This does not apply to {@link PlanningListVariable#allowsUnassignedValues()}.
* Elements can be unassigned (not present in any list) and this method will still include them.
* This situation needs to be handled by adding a {@link InverseRelationShadowVariable} on the element,
* exposing it via a new method such as {@code getUnassigned()},
Expand All @@ -55,7 +55,7 @@ public interface ConstraintFactory {
* &#64;PlanningEntity
* public class Vehicle {
*
* &#64;PlanningListVariable(partial = true)
* &#64;PlanningListVariable(allowsUnassignedValues = true)
* List<Customer> customerList = ...;
*
* ...
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1073,7 +1073,7 @@ public SolutionInitializationStatistics computeInitializationStatistics(Solution
var genuineEntityCount = new MutableInt();
var shadowEntityCount = new MutableInt();
for (var listVariableDescriptor : listVariableDescriptors) {
if (listVariableDescriptor.allowsUnassigned()) { // Unassigned elements of partial vars count as assigned.
if (listVariableDescriptor.allowsUnassigned()) { // Unassigned elements count as assigned.
continue;
}
unassignedValueCount.add((int) listVariableDescriptor.getValueRangeSize(solution, null));
Expand All @@ -1097,7 +1097,7 @@ public SolutionInitializationStatistics computeInitializationStatistics(Solution
return;
}
for (var listVariableDescriptor : listVariableDescriptors) {
if (listVariableDescriptor.allowsUnassigned()) { // Unassigned elements of partial vars count as assigned.
if (listVariableDescriptor.allowsUnassigned()) { // Unassigned elements count as assigned.
continue;
}
var listVariableEntityDescriptor = listVariableDescriptor.getEntityDescriptor();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ public final boolean isChained() {

public final boolean allowsUnassigned() {
if (this instanceof ListVariableDescriptor<Solution_> listVariableDescriptor) {
return listVariableDescriptor.partial;
return listVariableDescriptor.allowsUnassignedValues;
} else if (this instanceof BasicVariableDescriptor<Solution_> basicVariableDescriptor) {
return basicVariableDescriptor.allowsUnassigned;
} else {
Expand Down Expand Up @@ -258,7 +258,8 @@ public boolean isValueRangeEntityIndependent() {

/**
* A basic planning variable {@link PlanningVariable#allowsUnassigned() allowing unassigned}
* and {@link PlanningListVariable#partial() partial list variable} are always considered initialized.
* and {@link PlanningListVariable#allowsUnassignedValues() allowing unassigned values}
* are always considered initialized.
*
* @param entity never null
* @return true if the variable on that entity is initialized
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
public final class ListVariableDescriptor<Solution_> extends GenuineVariableDescriptor<Solution_> {

private final ListVariableDataDemand<Solution_> providedDemand = new ListVariableDataDemand<>(this);
boolean partial = true;
boolean allowsUnassignedValues = true;

// ************************************************************************
// Constructors and simple getters/setters
Expand All @@ -35,7 +35,7 @@ public ListVariableDescriptor(int ordinal, EntityDescriptor<Solution_> entityDes
@Override
protected void processPropertyAnnotations(DescriptorPolicy descriptorPolicy) {
PlanningListVariable planningVariableAnnotation = variableMemberAccessor.getAnnotation(PlanningListVariable.class);
partial = planningVariableAnnotation.partial();
allowsUnassignedValues = planningVariableAnnotation.allowsUnassignedValues();
processValueRangeRefs(descriptorPolicy, planningVariableAnnotation.valueRangeProviderRefs());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,14 +87,14 @@ private EntityIndependentValueSelector<Solution_> getEffectiveValueSelector() {
@Override
public Iterator<ElementLocation> iterator() {
if (randomSelection) {
var isPartial = listVariableDescriptor.allowsUnassigned();
var allowsUnassignedValues = listVariableDescriptor.allowsUnassigned();
var entityIterator = entitySelector.iterator();

// In case of partial list variable, we need to exclude unassigned elements.
// In case of list var which allows unassigned values, we need to exclude unassigned elements.
var totalValueSize = getEffectiveValueSelector().getSize()
- (isPartial ? listVariableDataSupply.countNotAssigned() : 0);
- (allowsUnassignedValues ? listVariableDataSupply.countNotAssigned() : 0);
var totalSize = Math.addExact(entitySelector.getSize(), totalValueSize);
return new ElementLocationRandomIterator(entityIterator, totalSize, isPartial);
return new ElementLocationRandomIterator(entityIterator, totalSize, allowsUnassignedValues);
} else {
if (entitySelector.getSize() == 0) {
return Collections.emptyIterator();
Expand All @@ -116,7 +116,7 @@ public Iterator<ElementLocation> iterator() {
: Stream.empty())
.map(locationInList -> ElementLocation.of(locationInList.entity(), locationInList.index() + 1)));
if (listVariableDescriptor.allowsUnassigned()) {
// If the list variable is partial, add the option of unassigning.
// If the list variable allows unassigned values, add the option of unassigning.
stream = Stream.concat(stream, Stream.of(ElementLocation.unassigned()));
}
return stream.iterator();
Expand Down Expand Up @@ -178,21 +178,22 @@ public String toString() {
private final class ElementLocationRandomIterator implements Iterator<ElementLocation> {
private final Iterator<Object> entityIterator;
private final long totalSize;
private final boolean isPartial;
private final boolean allowsUnassignedValues;
private Iterator<Object> valueIterator;

public ElementLocationRandomIterator(Iterator<Object> entityIterator, long totalSize, boolean isPartial) {
public ElementLocationRandomIterator(Iterator<Object> entityIterator, long totalSize, boolean allowsUnassignedValues) {
this.entityIterator = entityIterator;
this.totalSize = totalSize;
this.isPartial = isPartial;
this.valueIterator = getValueIterator(isPartial);
this.allowsUnassignedValues = allowsUnassignedValues;
this.valueIterator = getValueIterator(allowsUnassignedValues);
}

private Iterator<Object> getValueIterator(boolean isPartial) {
private Iterator<Object> getValueIterator(boolean allowsUnassignedValues) {
var effectiveValueSelector = getEffectiveValueSelector();
if (isPartial) {
if (allowsUnassignedValues) {
/*
* In case of a partial list variable, unassigned elements will show up in the valueSelector.
* In case of list variable that allows unassigned values,
* unassigned elements will show up in the valueSelector.
* These skew the selection probability, so we need to exclude them.
* The option to unassign needs to be added to the iterator once later,
* to make sure that it can get selected.
Expand Down Expand Up @@ -225,9 +226,9 @@ public boolean hasNext() {
@Override
public ElementLocation next() {
var entitySize = entitySelector.getSize();
var entityBoundary = isPartial ? entitySize + 1 : entitySize;
var entityBoundary = allowsUnassignedValues ? entitySize + 1 : entitySize;
long random = RandomUtils.nextLong(workingRandom, totalSize);
if (isPartial && random == 0) {
if (allowsUnassignedValues && random == 0) {
/*
* We have already excluded all unassigned elements,
* the only way to get an unassigned destination is to explicitly add it.
Expand All @@ -241,7 +242,7 @@ public ElementLocation next() {
return new LocationInList(entity, entityDescriptor.extractFirstUnpinnedIndex(entity));
} else { // Value selector already returns only unpinned values.
if (!valueIterator.hasNext()) {
valueIterator = getValueIterator(isPartial);
valueIterator = getValueIterator(allowsUnassignedValues);
}
var value = valueIterator.next();
var elementLocation = (LocationInList) listVariableDataSupply.getLocationInList(value);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
/**
* A supertype for {@link LocationInList} and {@link UnassignedLocation}.
* <p>
* {@link PlanningListVariable#partial()} introduces {@link UnassignedLocation},
* {@link PlanningListVariable#allowsUnassignedValues()} introduces {@link UnassignedLocation},
* and the handling of unassigned values is brittle.
* These values may leak into code which expects {@link LocationInList} instead.
* Therefore, we use {@link ElementLocation} and we force calling code to cast to either of the two subtypes.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,32 +67,27 @@ static <Solution_> Move<Solution_> buildSwapMove(ListVariableDescriptor<Solution
}
boolean leftUnassigned = upcomingLeft instanceof UnassignedLocation;
boolean rightUnassigned = upcomingRight instanceof UnassignedLocation;
if (leftUnassigned && rightUnassigned) {
// No need to swap two unassigned elements in a partial list var.
if (leftUnassigned && rightUnassigned) { // No need to swap two unassigned elements.
return NoChangeMove.getInstance();
} else if (leftUnassigned) {
// Unassign right, put left where right used to be.
} else if (leftUnassigned) { // Unassign right, put left where right used to be.
var rightDestination = (LocationInList) upcomingRight;
var unassignMove =
new ListUnassignMove<>(listVariableDescriptor, rightDestination.entity(), rightDestination.index());
var assignMove = new ListAssignMove<>(listVariableDescriptor, upcomingLeftValue, rightDestination.entity(),
rightDestination.index());
return CompositeMove.buildMove(unassignMove, assignMove);
} else if (rightUnassigned) { // Unassign left, put right where left used to be.
var leftDestination = (LocationInList) upcomingLeft;
var unassignMove =
new ListUnassignMove<>(listVariableDescriptor, leftDestination.entity(), leftDestination.index());
var assignMove = new ListAssignMove<>(listVariableDescriptor, upcomingRightValue, leftDestination.entity(),
leftDestination.index());
return CompositeMove.buildMove(unassignMove, assignMove);
} else {
if (rightUnassigned) {
// Unassign left, put right where left used to be.
var leftDestination = (LocationInList) upcomingLeft;
var unassignMove =
new ListUnassignMove<>(listVariableDescriptor, leftDestination.entity(), leftDestination.index());
var assignMove = new ListAssignMove<>(listVariableDescriptor, upcomingRightValue, leftDestination.entity(),
leftDestination.index());
return CompositeMove.buildMove(unassignMove, assignMove);
} else {
var leftDestination = (LocationInList) upcomingLeft;
var rightDestination = (LocationInList) upcomingRight;
return new ListSwapMove<>(listVariableDescriptor, leftDestination.entity(), leftDestination.index(),
rightDestination.entity(), rightDestination.index());
}
var leftDestination = (LocationInList) upcomingLeft;
var rightDestination = (LocationInList) upcomingRight;
return new ListSwapMove<>(listVariableDescriptor, leftDestination.entity(), leftDestination.index(),
rightDestination.entity(), rightDestination.index());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ private EntityIndependentValueSelector<Solution_> filterNotAssignedValues(
if (!listVariableDescriptor.allowsUnassigned()) {
return entityIndependentValueSelector;
}
// In case of partial list var, we need to filter out unassigned vars.
// We need to filter out unassigned vars.
return (EntityIndependentValueSelector<Solution_>) FilteringValueSelector.create(entityIndependentValueSelector,
(scoreDirector, selection) -> listVariableDataSupply.getState(selection) == ASSIGNED);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -484,7 +484,7 @@ public void beforeListVariableElementAssigned(ListVariableDescriptor<Solution_>

@Override
public void afterListVariableElementAssigned(ListVariableDescriptor<Solution_> variableDescriptor, Object element) {
if (!variableDescriptor.allowsUnassigned()) { // Partial variables don't count towards the initScore.
if (!variableDescriptor.allowsUnassigned()) { // Unassigned elements don't count towards the initScore here.
workingInitScore++;
assertInitScoreZeroOrLess();
}
Expand All @@ -498,7 +498,7 @@ public void beforeListVariableElementUnassigned(ListVariableDescriptor<Solution_

@Override
public void afterListVariableElementUnassigned(ListVariableDescriptor<Solution_> variableDescriptor, Object element) {
if (!variableDescriptor.allowsUnassigned()) { // Partial variables don't count towards the initScore.
if (!variableDescriptor.allowsUnassigned()) { // Unassigned elements don't count towards the initScore here.
workingInitScore--;
}
variableListenerSupport.afterElementUnassigned(variableDescriptor, element);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,39 +25,31 @@ public interface VariableDescriptorAwareScoreDirector<Solution_>
// ************************************************************************

/**
* Call this for each element that will be initialized during construction heuristics,
* without being assigned to any list variable.
* (Partial list variable.)
* Call this for each element that will be initialized during construction heuristics.
*
* @param variableDescriptor the list variable descriptor
* @param element the initialized element
*/
void beforeListVariableElementInitialized(ListVariableDescriptor<Solution_> variableDescriptor, Object element);

/**
* Call this for each element that will be initialized during construction heuristics,
* without being assigned to any list variable.
* (Partial list variable + CH change move.)
* Call this for each element that will be initialized during construction heuristics.
*
* @param variableDescriptor the list variable descriptor
* @param element the initialized element
*/
void afterListVariableElementInitialized(ListVariableDescriptor<Solution_> variableDescriptor, Object element);

/**
* Call this for each element that will be uninitialized during construction heuristics,
* without being assigned to any list variable.
* (Partial list variable + CH change move undo.)
* Call this for each element that will be uninitialized during construction heuristics.
*
* @param variableDescriptor the list variable descriptor
* @param element the uninitialized element
*/
void beforeListVariableElementUninitialized(ListVariableDescriptor<Solution_> variableDescriptor, Object element);

/**
* Call this for each element that will be uninitialized during construction heuristics,
* without being assigned to any list variable.
* (Partial list variable, undo move.)
* Call this for each element that will be uninitialized during construction heuristics.
*
* @param variableDescriptor the list variable descriptor
* @param element the initialized element
Expand Down
Loading

0 comments on commit 1d4850c

Please sign in to comment.