Skip to content

Commit

Permalink
PLANNER-391: Fixed ranking of uninitialized solutions
Browse files Browse the repository at this point in the history
  • Loading branch information
oskopek committed Aug 12, 2015
1 parent 88ed902 commit d9368d0
Show file tree
Hide file tree
Showing 11 changed files with 95 additions and 51 deletions.
Expand Up @@ -89,7 +89,7 @@ public SingleBenchmarkRunner call() {
solutionDescriptor.getVariableCount(outputSolution), solutionDescriptor.getVariableCount(outputSolution),
solutionDescriptor.getProblemScale(outputSolution)); solutionDescriptor.getProblemScale(outputSolution));
singleBenchmarkResult.setScore(outputSolution.getScore()); singleBenchmarkResult.setScore(outputSolution.getScore());
singleBenchmarkResult.setInitialized(solverScope.isBestSolutionInitialized()); singleBenchmarkResult.setUninitializedVariableCount(solverScope.getBestUninitializedVariableCount());
singleBenchmarkResult.setTimeMillisSpent(timeMillisSpent); singleBenchmarkResult.setTimeMillisSpent(timeMillisSpent);
singleBenchmarkResult.setCalculateCount(solverScope.getCalculateCount()); singleBenchmarkResult.setCalculateCount(solverScope.getCalculateCount());


Expand Down
Expand Up @@ -484,8 +484,8 @@ private MixedCheckBox createSolverBenchmarkCheckBox(SolverBenchmarkResult solver
+ "Total score: %s%n" + "Total score: %s%n"
+ "Average time spent: %s%n" + "Average time spent: %s%n"
+ "Total winning score difference: %s", + "Total winning score difference: %s",
solverBenchmarkResult.getAverageScore(), solverBenchmarkResult.getAverageScoreWithUninitializedPrefix(),
solverBenchmarkResult.getTotalScore(), solverBenchmarkResult.getTotalScoreWithUninitializedPrefix(),
solverBenchmarkResult.getAverageTimeMillisSpent() == null solverBenchmarkResult.getAverageTimeMillisSpent() == null
? "" : millisecondsSpentNumberFormat.format(solverBenchmarkResult.getAverageTimeMillisSpent()), ? "" : millisecondsSpentNumberFormat.format(solverBenchmarkResult.getAverageTimeMillisSpent()),
solverBenchmarkResult.getTotalWinningScoreDifference()); solverBenchmarkResult.getTotalWinningScoreDifference());
Expand Down
Expand Up @@ -21,13 +21,18 @@


import org.apache.commons.lang3.builder.CompareToBuilder; import org.apache.commons.lang3.builder.CompareToBuilder;
import org.optaplanner.benchmark.impl.result.SingleBenchmarkResult; import org.optaplanner.benchmark.impl.result.SingleBenchmarkResult;
import org.optaplanner.core.api.score.Score;


