From 86ca247f67f77820508bc96578deac7db70dfcd9 Mon Sep 17 00:00:00 2001 From: Christopher Chianelli Date: Fri, 5 Jun 2026 09:57:47 -0400 Subject: [PATCH 1/2] chore: use approximate problem scale instead of log in benchmark report --- .../swingui/BenchmarkAggregatorFrame.java | 9 ++++---- .../impl/report/BenchmarkReport.java | 12 +++++------ .../impl/result/PlannerBenchmarkResult.java | 21 +++++++++---------- .../impl/result/ProblemBenchmarkResult.java | 19 ++++++++++++----- 4 files changed, 35 insertions(+), 26 deletions(-) diff --git a/tools/benchmark-aggregator/src/main/java/ai/timefold/solver/benchmark/aggregator/swingui/BenchmarkAggregatorFrame.java b/tools/benchmark-aggregator/src/main/java/ai/timefold/solver/benchmark/aggregator/swingui/BenchmarkAggregatorFrame.java index b401f3efd29..df705839415 100644 --- a/tools/benchmark-aggregator/src/main/java/ai/timefold/solver/benchmark/aggregator/swingui/BenchmarkAggregatorFrame.java +++ b/tools/benchmark-aggregator/src/main/java/ai/timefold/solver/benchmark/aggregator/swingui/BenchmarkAggregatorFrame.java @@ -518,10 +518,11 @@ private MixedCheckBox createSolverBenchmarkCheckBox(SolverBenchmarkResult solver } private MixedCheckBox createProblemBenchmarkCheckBox(ProblemBenchmarkResult problemBenchmarkResult) { - String problemBenchmarkDetail = String.format( - "Entity count: %d%n" - + "Problem scale: %d%n" - + "Used memory: %s", + var problemBenchmarkDetail = String.format( + """ + Entity count: %d%n\ + Problem scale: %s%n\ + Used memory: %s""", problemBenchmarkResult.getEntityCount(), problemBenchmarkResult.getProblemScale(), toEmptyStringIfNull(problemBenchmarkResult.getAverageUsedMemoryAfterInputSolution())); diff --git a/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/impl/report/BenchmarkReport.java b/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/impl/report/BenchmarkReport.java index d12d0ee41cd..f219266877c 100644 --- a/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/impl/report/BenchmarkReport.java +++ b/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/impl/report/BenchmarkReport.java @@ -372,8 +372,8 @@ private List> createBestScoreScalabilitySummaryChart() { String solverLabel = solverBenchmarkResult.getNameWithFavoriteSuffix(); for (SingleBenchmarkResult singleBenchmarkResult : solverBenchmarkResult.getSingleBenchmarkResultList()) { if (singleBenchmarkResult.hasAllSuccess()) { - long problemScale = singleBenchmarkResult.getProblemBenchmarkResult().getProblemScale(); - double[] levelValues = singleBenchmarkResult.getAverageScore().toLevelDoubles(); + var problemScale = singleBenchmarkResult.getProblemBenchmarkResult().getProblemScaleLog(); + var levelValues = singleBenchmarkResult.getAverageScore().toLevelDoubles(); for (int i = 0; i < levelValues.length && i < CHARTED_SCORE_LEVEL_SIZE; i++) { if (i >= builderList.size()) { builderList.add(new LineChart.Builder<>()); @@ -556,8 +556,8 @@ private LineChart createScalabilitySummaryChart(ToLongFunction { - long problemScale = singleBenchmarkResult.getProblemBenchmarkResult().getProblemScale(); - long timeMillisSpent = valueFunction.applyAsLong(singleBenchmarkResult); + var problemScale = singleBenchmarkResult.getProblemBenchmarkResult().getProblemScaleLog(); + var timeMillisSpent = valueFunction.applyAsLong(singleBenchmarkResult); builder.add(solverLabel, problemScale, timeMillisSpent); }); } @@ -570,8 +570,8 @@ private List> createBestScorePerTimeSpentSummaryChart() String solverLabel = solverBenchmarkResult.getNameWithFavoriteSuffix(); for (SingleBenchmarkResult singleBenchmarkResult : solverBenchmarkResult.getSingleBenchmarkResultList()) { if (singleBenchmarkResult.hasAllSuccess()) { - long timeMillisSpent = singleBenchmarkResult.getTimeMillisSpent(); - double[] levelValues = singleBenchmarkResult.getAverageScore().toLevelDoubles(); + var timeMillisSpent = singleBenchmarkResult.getTimeMillisSpent(); + var levelValues = singleBenchmarkResult.getAverageScore().toLevelDoubles(); for (int i = 0; i < levelValues.length && i < CHARTED_SCORE_LEVEL_SIZE; i++) { if (i >= builderList.size()) { builderList.add(new LineChart.Builder<>()); diff --git a/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/impl/result/PlannerBenchmarkResult.java b/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/impl/result/PlannerBenchmarkResult.java index 4827809baa2..65b26680b20 100644 --- a/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/impl/result/PlannerBenchmarkResult.java +++ b/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/impl/result/PlannerBenchmarkResult.java @@ -23,7 +23,6 @@ import ai.timefold.solver.core.config.solver.EnvironmentMode; import ai.timefold.solver.core.config.util.ConfigUtils; import ai.timefold.solver.core.enterprise.TimefoldSolverEnterpriseService; -import ai.timefold.solver.core.impl.score.definition.ScoreDefinition; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -316,10 +315,10 @@ public void accumulateResults(BenchmarkReport benchmarkReport) { private > void determineTotalsAndAverages() { failureCount = 0; - long totalProblemScale = 0L; - int problemScaleCount = 0; - for (ProblemBenchmarkResult problemBenchmarkResult : unifiedProblemBenchmarkResultList) { - Long problemScale = problemBenchmarkResult.getProblemScale(); + var totalProblemScale = 0L; + var problemScaleCount = 0; + for (var problemBenchmarkResult : unifiedProblemBenchmarkResultList) { + var problemScale = problemBenchmarkResult.getProblemScaleLog(); if (problemScale != null && problemScale >= 0L) { totalProblemScale += problemScale; problemScaleCount++; @@ -328,10 +327,10 @@ private > void determineTotalsAndAverages() { } averageProblemScale = problemScaleCount == 0 ? null : totalProblemScale / problemScaleCount; Score_ totalScore = null; - int solverBenchmarkCount = 0; - boolean firstSolverBenchmarkResult = true; - for (SolverBenchmarkResult solverBenchmarkResult : solverBenchmarkResultList) { - EnvironmentMode solverEnvironmentMode = solverBenchmarkResult.getEnvironmentMode(); + var solverBenchmarkCount = 0; + var firstSolverBenchmarkResult = true; + for (var solverBenchmarkResult : solverBenchmarkResultList) { + var solverEnvironmentMode = solverBenchmarkResult.getEnvironmentMode(); if (firstSolverBenchmarkResult && solverEnvironmentMode != null) { environmentMode = solverEnvironmentMode; firstSolverBenchmarkResult = false; @@ -339,9 +338,9 @@ private > void determineTotalsAndAverages() { environmentMode = null; } - Score_ score = (Score_) solverBenchmarkResult.getAverageScore(); + var score = (Score_) solverBenchmarkResult.getAverageScore(); if (score != null) { - ScoreDefinition scoreDefinition = solverBenchmarkResult.getScoreDefinition(); + var scoreDefinition = solverBenchmarkResult.getScoreDefinition(); if (totalScore != null && !scoreDefinition.isCompatibleArithmeticArgument(totalScore)) { // Mixing different use cases with different score definitions. totalScore = null; diff --git a/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/impl/result/ProblemBenchmarkResult.java b/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/impl/result/ProblemBenchmarkResult.java index b278b9d1d15..1f888954f58 100644 --- a/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/impl/result/ProblemBenchmarkResult.java +++ b/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/impl/result/ProblemBenchmarkResult.java @@ -84,7 +84,8 @@ public class ProblemBenchmarkResult { private Long entityCount = null; private Long variableCount = null; private Long maximumValueCount = null; - private Long problemScale = null; + private String problemScale = null; + private Long problemScaleLog = null; private Long inputSolutionLoadingTimeMillisSpent = null; @XmlTransient // Loaded lazily from singleBenchmarkResults @@ -177,10 +178,14 @@ public Long getMaximumValueCount() { return maximumValueCount; } - public Long getProblemScale() { + public String getProblemScale() { return problemScale; } + public Long getProblemScaleLog() { + return problemScaleLog; + } + @SuppressWarnings("unused") // Used by FreeMarker. public Long getInputSolutionLoadingTimeMillisSpent() { return inputSolutionLoadingTimeMillisSpent; @@ -455,13 +460,15 @@ public void registerProblemSizeStatistics(ProblemSizeStatistics problemSizeStati maximumValueCount = -1L; } if (problemScale == null) { - problemScale = problemSizeStatistics.approximateProblemScaleLogAsFixedPointLong(); - } else if (problemScale.longValue() != problemSizeStatistics.approximateProblemScaleLogAsFixedPointLong()) { + problemScale = problemSizeStatistics.approximateProblemScaleAsFormattedString(); + problemScaleLog = problemSizeStatistics.approximateProblemScaleLogAsFixedPointLong(); + } else if (!Objects.equals(problemScaleLog, problemSizeStatistics.approximateProblemScaleLogAsFixedPointLong())) { LOGGER.warn("The problemBenchmarkResult ({}) has different problemScale values ([{},{}]).\n" + "This is normally impossible for 1 inputSolutionFile.", getName(), problemScale, problemSizeStatistics.approximateProblemScaleLogAsFixedPointLong()); // The problemScale is not unknown (null), but known to be ambiguous - problemScale = -1L; + problemScale = "-1"; + problemScaleLog = -1L; } } @@ -517,6 +524,7 @@ protected static Map newResult.variableCount = oldResult.variableCount; newResult.maximumValueCount = oldResult.maximumValueCount; newResult.problemScale = oldResult.problemScale; + newResult.problemScaleLog = oldResult.problemScaleLog; problemProviderToNewResultMap.put(oldResult.problemProvider, newResult); newPlannerBenchmarkResult.getUnifiedProblemBenchmarkResultList().add(newResult); } else { @@ -534,6 +542,7 @@ protected static Map newResult.maximumValueCount = ConfigUtils.meldProperty(oldResult.maximumValueCount, newResult.maximumValueCount); newResult.problemScale = ConfigUtils.meldProperty(oldResult.problemScale, newResult.problemScale); + newResult.problemScaleLog = ConfigUtils.meldProperty(oldResult.problemScaleLog, newResult.problemScaleLog); } mergeMap.put(oldResult, newResult); } From 45f0d53e729e20a0db6abd9e3d47c1bbf3c676e8 Mon Sep 17 00:00:00 2001 From: Christopher Chianelli Date: Fri, 5 Jun 2026 11:20:08 -0400 Subject: [PATCH 2/2] chore: be backward compatiable when possible --- .../api/solver/ProblemSizeStatistics.java | 52 +------------------ .../solver/core/impl/util/MathUtils.java | 49 +++++++++++++++++ .../swingui/BenchmarkAggregatorFrame.java | 2 +- .../impl/report/BenchmarkReport.java | 4 +- .../impl/result/PlannerBenchmarkResult.java | 12 +++-- .../impl/result/ProblemBenchmarkResult.java | 37 +++++++------ .../impl/report/benchmarkReport.html.ftl | 8 +-- 7 files changed, 87 insertions(+), 77 deletions(-) diff --git a/core/src/main/java/ai/timefold/solver/core/api/solver/ProblemSizeStatistics.java b/core/src/main/java/ai/timefold/solver/core/api/solver/ProblemSizeStatistics.java index 08e22ca9250..f59ec245dc3 100644 --- a/core/src/main/java/ai/timefold/solver/core/api/solver/ProblemSizeStatistics.java +++ b/core/src/main/java/ai/timefold/solver/core/api/solver/ProblemSizeStatistics.java @@ -1,7 +1,5 @@ package ai.timefold.solver.core.api.solver; -import java.text.DecimalFormat; -import java.text.DecimalFormatSymbols; import java.util.Locale; import ai.timefold.solver.core.impl.util.MathUtils; @@ -23,13 +21,6 @@ public record ProblemSizeStatistics(long entityCount, long approximateValueCount, double approximateProblemSizeLog) { - private static final Locale FORMATTER_LOCALE = Locale.getDefault(); - private static final DecimalFormat BASIC_FORMATTER = new DecimalFormat("#,###"); - - // Exponent should not use grouping, unlike basic - private static final DecimalFormat EXPONENT_FORMATTER = new DecimalFormat("#"); - private static final DecimalFormat SIGNIFICANT_FIGURE_FORMATTER = new DecimalFormat("0.######"); - /** * Return the {@link #approximateProblemSizeLog} as a fixed point integer. */ @@ -38,48 +29,7 @@ public long approximateProblemScaleLogAsFixedPointLong() { } public String approximateProblemScaleAsFormattedString() { - return approximateProblemScaleAsFormattedString(Locale.getDefault()); - } - - String approximateProblemScaleAsFormattedString(Locale locale) { - if (Double.isNaN(approximateProblemSizeLog) || Double.isInfinite(approximateProblemSizeLog)) { - return "0"; - } - - if (approximateProblemSizeLog < 10) { // log_10(10_000_000_000) = 10 - return "%s".formatted(format(Math.pow(10d, approximateProblemSizeLog), BASIC_FORMATTER, locale)); - } - // The actual number will often be too large to fit in a double, so cannot use basic - // formatting. - // Separate the exponent into its integral and fractional parts - // Use the integral part as the power of 10, and the fractional part as the significant digits. - double exponentPart = Math.floor(approximateProblemSizeLog); - double remainderPartAsExponent = approximateProblemSizeLog - exponentPart; - double remainderPart = Math.pow(10, remainderPartAsExponent); - return "%s × 10^%s".formatted( - format(remainderPart, SIGNIFICANT_FIGURE_FORMATTER, locale), - format(exponentPart, EXPONENT_FORMATTER, locale)); - } - - /** - * In order for tests to work currently regardless of the default system locale, - * we need to set the locale to a known value before running the tests. - * And because the {@link DecimalFormat} instances are initialized statically for reasons of performance, - * we cannot expect them to be in the locale that the test expects them to be in. - * This method exists to allow for an override. - * - * @return the given decimalFormat with the given locale - */ - private static String format(double number, DecimalFormat decimalFormat, Locale locale) { - if (locale.equals(FORMATTER_LOCALE)) { - return decimalFormat.format(number); - } - try { // Slow path for corner cases where input locale doesn't match the default locale. - decimalFormat.setDecimalFormatSymbols(DecimalFormatSymbols.getInstance(locale)); - return decimalFormat.format(number); - } finally { - decimalFormat.setDecimalFormatSymbols(DecimalFormatSymbols.getInstance(FORMATTER_LOCALE)); - } + return MathUtils.approximateProblemScaleAsFormattedString(approximateProblemSizeLog, Locale.getDefault()); } } diff --git a/core/src/main/java/ai/timefold/solver/core/impl/util/MathUtils.java b/core/src/main/java/ai/timefold/solver/core/impl/util/MathUtils.java index 8c3d3aa1d5d..7984ee8266d 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/util/MathUtils.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/util/MathUtils.java @@ -1,5 +1,9 @@ package ai.timefold.solver.core.impl.util; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.util.Locale; + /** * Includes code taken from Apache's commons-math library, v3.6.1 (Apache 2.0-licensed) * These methods are clearly marked in comments, their modifications listed. @@ -9,6 +13,11 @@ public class MathUtils { public static final long LOG_PRECISION = 1_000_000L; private static final int FACTORIAL_MAX_N = 20; private static final long[] FACTORIAL_CACHE = new long[FACTORIAL_MAX_N - 1]; // 0 and 1 are hard-coded. + public static final Locale FORMATTER_LOCALE = Locale.getDefault(); + private static final DecimalFormat BASIC_FORMATTER = new DecimalFormat("#,###"); + // Exponent should not use grouping, unlike basic + private static final DecimalFormat EXPONENT_FORMATTER = new DecimalFormat("#"); + private static final DecimalFormat SIGNIFICANT_FIGURE_FORMATTER = new DecimalFormat("0.######"); private MathUtils() { } @@ -346,4 +355,44 @@ private static long mulAndCheck(long a, long b) { } } + public static String approximateProblemScaleAsFormattedString(double approximateProblemSizeLog, Locale locale) { + if (Double.isNaN(approximateProblemSizeLog) || Double.isInfinite(approximateProblemSizeLog)) { + return "0"; + } + + if (approximateProblemSizeLog < 10) { // log_10(10_000_000_000) = 10 + return "%s".formatted(formatNumber(Math.pow(10d, approximateProblemSizeLog), BASIC_FORMATTER, locale)); + } + // The actual number will often be too large to fit in a double, so cannot use basic + // formatting. + // Separate the exponent into its integral and fractional parts + // Use the integral part as the power of 10, and the fractional part as the significant digits. + double exponentPart = Math.floor(approximateProblemSizeLog); + double remainderPartAsExponent = approximateProblemSizeLog - exponentPart; + double remainderPart = Math.pow(10, remainderPartAsExponent); + return "%s × 10^%s".formatted( + formatNumber(remainderPart, SIGNIFICANT_FIGURE_FORMATTER, locale), + formatNumber(exponentPart, EXPONENT_FORMATTER, locale)); + } + + /** + * In order for tests to work currently regardless of the default system locale, + * we need to set the locale to a known value before running the tests. + * And because the {@link DecimalFormat} instances are initialized statically for reasons of performance, + * we cannot expect them to be in the locale that the test expects them to be in. + * This method exists to allow for an override. + * + * @return the given decimalFormat with the given locale + */ + private static String formatNumber(double number, DecimalFormat decimalFormat, Locale locale) { + if (locale.equals(MathUtils.FORMATTER_LOCALE)) { + return decimalFormat.format(number); + } + try { // Slow path for corner cases where input locale doesn't match the default locale. + decimalFormat.setDecimalFormatSymbols(DecimalFormatSymbols.getInstance(locale)); + return decimalFormat.format(number); + } finally { + decimalFormat.setDecimalFormatSymbols(DecimalFormatSymbols.getInstance(MathUtils.FORMATTER_LOCALE)); + } + } } diff --git a/tools/benchmark-aggregator/src/main/java/ai/timefold/solver/benchmark/aggregator/swingui/BenchmarkAggregatorFrame.java b/tools/benchmark-aggregator/src/main/java/ai/timefold/solver/benchmark/aggregator/swingui/BenchmarkAggregatorFrame.java index df705839415..d06b2f1ec3c 100644 --- a/tools/benchmark-aggregator/src/main/java/ai/timefold/solver/benchmark/aggregator/swingui/BenchmarkAggregatorFrame.java +++ b/tools/benchmark-aggregator/src/main/java/ai/timefold/solver/benchmark/aggregator/swingui/BenchmarkAggregatorFrame.java @@ -524,7 +524,7 @@ private MixedCheckBox createProblemBenchmarkCheckBox(ProblemBenchmarkResult prob Problem scale: %s%n\ Used memory: %s""", problemBenchmarkResult.getEntityCount(), - problemBenchmarkResult.getProblemScale(), + problemBenchmarkResult.getFormattedProblemScale(), toEmptyStringIfNull(problemBenchmarkResult.getAverageUsedMemoryAfterInputSolution())); return new MixedCheckBox(problemBenchmarkResult.getName(), problemBenchmarkDetail); } diff --git a/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/impl/report/BenchmarkReport.java b/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/impl/report/BenchmarkReport.java index f219266877c..5fee501b3f8 100644 --- a/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/impl/report/BenchmarkReport.java +++ b/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/impl/report/BenchmarkReport.java @@ -372,7 +372,7 @@ private List> createBestScoreScalabilitySummaryChart() { String solverLabel = solverBenchmarkResult.getNameWithFavoriteSuffix(); for (SingleBenchmarkResult singleBenchmarkResult : solverBenchmarkResult.getSingleBenchmarkResultList()) { if (singleBenchmarkResult.hasAllSuccess()) { - var problemScale = singleBenchmarkResult.getProblemBenchmarkResult().getProblemScaleLog(); + var problemScale = singleBenchmarkResult.getProblemBenchmarkResult().getProblemScale(); var levelValues = singleBenchmarkResult.getAverageScore().toLevelDoubles(); for (int i = 0; i < levelValues.length && i < CHARTED_SCORE_LEVEL_SIZE; i++) { if (i >= builderList.size()) { @@ -556,7 +556,7 @@ private LineChart createScalabilitySummaryChart(ToLongFunction { - var problemScale = singleBenchmarkResult.getProblemBenchmarkResult().getProblemScaleLog(); + var problemScale = singleBenchmarkResult.getProblemBenchmarkResult().getProblemScale(); var timeMillisSpent = valueFunction.applyAsLong(singleBenchmarkResult); builder.add(solverLabel, problemScale, timeMillisSpent); }); diff --git a/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/impl/result/PlannerBenchmarkResult.java b/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/impl/result/PlannerBenchmarkResult.java index 65b26680b20..f12ad914978 100644 --- a/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/impl/result/PlannerBenchmarkResult.java +++ b/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/impl/result/PlannerBenchmarkResult.java @@ -9,6 +9,7 @@ import java.util.Comparator; import java.util.IdentityHashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.SortedMap; import java.util.TreeMap; @@ -23,6 +24,7 @@ import ai.timefold.solver.core.config.solver.EnvironmentMode; import ai.timefold.solver.core.config.util.ConfigUtils; import ai.timefold.solver.core.enterprise.TimefoldSolverEnterpriseService; +import ai.timefold.solver.core.impl.util.MathUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -66,7 +68,7 @@ public class PlannerBenchmarkResult { // ************************************************************************ private Integer failureCount = null; - private Long averageProblemScale = null; + private String averageProblemScale = null; private Score averageScore = null; private SolverBenchmarkResult favoriteSolverBenchmarkResult = null; @@ -183,7 +185,7 @@ public Integer getFailureCount() { return failureCount; } - public Long getAverageProblemScale() { + public String getAverageProblemScale() { return averageProblemScale; } @@ -318,14 +320,16 @@ private > void determineTotalsAndAverages() { var totalProblemScale = 0L; var problemScaleCount = 0; for (var problemBenchmarkResult : unifiedProblemBenchmarkResultList) { - var problemScale = problemBenchmarkResult.getProblemScaleLog(); + var problemScale = problemBenchmarkResult.getProblemScale(); if (problemScale != null && problemScale >= 0L) { totalProblemScale += problemScale; problemScaleCount++; } failureCount += problemBenchmarkResult.getFailureCount(); } - averageProblemScale = problemScaleCount == 0 ? null : totalProblemScale / problemScaleCount; + averageProblemScale = problemScaleCount == 0 ? null + : MathUtils.approximateProblemScaleAsFormattedString( + (double) totalProblemScale / problemScaleCount / MathUtils.LOG_PRECISION, Locale.getDefault()); Score_ totalScore = null; var solverBenchmarkCount = 0; var firstSolverBenchmarkResult = true; diff --git a/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/impl/result/ProblemBenchmarkResult.java b/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/impl/result/ProblemBenchmarkResult.java index 1f888954f58..fe7dfcb80d0 100644 --- a/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/impl/result/ProblemBenchmarkResult.java +++ b/tools/benchmark/src/main/java/ai/timefold/solver/benchmark/impl/result/ProblemBenchmarkResult.java @@ -84,8 +84,11 @@ public class ProblemBenchmarkResult { private Long entityCount = null; private Long variableCount = null; private Long maximumValueCount = null; - private String problemScale = null; - private Long problemScaleLog = null; + private String formattedProblemScale = null; + /** + * Note: this is a fixed-point long of the problem scale log + */ + private Long problemScale = null; private Long inputSolutionLoadingTimeMillisSpent = null; @XmlTransient // Loaded lazily from singleBenchmarkResults @@ -178,12 +181,15 @@ public Long getMaximumValueCount() { return maximumValueCount; } - public String getProblemScale() { - return problemScale; + public String getFormattedProblemScale() { + return formattedProblemScale; } - public Long getProblemScaleLog() { - return problemScaleLog; + /** + * Returns the fixed-point long of the problem scale log + */ + public Long getProblemScale() { + return problemScale; } @SuppressWarnings("unused") // Used by FreeMarker. @@ -459,16 +465,16 @@ public void registerProblemSizeStatistics(ProblemSizeStatistics problemSizeStati // The approximateValueCount is not unknown (null), but known to be ambiguous maximumValueCount = -1L; } - if (problemScale == null) { - problemScale = problemSizeStatistics.approximateProblemScaleAsFormattedString(); - problemScaleLog = problemSizeStatistics.approximateProblemScaleLogAsFixedPointLong(); - } else if (!Objects.equals(problemScaleLog, problemSizeStatistics.approximateProblemScaleLogAsFixedPointLong())) { + if (formattedProblemScale == null) { + formattedProblemScale = problemSizeStatistics.approximateProblemScaleAsFormattedString(); + problemScale = problemSizeStatistics.approximateProblemScaleLogAsFixedPointLong(); + } else if (!Objects.equals(problemScale, problemSizeStatistics.approximateProblemScaleLogAsFixedPointLong())) { LOGGER.warn("The problemBenchmarkResult ({}) has different problemScale values ([{},{}]).\n" + "This is normally impossible for 1 inputSolutionFile.", - getName(), problemScale, problemSizeStatistics.approximateProblemScaleLogAsFixedPointLong()); + getName(), formattedProblemScale, problemSizeStatistics.approximateProblemScaleLogAsFixedPointLong()); // The problemScale is not unknown (null), but known to be ambiguous - problemScale = "-1"; - problemScaleLog = -1L; + formattedProblemScale = "-1"; + problemScale = -1L; } } @@ -523,8 +529,8 @@ protected static Map newResult.entityCount = oldResult.entityCount; newResult.variableCount = oldResult.variableCount; newResult.maximumValueCount = oldResult.maximumValueCount; + newResult.formattedProblemScale = oldResult.formattedProblemScale; newResult.problemScale = oldResult.problemScale; - newResult.problemScaleLog = oldResult.problemScaleLog; problemProviderToNewResultMap.put(oldResult.problemProvider, newResult); newPlannerBenchmarkResult.getUnifiedProblemBenchmarkResultList().add(newResult); } else { @@ -541,8 +547,9 @@ protected static Map newResult.variableCount = ConfigUtils.meldProperty(oldResult.variableCount, newResult.variableCount); newResult.maximumValueCount = ConfigUtils.meldProperty(oldResult.maximumValueCount, newResult.maximumValueCount); + newResult.formattedProblemScale = + ConfigUtils.meldProperty(oldResult.formattedProblemScale, newResult.formattedProblemScale); newResult.problemScale = ConfigUtils.meldProperty(oldResult.problemScale, newResult.problemScale); - newResult.problemScaleLog = ConfigUtils.meldProperty(oldResult.problemScaleLog, newResult.problemScaleLog); } mergeMap.put(oldResult, newResult); } diff --git a/tools/benchmark/src/main/resources/ai/timefold/solver/benchmark/impl/report/benchmarkReport.html.ftl b/tools/benchmark/src/main/resources/ai/timefold/solver/benchmark/impl/report/benchmarkReport.html.ftl index 235481ef52d..00e46061df0 100644 --- a/tools/benchmark/src/main/resources/ai/timefold/solver/benchmark/impl/report/benchmarkReport.html.ftl +++ b/tools/benchmark/src/main/resources/ai/timefold/solver/benchmark/impl/report/benchmarkReport.html.ftl @@ -404,7 +404,7 @@ Problem scale ${benchmarkReport.plannerBenchmarkResult.averageProblemScale!""} <#list benchmarkReport.plannerBenchmarkResult.unifiedProblemBenchmarkResultList as problemBenchmarkResult> - ${problemBenchmarkResult.problemScale!""} + ${problemBenchmarkResult.formattedProblemScale!""} @@ -472,7 +472,7 @@ Problem scale ${benchmarkReport.plannerBenchmarkResult.averageProblemScale!""} <#list benchmarkReport.plannerBenchmarkResult.unifiedProblemBenchmarkResultList as problemBenchmarkResult> - ${problemBenchmarkResult.problemScale!""} + ${problemBenchmarkResult.formattedProblemScale!""} @@ -585,7 +585,7 @@ Problem scale ${benchmarkReport.plannerBenchmarkResult.averageProblemScale!""} <#list benchmarkReport.plannerBenchmarkResult.unifiedProblemBenchmarkResultList as problemBenchmarkResult> - ${problemBenchmarkResult.problemScale!""} + ${problemBenchmarkResult.formattedProblemScale!""} @@ -668,7 +668,7 @@ Problem scale - ${problemBenchmarkResult.problemScale!""} + ${problemBenchmarkResult.formattedProblemScale!""} <#if problemBenchmarkResult.inputSolutionLoadingTimeMillisSpent??>