Skip to content

Commit

Permalink
fix: unassigned location is the last element destination
Browse files Browse the repository at this point in the history
  • Loading branch information
triceo committed Apr 18, 2024
1 parent d8a98a1 commit b5ff711
Show file tree
Hide file tree
Showing 12 changed files with 110 additions and 74 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -97,14 +97,10 @@ public Iterator<ElementLocation> iterator() {
if (entitySelector.getSize() == 0) {
return Collections.emptyIterator();
}
// If the list variable allows unassigned values, add the option of unassigning.
var stream = listVariableDescriptor.allowsUnassignedValues() ? Stream.of(ElementLocation.unassigned())
: Stream.<ElementLocation> empty();
// Start with the first unpinned value of each entity, or zero if no pinning.
// Entity selector is guaranteed to return only unpinned entities.
stream = Stream.concat(stream,
StreamSupport.stream(entitySelector.spliterator(), false)
.map(entity -> ElementLocation.of(entity, listVariableDescriptor.getFirstUnpinnedIndex(entity))));
Stream<ElementLocation> stream = StreamSupport.stream(entitySelector.spliterator(), false)
.map(entity -> ElementLocation.of(entity, listVariableDescriptor.getFirstUnpinnedIndex(entity)));
// Filter guarantees that we only get values that are actually in one of the lists.
// Value selector guarantees only unpinned values.
stream = Stream.concat(stream,
Expand All @@ -114,7 +110,11 @@ public Iterator<ElementLocation> iterator() {
? Stream.of(locationInList)
: Stream.empty())
.map(locationInList -> ElementLocation.of(locationInList.entity(), locationInList.index() + 1)));
return (Iterator<ElementLocation>) stream.iterator();
// If the list variable allows unassigned values, add the option of unassigning.
if (listVariableDescriptor.allowsUnassignedValues()) {
stream = Stream.concat(stream, Stream.of(ElementLocation.unassigned()));
}
return stream.iterator();
}
}

Expand All @@ -137,7 +137,7 @@ public EntityDescriptor<Solution_> getEntityDescriptor() {
}