public class SingleBenchmarkRankingComparator implements Comparator<SingleBenchmarkResult>, Serializable { public class SingleBenchmarkRankingComparator implements Comparator<SingleBenchmarkResult>, Serializable {


private final Comparator<Score> resilientScoreComparator = new ResilientScoreComparator();

@Override
public int compare(SingleBenchmarkResult a, SingleBenchmarkResult b) { public int compare(SingleBenchmarkResult a, SingleBenchmarkResult b) {
return new CompareToBuilder() return new CompareToBuilder()
.append(a.isFailure(), b.isFailure()) .append(b.isFailure(), a.isFailure()) // Reverse, less is better (redundant: failed benchmarks don't get ranked at all)
.append(a.getScore(), b.getScore()) .append(b.getUninitializedVariableCount(), a.getUninitializedVariableCount()) // Reverse, less is better
.append(a.getScore(), b.getScore(), resilientScoreComparator)
.toComparison(); .toComparison();
} }


Expand Down
Expand Up @@ -39,51 +39,56 @@
*/ */
public class TotalRankSolverRankingWeightFactory implements SolverRankingWeightFactory { public class TotalRankSolverRankingWeightFactory implements SolverRankingWeightFactory {


private final Comparator<SingleBenchmarkResult> singleBenchmarkRankingComparator = new SingleBenchmarkRankingComparator();

public Comparable createRankingWeight(List<SolverBenchmarkResult> solverBenchmarkResultList, SolverBenchmarkResult solverBenchmarkResult) { public Comparable createRankingWeight(List<SolverBenchmarkResult> solverBenchmarkResultList, SolverBenchmarkResult solverBenchmarkResult) {
int betterCount = 0; int betterCount = 0;
int equalCount = 0; int equalCount = 0;
List<Score> scoreList = solverBenchmarkResult.getScoreList(); int lowerCount = 0;
for (SolverBenchmarkResult otherSolverBenchmarkResult : solverBenchmarkResultList) { List<SingleBenchmarkResult> singleBenchmarkResultList = solverBenchmarkResult.getSingleBenchmarkResultList();
if (otherSolverBenchmarkResult != solverBenchmarkResult) { for (SingleBenchmarkResult single : solverBenchmarkResult.getSingleBenchmarkResultList()) {
List<Score> otherScoreList = otherSolverBenchmarkResult.getScoreList(); for (SingleBenchmarkResult otherSingle : single.getProblemBenchmarkResult().getSingleBenchmarkResultList()) {
// TODO the scoreList.size() can differ between SolverBenchmarks if (single == otherSingle) {
for (int i = 0; i < scoreList.size(); i++) { continue;
Score score = scoreList.get(i); }
Score otherScore = otherScoreList.get(i); int scoreComparison = singleBenchmarkRankingComparator.compare(single, otherSingle);
int scoreComparison = score.compareTo(otherScore); if (scoreComparison > 0) {
if (scoreComparison > 0) { betterCount++;
betterCount++; } else if (scoreComparison == 0) {
} else if (scoreComparison == 0) { equalCount++;
equalCount++; } else {
} lowerCount++;
} }
} }
} }
return new TotalRankSolverRankingWeight(solverBenchmarkResult, betterCount, equalCount); return new TotalRankSolverRankingWeight(solverBenchmarkResult, betterCount, equalCount);
} }


public static class TotalRankSolverRankingWeight public static class TotalRankSolverRankingWeight implements Comparable<TotalRankSolverRankingWeight> {
implements Comparable<TotalRankSolverRankingWeight> {


private final Comparator<SolverBenchmarkResult> totalScoreSolverRankingComparator private final Comparator<SolverBenchmarkResult> totalScoreSolverRankingComparator
= new TotalScoreSolverRankingComparator(); = new TotalScoreSolverRankingComparator();


private SolverBenchmarkResult solverBenchmarkResult; private SolverBenchmarkResult solverBenchmarkResult;
private int betterCount; private int betterCount;
private int equalCount; private int equalCount;
private int lowerCount;


public TotalRankSolverRankingWeight(SolverBenchmarkResult solverBenchmarkResult, public TotalRankSolverRankingWeight(SolverBenchmarkResult solverBenchmarkResult,
int betterCount, int equalCount) { int betterCount, int equalCount) {
this.solverBenchmarkResult = solverBenchmarkResult; this.solverBenchmarkResult = solverBenchmarkResult;
this.betterCount = betterCount; this.betterCount = betterCount;
this.equalCount = equalCount; this.equalCount = equalCount;
this.lowerCount = lowerCount;
} }


@Override
public int compareTo(TotalRankSolverRankingWeight other) { public int compareTo(TotalRankSolverRankingWeight other) {
return new CompareToBuilder() return new CompareToBuilder()
.append(betterCount, other.betterCount) .append(betterCount, other.betterCount)
.append(equalCount, other.equalCount) .append(equalCount, other.equalCount)
.append(solverBenchmarkResult, other.solverBenchmarkResult, totalScoreSolverRankingComparator) .append(lowerCount, other.lowerCount)
.append(solverBenchmarkResult, other.solverBenchmarkResult, totalScoreSolverRankingComparator) // Tie-breaker
.toComparison(); .toComparison();
} }


Expand All @@ -96,6 +101,7 @@ public boolean equals(Object o) {
return new EqualsBuilder() return new EqualsBuilder()
.append(betterCount, other.betterCount) .append(betterCount, other.betterCount)
.append(equalCount, other.equalCount) .append(equalCount, other.equalCount)
.append(lowerCount, other.lowerCount)
.appendSuper(totalScoreSolverRankingComparator .appendSuper(totalScoreSolverRankingComparator
.compare(solverBenchmarkResult, other.solverBenchmarkResult) == 0) .compare(solverBenchmarkResult, other.solverBenchmarkResult) == 0)
.isEquals(); .isEquals();
Expand All @@ -109,6 +115,7 @@ public int hashCode() {
return new HashCodeBuilder() return new HashCodeBuilder()
.append(betterCount) .append(betterCount)
.append(equalCount) .append(equalCount)
.append(lowerCount)
.toHashCode(); .toHashCode();
} }


Expand Down
Expand Up @@ -35,15 +35,17 @@
*/ */
public class TotalScoreSolverRankingComparator implements Comparator<SolverBenchmarkResult>, Serializable { public class TotalScoreSolverRankingComparator implements Comparator<SolverBenchmarkResult>, Serializable {


private final ResilientScoreComparator resilientScoreComparator private final Comparator<Score> resilientScoreComparator = new ResilientScoreComparator();
= new ResilientScoreComparator(); private final Comparator<SolverBenchmarkResult> worstScoreSolverRankingComparator
private final WorstScoreSolverRankingComparator worstScoreSolverRankingComparator
= new WorstScoreSolverRankingComparator(); = new WorstScoreSolverRankingComparator();


@Override
public int compare(SolverBenchmarkResult a, SolverBenchmarkResult b) { public int compare(SolverBenchmarkResult a, SolverBenchmarkResult b) {
return new CompareToBuilder() return new CompareToBuilder()
.append(b.getFailureCount(), a.getFailureCount()) // Reverse, less is better (redundant: failed benchmarks don't get ranked at all)
.append(b.getUninitializedVariableCountSum(), a.getUninitializedVariableCountSum()) // Reverse, less is better
.append(a.getTotalScore(), b.getTotalScore(), resilientScoreComparator) .append(a.getTotalScore(), b.getTotalScore(), resilientScoreComparator)
.append(a, b, worstScoreSolverRankingComparator) .append(a, b, worstScoreSolverRankingComparator) // Tie breaker
.toComparison(); .toComparison();
} }


Expand Down
Expand Up @@ -21,27 +21,29 @@
import java.util.Comparator; import java.util.Comparator;
import java.util.List; import java.util.List;


import org.optaplanner.benchmark.impl.result.SingleBenchmarkResult;
import org.optaplanner.benchmark.impl.result.SolverBenchmarkResult; import org.optaplanner.benchmark.impl.result.SolverBenchmarkResult;
import org.optaplanner.core.api.score.Score;


/** /**
* This ranking {@link Comparator} orders a {@link SolverBenchmarkResult} by its worst {@link Score}. * This ranking {@link Comparator} orders a {@link SolverBenchmarkResult} by its worst {@link Score}.
* It minimizes the worst case scenario. * It minimizes the worst case scenario.
*/ */
public class WorstScoreSolverRankingComparator implements Comparator<SolverBenchmarkResult>, Serializable { public class WorstScoreSolverRankingComparator implements Comparator<SolverBenchmarkResult>, Serializable {


private final ResilientScoreComparator resilientScoreComparator private final Comparator<SingleBenchmarkResult> singleBenchmarkComparator = new SingleBenchmarkRankingComparator();
= new ResilientScoreComparator();


@Override
public int compare(SolverBenchmarkResult a, SolverBenchmarkResult b) { public int compare(SolverBenchmarkResult a, SolverBenchmarkResult b) {
List<Score> aScoreList = a.getScoreList(); List<SingleBenchmarkResult> aSingleBenchmarkResultList = a.getSingleBenchmarkResultList();
Collections.sort(aScoreList); // Worst scores become first in the list List<SingleBenchmarkResult> bSingleBenchmarkResultList = b.getSingleBenchmarkResultList();
List<Score> bScoreList = b.getScoreList(); // Order scores from worst to best
Collections.sort(bScoreList); // Worst scores become first in the list Collections.sort(aSingleBenchmarkResultList, singleBenchmarkComparator);
int aSize = aScoreList.size(); Collections.sort(bSingleBenchmarkResultList, singleBenchmarkComparator);
int bSize = bScoreList.size(); int aSize = aSingleBenchmarkResultList.size();
int bSize = bSingleBenchmarkResultList.size();
for (int i = 0; i < aSize && i < bSize; i++) { for (int i = 0; i < aSize && i < bSize; i++) {
int comparison = resilientScoreComparator.compare(aScoreList.get(i), bScoreList.get(i)); int comparison = singleBenchmarkComparator.compare(aSingleBenchmarkResultList.get(i),
bSingleBenchmarkResultList.get(i));
if (comparison != 0) { if (comparison != 0) {
return comparison; return comparison;
} }
Expand Down
Expand Up @@ -34,6 +34,7 @@
import org.optaplanner.core.api.score.FeasibilityScore; import org.optaplanner.core.api.score.FeasibilityScore;
import org.optaplanner.core.api.score.Score; import org.optaplanner.core.api.score.Score;
import org.optaplanner.core.api.solver.Solver; import org.optaplanner.core.api.solver.Solver;
import org.optaplanner.core.impl.score.ScoreUtils;


/** /**
* Represents 1 benchmark for 1 {@link Solver} configuration for 1 problem instance (data set). * Represents 1 benchmark for 1 {@link Solver} configuration for 1 problem instance (data set).
Expand All @@ -55,8 +56,8 @@ public class SingleBenchmarkResult {
private Long usedMemoryAfterInputSolution = null; private Long usedMemoryAfterInputSolution = null;


private Boolean succeeded = null; private Boolean succeeded = null;
private Boolean initialized = null;
private Score score = null; private Score score = null;
private Integer uninitializedVariableCount = null;
private long timeMillisSpent = -1L; private long timeMillisSpent = -1L;
private long calculateCount = -1L; private long calculateCount = -1L;


Expand Down Expand Up @@ -135,14 +136,6 @@ public void setSucceeded(Boolean succeeded) {
this.succeeded = succeeded; this.succeeded = succeeded;
} }


public Boolean getInitialized() {
return initialized;
}

public void setInitialized(Boolean initialized) {
this.initialized = initialized;
}

public Score getScore() { public Score getScore() {
return score; return score;
} }
Expand All @@ -151,6 +144,14 @@ public void setScore(Score score) {
this.score = score; this.score = score;
} }


public Integer getUninitializedVariableCount() {
return uninitializedVariableCount;
}

public void setUninitializedVariableCount(Integer uninitializedVariableCount) {
this.uninitializedVariableCount = uninitializedVariableCount;
}

public long getTimeMillisSpent() { public long getTimeMillisSpent() {
return timeMillisSpent; return timeMillisSpent;
} }
Expand Down Expand Up @@ -211,7 +212,7 @@ public boolean isSuccess() {
} }


public boolean isInitialized() { public boolean isInitialized() {
return initialized != null && initialized.booleanValue(); return uninitializedVariableCount != null && uninitializedVariableCount == 0;
} }


public boolean isFailure() { public boolean isFailure() {
Expand Down Expand Up @@ -243,6 +244,10 @@ public SingleStatistic getSingleStatistic(StatisticType statisticType) {
return effectiveSingleStatisticMap.get(statisticType); return effectiveSingleStatisticMap.get(statisticType);
} }


public String getScoreWithUninitializedPrefix() {
return ScoreUtils.getScoreWithUninitializedPrefix(uninitializedVariableCount, score);
}

// ************************************************************************ // ************************************************************************
// Accumulate methods // Accumulate methods
// ************************************************************************ // ************************************************************************
Expand Down Expand Up @@ -285,8 +290,8 @@ protected static SingleBenchmarkResult createMerge(SolverBenchmarkResult solverB
// Skip oldResult.reportDirectory // Skip oldResult.reportDirectory
// Skip oldResult.usedMemoryAfterInputSolution // Skip oldResult.usedMemoryAfterInputSolution
newResult.succeeded = oldResult.succeeded; newResult.succeeded = oldResult.succeeded;
newResult.initialized = oldResult.initialized;
newResult.score = oldResult.score; newResult.score = oldResult.score;
newResult.uninitializedVariableCount = oldResult.uninitializedVariableCount;
newResult.timeMillisSpent = oldResult.timeMillisSpent; newResult.timeMillisSpent = oldResult.timeMillisSpent;
newResult.calculateCount = oldResult.calculateCount; newResult.calculateCount = oldResult.calculateCount;


Expand Down
Expand Up @@ -58,6 +58,7 @@ public class SolverBenchmarkResult {


private Integer failureCount = null; private Integer failureCount = null;
private Integer uninitializedSolutionCount = null; private Integer uninitializedSolutionCount = null;
private Integer uninitializedVariableCountSum = null;
private Integer infeasibleScoreCount = null; private Integer infeasibleScoreCount = null;
private Score totalScore = null; private Score totalScore = null;
private Score averageScore = null; private Score averageScore = null;
Expand Down Expand Up @@ -121,6 +122,10 @@ public Integer getUninitializedSolutionCount() {
return uninitializedSolutionCount; return uninitializedSolutionCount;
} }


public Integer getUninitializedVariableCountSum() {
return uninitializedVariableCountSum;
}

public Integer getInfeasibleScoreCount() { public Integer getInfeasibleScoreCount() {
return infeasibleScoreCount; return infeasibleScoreCount;
} }
Expand Down Expand Up @@ -257,6 +262,14 @@ public String getSolverConfigAsHtmlEscapedXml() {
return StringEscapeUtils.ESCAPE_HTML4.translate(xml); return StringEscapeUtils.ESCAPE_HTML4.translate(xml);
} }


public String getTotalScoreWithUninitializedPrefix() {
return ScoreUtils.getScoreWithUninitializedPrefix(uninitializedVariableCountSum, totalScore);
}

public String getAverageScoreWithUninitializedPrefix() {
return ScoreUtils.getScoreWithUninitializedPrefix(uninitializedVariableCountSum / getSingleBenchmarkResultList().size(), averageScore);
}

// ************************************************************************ // ************************************************************************
// Accumulate methods // Accumulate methods
// ************************************************************************ // ************************************************************************
Expand All @@ -281,13 +294,15 @@ protected void determineTotalsAndAverages() {
long totalAverageCalculateCountPerSecond = 0L; long totalAverageCalculateCountPerSecond = 0L;
long totalTimeMillisSpent = 0L; long totalTimeMillisSpent = 0L;
uninitializedSolutionCount = 0; uninitializedSolutionCount = 0;
uninitializedVariableCountSum = 0;
infeasibleScoreCount = 0; infeasibleScoreCount = 0;
for (SingleBenchmarkResult singleBenchmarkResult : singleBenchmarkResultList) { for (SingleBenchmarkResult singleBenchmarkResult : singleBenchmarkResultList) {
if (singleBenchmarkResult.isFailure()) { if (singleBenchmarkResult.isFailure()) {
failureCount++; failureCount++;
} else { } else {
if (!singleBenchmarkResult.isInitialized()) { if (!singleBenchmarkResult.isInitialized()) {
uninitializedSolutionCount++; uninitializedSolutionCount++;
uninitializedVariableCountSum += singleBenchmarkResult.getUninitializedVariableCount();
} else if (!singleBenchmarkResult.isScoreFeasible()) { } else if (!singleBenchmarkResult.isScoreFeasible()) {
infeasibleScoreCount++; infeasibleScoreCount++;
} }
Expand Down
Expand Up @@ -174,8 +174,8 @@
<#list benchmarkReport.plannerBenchmarkResult.solverBenchmarkResultList as solverBenchmarkResult> <#list benchmarkReport.plannerBenchmarkResult.solverBenchmarkResultList as solverBenchmarkResult>
<tr<#if solverBenchmarkResult.favorite> class="favoriteSolverBenchmark"</#if>> <tr<#if solverBenchmarkResult.favorite> class="favoriteSolverBenchmark"</#if>>
<th>${solverBenchmarkResult.name}&nbsp;<@addSolverBenchmarkBadges solverBenchmarkResult=solverBenchmarkResult/></th> <th>${solverBenchmarkResult.name}&nbsp;<@addSolverBenchmarkBadges solverBenchmarkResult=solverBenchmarkResult/></th>
<td>${solverBenchmarkResult.totalScore!""}</td> <td>${solverBenchmarkResult.totalScoreWithUninitializedPrefix!""}</td>
<td>${solverBenchmarkResult.averageScore!""}</td> <td>${solverBenchmarkResult.averageScoreWithUninitializedPrefix!""}</td>
<td>${solverBenchmarkResult.standardDeviationString!""}</td> <td>${solverBenchmarkResult.standardDeviationString!""}</td>
<#list benchmarkReport.plannerBenchmarkResult.unifiedProblemBenchmarkResultList as problemBenchmarkResult> <#list benchmarkReport.plannerBenchmarkResult.unifiedProblemBenchmarkResultList as problemBenchmarkResult>
<#if !solverBenchmarkResult.findSingleBenchmark(problemBenchmarkResult)??> <#if !solverBenchmarkResult.findSingleBenchmark(problemBenchmarkResult)??>
Expand All @@ -185,7 +185,7 @@
<#if !singleBenchmarkResult.success> <#if !singleBenchmarkResult.success>
<td><span class="label label-important">Failed</span></td> <td><span class="label label-important">Failed</span></td>
<#else> <#else>
<td>${singleBenchmarkResult.score}&nbsp;<@addSingleBenchmarkBadges singleBenchmarkResult=singleBenchmarkResult/></td> <td>${singleBenchmarkResult.scoreWithUninitializedPrefix}&nbsp;<@addSingleBenchmarkBadges singleBenchmarkResult=singleBenchmarkResult/></td>
</#if> </#if>
</#if> </#if>
</#list> </#list>
Expand Down
Expand Up @@ -79,6 +79,13 @@ public static double calculateTimeGradient(Number[] totalDiffNumbers, Number[] s
return timeGradient; return timeGradient;
} }


public static String getScoreWithUninitializedPrefix(int uninitializedVariableCount, Score score) {
if (score == null) {
return null; // return null, not String "null" so we can use a default value
}
return uninitializedVariableCount == 0 ? score.toString() : uninitializedVariableCount + "uninitialized/" + score.toString();
}

private ScoreUtils() { private ScoreUtils() {
} }


Expand Down
Expand Up @@ -23,6 +23,7 @@
import org.optaplanner.core.api.domain.solution.cloner.SolutionCloner; import org.optaplanner.core.api.domain.solution.cloner.SolutionCloner;
import org.optaplanner.core.api.score.Score; import org.optaplanner.core.api.score.Score;
import org.optaplanner.core.impl.domain.solution.descriptor.SolutionDescriptor; import org.optaplanner.core.impl.domain.solution.descriptor.SolutionDescriptor;
import org.optaplanner.core.impl.score.ScoreUtils;
import org.optaplanner.core.impl.score.definition.ScoreDefinition; import org.optaplanner.core.impl.score.definition.ScoreDefinition;
import org.optaplanner.core.impl.score.director.InnerScoreDirector; import org.optaplanner.core.impl.score.director.InnerScoreDirector;
import org.slf4j.Logger; import org.slf4j.Logger;
Expand Down Expand Up @@ -198,7 +199,7 @@ public void setWorkingSolutionFromBestSolution() {
} }


public String getBestScoreWithUninitializedPrefix() { public String getBestScoreWithUninitializedPrefix() {
return isBestSolutionInitialized() ? bestScore.toString() : "uninitialized/" + bestScore; return ScoreUtils.getScoreWithUninitializedPrefix(bestUninitializedVariableCount, bestScore);
} }


} }

0 comments on commit d9368d0

Please sign in to comment.