-
Notifications
You must be signed in to change notification settings - Fork 224
chore: introduce structural component to score #2583
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: no-loops
Are you sure you want to change the base?
Changes from all commits
de217dc
730f5de
d8d0429
c75f07d
3b139ee
25ec581
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,7 +3,6 @@ | |
| import java.math.BigDecimal; | ||
| import java.math.RoundingMode; | ||
| import java.util.Arrays; | ||
| import java.util.stream.Stream; | ||
|
|
||
| import ai.timefold.solver.core.impl.score.ScoreUtil; | ||
| import ai.timefold.solver.core.impl.score.definition.BendableScoreDefinition; | ||
|
|
@@ -22,18 +21,30 @@ | |
| * @see Score | ||
| */ | ||
| @NullMarked | ||
| public record BendableBigDecimalScore(BigDecimal[] hardScores, | ||
| public record BendableBigDecimalScore(long structuralScore, BigDecimal[] hardScores, | ||
| BigDecimal[] softScores) implements IBendableScore<BendableBigDecimalScore> { | ||
|
|
||
| public BendableBigDecimalScore(BigDecimal[] hardScores, | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's deprecate these convenience constructors to make it clear that the canonical constructor is still the way to go.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure about that; the solver sets the structural score, not score calculation, so for the vast majority, if not all, cases, users will do
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does the user need to create a score? Should they? IMO no. |
||
| BigDecimal[] softScores) { | ||
| this(0L, hardScores, softScores); | ||
| } | ||
|
|
||
| public static BendableBigDecimalScore parseScore(String scoreString) { | ||
| var scoreTokens = ScoreUtil.parseBendableScoreTokens(BendableBigDecimalScore.class, scoreString); | ||
| var hardScores = new BigDecimal[scoreTokens[0].length]; | ||
| long structuralScore = 0L; | ||
| if (scoreTokens[0] != null && scoreTokens[0].length > 0) { | ||
| structuralScore = ScoreUtil.parseLevelAsLong(BendableBigDecimalScore.class, scoreString, scoreTokens[0][0]); | ||
| } | ||
| var hardScores = new BigDecimal[scoreTokens[1].length]; | ||
| for (var i = 0; i < hardScores.length; i++) { | ||
| hardScores[i] = ScoreUtil.parseLevelAsBigDecimal(BendableBigDecimalScore.class, scoreString, scoreTokens[0][i]); | ||
| hardScores[i] = ScoreUtil.parseLevelAsBigDecimal(BendableBigDecimalScore.class, scoreString, scoreTokens[1][i]); | ||
| } | ||
| var softScores = new BigDecimal[scoreTokens[1].length]; | ||
| var softScores = new BigDecimal[scoreTokens[2].length]; | ||
| for (var i = 0; i < softScores.length; i++) { | ||
| softScores[i] = ScoreUtil.parseLevelAsBigDecimal(BendableBigDecimalScore.class, scoreString, scoreTokens[1][i]); | ||
| softScores[i] = ScoreUtil.parseLevelAsBigDecimal(BendableBigDecimalScore.class, scoreString, scoreTokens[2][i]); | ||
| } | ||
| if (structuralScore != 0L) { | ||
| return new BendableBigDecimalScore(structuralScore, hardScores, softScores); | ||
| } | ||
| return of(hardScores, softScores); | ||
| } | ||
|
|
@@ -134,6 +145,9 @@ public BigDecimal hardOrSoftScore(int index) { | |
|
|
||
| @Override | ||
| public boolean isFeasible() { | ||
| if (structuralScore < 0) { | ||
| return false; | ||
| } | ||
| for (var hardScore : hardScores) { | ||
| if (hardScore.compareTo(BigDecimal.ZERO) < 0) { | ||
| return false; | ||
|
|
@@ -270,6 +284,9 @@ public Number[] toLevelNumbers() { | |
| @Override | ||
| public boolean equals(Object o) { | ||
| if (o instanceof BendableBigDecimalScore other) { | ||
| if (structuralScore != other.structuralScore) { | ||
| return false; | ||
| } | ||
| if (hardLevelsSize() != other.hardLevelsSize() | ||
| || softLevelsSize() != other.softLevelsSize()) { | ||
| return false; | ||
|
|
@@ -291,16 +308,22 @@ public boolean equals(Object o) { | |
|
|
||
| @Override | ||
| public int hashCode() { | ||
| var scoreHashCodes = Stream.concat(Arrays.stream(hardScores), Arrays.stream(softScores)) | ||
| .map(BigDecimal::stripTrailingZeros) | ||
| .mapToInt(BigDecimal::hashCode) | ||
| .toArray(); | ||
| return Arrays.hashCode(scoreHashCodes); | ||
| var hash = Long.hashCode(structuralScore); | ||
| for (var hardScore : hardScores) { | ||
| hash = 31 * hash + hardScore.stripTrailingZeros().hashCode(); | ||
| } | ||
| for (var softScore : softScores) { | ||
| hash = 31 * hash + softScore.stripTrailingZeros().hashCode(); | ||
| } | ||
| return hash; | ||
| } | ||
|
|
||
| @Override | ||
| public int compareTo(BendableBigDecimalScore other) { | ||
| validateCompatible(other); | ||
| if (structuralScore != other.structuralScore) { | ||
| return Long.compare(structuralScore, other.structuralScore); | ||
| } | ||
| for (var i = 0; i < hardScores.length; i++) { | ||
| var hardScoreComparison = hardScores[i].compareTo(other.hardScore(i)); | ||
| if (hardScoreComparison != 0) { | ||
|
|
@@ -323,7 +346,10 @@ public String toShortString() { | |
|
|
||
| @Override | ||
| public String toString() { | ||
| var s = new StringBuilder(((hardScores.length + softScores.length) * 4) + 7); | ||
| var s = new StringBuilder(((hardScores.length + softScores.length) * 4) + 15); | ||
| if (structuralScore < 0) { | ||
| s.append("%dstructural/".formatted(structuralScore)); | ||
| } | ||
| s.append("["); | ||
| var first = true; | ||
| for (var hardScore : hardScores) { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -24,7 +24,7 @@ | |
| * @see Score | ||
| */ | ||
| @NullMarked | ||
| public record HardMediumSoftBigDecimalScore(BigDecimal hardScore, BigDecimal mediumScore, | ||
| public record HardMediumSoftBigDecimalScore(long structuralScore, BigDecimal hardScore, BigDecimal mediumScore, | ||
| BigDecimal softScore) implements Score<HardMediumSoftBigDecimalScore> { | ||
|
|
||
| public static final HardMediumSoftBigDecimalScore ZERO = new HardMediumSoftBigDecimalScore(BigDecimal.ZERO, | ||
|
|
@@ -45,13 +45,28 @@ public record HardMediumSoftBigDecimalScore(BigDecimal hardScore, BigDecimal med | |
| private static final HardMediumSoftBigDecimalScore MINUS_ONE_SOFT = | ||
| new HardMediumSoftBigDecimalScore(BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ONE.negate()); | ||
|
|
||
| public HardMediumSoftBigDecimalScore(BigDecimal hardScore, BigDecimal mediumScore, | ||
| BigDecimal softScore) { | ||
| this(0L, hardScore, mediumScore, softScore); | ||
| } | ||
|
|
||
| public static HardMediumSoftBigDecimalScore parseScore(String scoreString) { | ||
| var scoreTokens = ScoreUtil.parseScoreTokens(HardMediumSoftBigDecimalScore.class, scoreString, | ||
| HARD_LABEL, MEDIUM_LABEL, SOFT_LABEL); | ||
| var hardScore = ScoreUtil.parseLevelAsBigDecimal(HardMediumSoftBigDecimalScore.class, scoreString, scoreTokens[0]); | ||
| var mediumScore = ScoreUtil.parseLevelAsBigDecimal(HardMediumSoftBigDecimalScore.class, scoreString, scoreTokens[1]); | ||
| var softScore = ScoreUtil.parseLevelAsBigDecimal(HardMediumSoftBigDecimalScore.class, scoreString, scoreTokens[2]); | ||
| return of(hardScore, mediumScore, softScore); | ||
| if (scoreTokens.length == 3) { | ||
| var hardScore = ScoreUtil.parseLevelAsBigDecimal(HardMediumSoftBigDecimalScore.class, scoreString, scoreTokens[0]); | ||
| var mediumScore = | ||
| ScoreUtil.parseLevelAsBigDecimal(HardMediumSoftBigDecimalScore.class, scoreString, scoreTokens[1]); | ||
| var softScore = ScoreUtil.parseLevelAsBigDecimal(HardMediumSoftBigDecimalScore.class, scoreString, scoreTokens[2]); | ||
| return of(hardScore, mediumScore, softScore); | ||
| } else { | ||
| var structuralScore = ScoreUtil.parseLevelAsLong(HardMediumSoftBigDecimalScore.class, scoreString, scoreTokens[0]); | ||
| var hardScore = ScoreUtil.parseLevelAsBigDecimal(HardMediumSoftBigDecimalScore.class, scoreString, scoreTokens[1]); | ||
| var mediumScore = | ||
| ScoreUtil.parseLevelAsBigDecimal(HardMediumSoftBigDecimalScore.class, scoreString, scoreTokens[2]); | ||
| var softScore = ScoreUtil.parseLevelAsBigDecimal(HardMediumSoftBigDecimalScore.class, scoreString, scoreTokens[3]); | ||
| return new HardMediumSoftBigDecimalScore(structuralScore, hardScore, mediumScore, softScore); | ||
| } | ||
| } | ||
|
|
||
| public static HardMediumSoftBigDecimalScore of(BigDecimal hardScore, BigDecimal mediumScore, | ||
|
|
@@ -118,7 +133,7 @@ public static HardMediumSoftBigDecimalScore ofSoft(BigDecimal softScore) { | |
| */ | ||
| @Override | ||
| public boolean isFeasible() { | ||
| return hardScore.compareTo(BigDecimal.ZERO) >= 0; | ||
| return structuralScore >= 0 && hardScore.compareTo(BigDecimal.ZERO) >= 0; | ||
| } | ||
|
|
||
| @Override | ||
|
|
@@ -183,8 +198,9 @@ public Number[] toLevelNumbers() { | |
|
|
||
| @Override | ||
| public boolean equals(Object o) { | ||
| if (o instanceof HardMediumSoftBigDecimalScore(var otherHardScore, var otherMediumScore, var otherSoftScore)) { | ||
| return hardScore.stripTrailingZeros().equals(otherHardScore.stripTrailingZeros()) | ||
| if (o instanceof HardMediumSoftBigDecimalScore(var otherStructuralScore, var otherHardScore, var otherMediumScore, var otherSoftScore)) { | ||
| return structuralScore == otherStructuralScore | ||
| && hardScore.stripTrailingZeros().equals(otherHardScore.stripTrailingZeros()) | ||
| && mediumScore.stripTrailingZeros().equals(otherMediumScore.stripTrailingZeros()) | ||
| && softScore.stripTrailingZeros().equals(otherSoftScore.stripTrailingZeros()); | ||
| } | ||
|
|
@@ -193,11 +209,15 @@ public boolean equals(Object o) { | |
|
|
||
| @Override | ||
| public int hashCode() { | ||
| return Objects.hash(hardScore.stripTrailingZeros(), mediumScore.stripTrailingZeros(), softScore.stripTrailingZeros()); | ||
| return Objects.hash(structuralScore, hardScore.stripTrailingZeros(), mediumScore.stripTrailingZeros(), | ||
| softScore.stripTrailingZeros()); | ||
| } | ||
|
|
||
| @Override | ||
| public int compareTo(HardMediumSoftBigDecimalScore other) { | ||
| if (structuralScore != other.structuralScore) { | ||
| return Long.compare(structuralScore, other.structuralScore); | ||
| } | ||
| var hardScoreComparison = hardScore.compareTo(other.hardScore()); | ||
| if (hardScoreComparison != 0) { | ||
| return hardScoreComparison; | ||
|
|
@@ -218,7 +238,10 @@ public String toShortString() { | |
|
|
||
| @Override | ||
| public String toString() { | ||
| return hardScore + HARD_LABEL + "/" + mediumScore + MEDIUM_LABEL + "/" + softScore + SOFT_LABEL; | ||
| return (structuralScore < 0) | ||
| ? "%dstructural/%s%s/%s%s/%s%s".formatted(structuralScore, hardScore, HARD_LABEL, mediumScore, MEDIUM_LABEL, | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We have String constants for hard/medium/soft, let's introduce a constant for "structural" as well. |
||
| softScore, SOFT_LABEL) | ||
| : "%s%s/%s%s/%s%s".formatted(hardScore, HARD_LABEL, mediumScore, MEDIUM_LABEL, softScore, SOFT_LABEL); | ||
| } | ||
|
|
||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.