public Iterator<Object> endingIterator() {
EntityIndependentValueSelector<Solution_> effectiveValueSelector = getEffectiveValueSelector();
var effectiveValueSelector = getEffectiveValueSelector();
return Stream.concat(
StreamSupport.stream(Spliterators.spliterator(entitySelector.endingIterator(),
entitySelector.getSize(), 0), false),
Expand All @@ -148,14 +148,8 @@ public Iterator<Object> endingIterator() {

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
ElementDestinationSelector<?> that = (ElementDestinationSelector<?>) o;
return randomSelection == that.randomSelection
return o instanceof ElementDestinationSelector<?> that
&& randomSelection == that.randomSelection
&& Objects.equals(entitySelector, that.entitySelector)
&& Objects.equals(valueSelector, that.valueSelector);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ public BendableLongScore createScoreUninitialized(int initScore, long... scores)
@Override
public BendableLongScore buildOptimisticBound(InitializingScoreTrend initializingScoreTrend,
BendableLongScore score) {
InitializingScoreTrendLevel[] trendLevels = initializingScoreTrend.getTrendLevels();
InitializingScoreTrendLevel[] trendLevels = initializingScoreTrend.trendLevels();
long[] hardScores = new long[hardLevelsSize];
for (int i = 0; i < hardLevelsSize; i++) {
hardScores[i] = (trendLevels[i] == InitializingScoreTrendLevel.ONLY_DOWN)
Expand All @@ -108,7 +108,7 @@ public BendableLongScore buildOptimisticBound(InitializingScoreTrend initializin
@Override
public BendableLongScore buildPessimisticBound(InitializingScoreTrend initializingScoreTrend,
BendableLongScore score) {
InitializingScoreTrendLevel[] trendLevels = initializingScoreTrend.getTrendLevels();
InitializingScoreTrendLevel[] trendLevels = initializingScoreTrend.trendLevels();
long[] hardScores = new long[hardLevelsSize];
for (int i = 0; i < hardLevelsSize; i++) {
hardScores[i] = (trendLevels[i] == InitializingScoreTrendLevel.ONLY_UP)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ public BendableScore createScoreUninitialized(int initScore, int... scores) {

@Override
public BendableScore buildOptimisticBound(InitializingScoreTrend initializingScoreTrend, BendableScore score) {
InitializingScoreTrendLevel[] trendLevels = initializingScoreTrend.getTrendLevels();
InitializingScoreTrendLevel[] trendLevels = initializingScoreTrend.trendLevels();
int[] hardScores = new int[hardLevelsSize];
for (int i = 0; i < hardLevelsSize; i++) {
hardScores[i] = (trendLevels[i] == InitializingScoreTrendLevel.ONLY_DOWN)
Expand All @@ -106,7 +106,7 @@ public BendableScore buildOptimisticBound(InitializingScoreTrend initializingSco

@Override
public BendableScore buildPessimisticBound(InitializingScoreTrend initializingScoreTrend, BendableScore score) {
InitializingScoreTrendLevel[] trendLevels = initializingScoreTrend.getTrendLevels();
InitializingScoreTrendLevel[] trendLevels = initializingScoreTrend.trendLevels();
int[] hardScores = new int[hardLevelsSize];
for (int i = 0; i < hardLevelsSize; i++) {
hardScores[i] = (trendLevels[i] == InitializingScoreTrendLevel.ONLY_UP)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public HardMediumSoftLongScore fromLevelNumbers(int initScore, Number[] levelNum
@Override
public HardMediumSoftLongScore buildOptimisticBound(InitializingScoreTrend initializingScoreTrend,
HardMediumSoftLongScore score) {
InitializingScoreTrendLevel[] trendLevels = initializingScoreTrend.getTrendLevels();
InitializingScoreTrendLevel[] trendLevels = initializingScoreTrend.trendLevels();
return HardMediumSoftLongScore.ofUninitialized(0,
trendLevels[0] == InitializingScoreTrendLevel.ONLY_DOWN ? score.hardScore() : Long.MAX_VALUE,
trendLevels[1] == InitializingScoreTrendLevel.ONLY_DOWN ? score.mediumScore() : Long.MAX_VALUE,
Expand All @@ -70,7 +70,7 @@ public HardMediumSoftLongScore buildOptimisticBound(InitializingScoreTrend initi
@Override
public HardMediumSoftLongScore buildPessimisticBound(InitializingScoreTrend initializingScoreTrend,
HardMediumSoftLongScore score) {
InitializingScoreTrendLevel[] trendLevels = initializingScoreTrend.getTrendLevels();
InitializingScoreTrendLevel[] trendLevels = initializingScoreTrend.trendLevels();
return HardMediumSoftLongScore.ofUninitialized(0,
trendLevels[0] == InitializingScoreTrendLevel.ONLY_UP ? score.hardScore() : Long.MIN_VALUE,
trendLevels[1] == InitializingScoreTrendLevel.ONLY_UP ? score.mediumScore() : Long.MIN_VALUE,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public HardMediumSoftScore fromLevelNumbers(int initScore, Number[] levelNumbers
@Override
public HardMediumSoftScore buildOptimisticBound(InitializingScoreTrend initializingScoreTrend,
HardMediumSoftScore score) {
InitializingScoreTrendLevel[] trendLevels = initializingScoreTrend.getTrendLevels();
InitializingScoreTrendLevel[] trendLevels = initializingScoreTrend.trendLevels();
return HardMediumSoftScore.ofUninitialized(0,
trendLevels[0] == InitializingScoreTrendLevel.ONLY_DOWN ? score.hardScore() : Integer.MAX_VALUE,
trendLevels[1] == InitializingScoreTrendLevel.ONLY_DOWN ? score.mediumScore() : Integer.MAX_VALUE,
Expand All @@ -70,7 +70,7 @@ public HardMediumSoftScore buildOptimisticBound(InitializingScoreTrend initializ
@Override
public HardMediumSoftScore buildPessimisticBound(InitializingScoreTrend initializingScoreTrend,
HardMediumSoftScore score) {
InitializingScoreTrendLevel[] trendLevels = initializingScoreTrend.getTrendLevels();
InitializingScoreTrendLevel[] trendLevels = initializingScoreTrend.trendLevels();
return HardMediumSoftScore.ofUninitialized(0,
trendLevels[0] == InitializingScoreTrendLevel.ONLY_UP ? score.hardScore() : Integer.MIN_VALUE,
trendLevels[1] == InitializingScoreTrendLevel.ONLY_UP ? score.mediumScore() : Integer.MIN_VALUE,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public HardSoftLongScore fromLevelNumbers(int initScore, Number[] levelNumbers)
@Override
public HardSoftLongScore buildOptimisticBound(InitializingScoreTrend initializingScoreTrend,
HardSoftLongScore score) {
InitializingScoreTrendLevel[] trendLevels = initializingScoreTrend.getTrendLevels();
InitializingScoreTrendLevel[] trendLevels = initializingScoreTrend.trendLevels();
return HardSoftLongScore.ofUninitialized(0,
trendLevels[0] == InitializingScoreTrendLevel.ONLY_DOWN ? score.hardScore() : Long.MAX_VALUE,
trendLevels[1] == InitializingScoreTrendLevel.ONLY_DOWN ? score.softScore() : Long.MAX_VALUE);
Expand All @@ -68,7 +68,7 @@ public HardSoftLongScore buildOptimisticBound(InitializingScoreTrend initializin
@Override
public HardSoftLongScore buildPessimisticBound(InitializingScoreTrend initializingScoreTrend,
HardSoftLongScore score) {
InitializingScoreTrendLevel[] trendLevels = initializingScoreTrend.getTrendLevels();
InitializingScoreTrendLevel[] trendLevels = initializingScoreTrend.trendLevels();
return HardSoftLongScore.ofUninitialized(0,
trendLevels[0] == InitializingScoreTrendLevel.ONLY_UP ? score.hardScore() : Long.MIN_VALUE,
trendLevels[1] == InitializingScoreTrendLevel.ONLY_UP ? score.softScore() : Long.MIN_VALUE);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,15 @@ public HardSoftScore fromLevelNumbers(int initScore, Number[] levelNumbers) {

@Override
public HardSoftScore buildOptimisticBound(InitializingScoreTrend initializingScoreTrend, HardSoftScore score) {
InitializingScoreTrendLevel[] trendLevels = initializingScoreTrend.getTrendLevels();
InitializingScoreTrendLevel[] trendLevels = initializingScoreTrend.trendLevels();
return HardSoftScore.ofUninitialized(0,
trendLevels[0] == InitializingScoreTrendLevel.ONLY_DOWN ? score.hardScore() : Integer.MAX_VALUE,
trendLevels[1] == InitializingScoreTrendLevel.ONLY_DOWN ? score.softScore() : Integer.MAX_VALUE);
}

@Override
public HardSoftScore buildPessimisticBound(InitializingScoreTrend initializingScoreTrend, HardSoftScore score) {
InitializingScoreTrendLevel[] trendLevels = initializingScoreTrend.getTrendLevels();
InitializingScoreTrendLevel[] trendLevels = initializingScoreTrend.trendLevels();
return HardSoftScore.ofUninitialized(0,
trendLevels[0] == InitializingScoreTrendLevel.ONLY_UP ? score.hardScore() : Integer.MIN_VALUE,
trendLevels[1] == InitializingScoreTrendLevel.ONLY_UP ? score.softScore() : Integer.MIN_VALUE);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,14 @@ public SimpleLongScore fromLevelNumbers(int initScore, Number[] levelNumbers) {

@Override
public SimpleLongScore buildOptimisticBound(InitializingScoreTrend initializingScoreTrend, SimpleLongScore score) {
InitializingScoreTrendLevel[] trendLevels = initializingScoreTrend.getTrendLevels();
InitializingScoreTrendLevel[] trendLevels = initializingScoreTrend.trendLevels();
return SimpleLongScore.ofUninitialized(0,
trendLevels[0] == InitializingScoreTrendLevel.ONLY_DOWN ? score.score() : Long.MAX_VALUE);
}

@Override
public SimpleLongScore buildPessimisticBound(InitializingScoreTrend initializingScoreTrend, SimpleLongScore score) {
InitializingScoreTrendLevel[] trendLevels = initializingScoreTrend.getTrendLevels();
InitializingScoreTrendLevel[] trendLevels = initializingScoreTrend.trendLevels();
return SimpleLongScore.ofUninitialized(0,
trendLevels[0] == InitializingScoreTrendLevel.ONLY_UP ? score.score() : Long.MIN_VALUE);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,14 +58,14 @@ public SimpleScore fromLevelNumbers(int initScore, Number[] levelNumbers) {

@Override
public SimpleScore buildOptimisticBound(InitializingScoreTrend initializingScoreTrend, SimpleScore score) {
InitializingScoreTrendLevel[] trendLevels = initializingScoreTrend.getTrendLevels();
InitializingScoreTrendLevel[] trendLevels = initializingScoreTrend.trendLevels();
return SimpleScore.ofUninitialized(0,
trendLevels[0] == InitializingScoreTrendLevel.ONLY_DOWN ? score.score() : Integer.MAX_VALUE);
}

@Override
public SimpleScore buildPessimisticBound(InitializingScoreTrend initializingScoreTrend, SimpleScore score) {
InitializingScoreTrendLevel[] trendLevels = initializingScoreTrend.getTrendLevels();
InitializingScoreTrendLevel[] trendLevels = initializingScoreTrend.trendLevels();
return SimpleScore.ofUninitialized(0,
trendLevels[0] == InitializingScoreTrendLevel.ONLY_UP ? score.score() : Integer.MIN_VALUE);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package ai.timefold.solver.core.impl.score.trend;

import java.util.Arrays;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import ai.timefold.solver.core.api.domain.solution.PlanningSolution;
import ai.timefold.solver.core.api.score.Score;
import ai.timefold.solver.core.config.score.trend.InitializingScoreTrendLevel;
Expand All @@ -10,58 +14,35 @@
*
* @see InitializingScoreTrendLevel
*/
public class InitializingScoreTrend {
public record InitializingScoreTrend(InitializingScoreTrendLevel[] trendLevels) {

public static InitializingScoreTrend parseTrend(String initializingScoreTrendString, int levelsSize) {
String[] trendTokens = initializingScoreTrendString.split("/");
boolean tokenIsSingle = trendTokens.length == 1;
var trendTokens = initializingScoreTrendString.split("/");
var tokenIsSingle = trendTokens.length == 1;
if (!tokenIsSingle && trendTokens.length != levelsSize) {
throw new IllegalArgumentException("The initializingScoreTrendString (" + initializingScoreTrendString
+ ") doesn't follow the correct pattern (" + buildTrendPattern(levelsSize) + "):"
+ " the trendTokens length (" + trendTokens.length
+ ") differs from the levelsSize (" + levelsSize + ").");
throw new IllegalArgumentException("""
The initializingScoreTrendString (%s) doesn't follow the correct pattern (%s): \
the trendTokens length (%d) differs from the levelsSize (%d)."""
.formatted(initializingScoreTrendString, buildTrendPattern(levelsSize), trendTokens.length,
levelsSize));
}
InitializingScoreTrendLevel[] trendLevels = new InitializingScoreTrendLevel[levelsSize];
for (int i = 0; i < levelsSize; i++) {
var trendLevels = new InitializingScoreTrendLevel[levelsSize];
for (var i = 0; i < levelsSize; i++) {
trendLevels[i] = InitializingScoreTrendLevel.valueOf(trendTokens[tokenIsSingle ? 0 : i]);
}
return new InitializingScoreTrend(trendLevels);
}

public static InitializingScoreTrend buildUniformTrend(InitializingScoreTrendLevel trendLevel, int levelsSize) {
InitializingScoreTrendLevel[] trendLevels = new InitializingScoreTrendLevel[levelsSize];
for (int i = 0; i < levelsSize; i++) {
trendLevels[i] = trendLevel;
}
var trendLevels = new InitializingScoreTrendLevel[levelsSize];
Arrays.fill(trendLevels, trendLevel);
return new InitializingScoreTrend(trendLevels);
}

protected static String buildTrendPattern(int levelsSize) {
StringBuilder trendPattern = new StringBuilder(levelsSize * 4);
boolean first = true;
for (int i = 0; i < levelsSize; i++) {
if (first) {
first = false;
} else {
trendPattern.append("/");
}
trendPattern.append(InitializingScoreTrendLevel.ANY.name());
}
return trendPattern.toString();
}

// ************************************************************************
// Fields, constructions, getters and setters
// ************************************************************************

private final InitializingScoreTrendLevel[] trendLevels;

public InitializingScoreTrend(InitializingScoreTrendLevel[] trendLevels) {
this.trendLevels = trendLevels;
}

public InitializingScoreTrendLevel[] getTrendLevels() {
return trendLevels;
private static String buildTrendPattern(int levelsSize) {
return Stream.generate(InitializingScoreTrendLevel.ANY::name)
.limit(levelsSize)
.collect(Collectors.joining("/"));
}

// ************************************************************************
Expand All @@ -73,7 +54,7 @@ public int getLevelsSize() {
}

public boolean isOnlyUp() {
for (InitializingScoreTrendLevel trendLevel : trendLevels) {
for (var trendLevel : trendLevels) {
if (trendLevel != InitializingScoreTrendLevel.ONLY_UP) {
return false;
}
Expand All @@ -82,12 +63,27 @@ public boolean isOnlyUp() {
}

public boolean isOnlyDown() {
for (InitializingScoreTrendLevel trendLevel : trendLevels) {
for (var trendLevel : trendLevels) {
if (trendLevel != InitializingScoreTrendLevel.ONLY_DOWN) {
return false;
}
}
return true;
}

@Override
public boolean equals(Object o) {
return o instanceof InitializingScoreTrend that
&& Arrays.equals(trendLevels, that.trendLevels);
}

@Override
public int hashCode() {
return Arrays.hashCode(trendLevels);
}

@Override
public String toString() {
return "InitializingScoreTrend(%s)".formatted(Arrays.toString(trendLevels));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class InitializingScoreTrendTest {

@Test
void parseTrend() {
assertThat(InitializingScoreTrend.parseTrend("ONLY_DOWN/ANY/ONLY_UP", 3).getTrendLevels())
assertThat(InitializingScoreTrend.parseTrend("ONLY_DOWN/ANY/ONLY_UP", 3).trendLevels())
.containsExactly(
InitializingScoreTrendLevel.ONLY_DOWN,
InitializingScoreTrendLevel.ANY,
Expand Down
Loading

0 comments on commit b5ff711

Please sign in to comment.