ai.timefold.solver.benchmark.impl.report.benchmarkReport.html.ftl Maven / Gradle / Ivy
Show all versions of timefold-solver-benchmark Show documentation
<#ftl output_format="HTML"> <#-- So that Freemarker escapes automatically. -->
${benchmarkReport.plannerBenchmarkResult.name} Planner benchmark report
<#-- Too large for a webjar. -->
<#-- Contains functions called by chart.js; must go first. -->
<#-- Includes Popper for dropdowns. -->
<#macro addSolverBenchmarkBadges solverBenchmarkResult>
<#if solverBenchmarkResult.favorite>
${solverBenchmarkResult.ranking}
<#elseif solverBenchmarkResult.ranking??>
${solverBenchmarkResult.ranking}
#if>
<#if solverBenchmarkResult.hasAnyFailure()>
F
<#elseif solverBenchmarkResult.hasAnyUninitializedSolution()>
!
<#elseif solverBenchmarkResult.hasAnyInfeasibleScore()>
!
#if>
#macro>
<#macro addProblemBenchmarkBadges problemBenchmarkResult>
<#if problemBenchmarkResult.hasAnyFailure()>
F
#if>
#macro>
<#macro addSolverProblemBenchmarkResultBadges solverProblemBenchmarkResult>
<#if solverProblemBenchmarkResult.winner>
${solverProblemBenchmarkResult.ranking}
<#elseif solverProblemBenchmarkResult.ranking??>
${solverProblemBenchmarkResult.ranking}
#if>
<#if solverProblemBenchmarkResult.hasAnyFailure()>
F
<#elseif !solverProblemBenchmarkResult.initialized>
!
<#elseif !solverProblemBenchmarkResult.scoreFeasible>
!
#if>
#macro>
<#macro addChartList chartList idPrefix>
<#assign scoreLevelIndex = 0>
<#list chartList as chart>
<#assign tabId = idPrefix + "_chart_" + scoreLevelIndex />
<@addChart chart=chart />
<#assign scoreLevelIndex = scoreLevelIndex + 1>
#list>
#macro>
<#macro addChart chart>
#macro>
Timefold Benchmark report
Result summary
<#if benchmarkReport.plannerBenchmarkResult.hasAnyFailure()>
${benchmarkReport.plannerBenchmarkResult.failureCount} benchmarks have failed!
#if>
<#list benchmarkReport.getWarningList() as warning>
${warning}
#list>
Best score summary
Useful for visualizing the best solver configuration.
<@addChartList chartList=benchmarkReport.bestScoreSummaryChartList idPrefix="summary_bestScore" />
Solver
Total
Average
Standard Deviation
Problem
<#list benchmarkReport.plannerBenchmarkResult.unifiedProblemBenchmarkResultList as problemBenchmarkResult>
${problemBenchmarkResult.name}
#list>
<#list benchmarkReport.plannerBenchmarkResult.solverBenchmarkResultList as solverBenchmarkResult>
class="table-success"#if>>
${solverBenchmarkResult.name} <@addSolverBenchmarkBadges solverBenchmarkResult=solverBenchmarkResult/>
${solverBenchmarkResult.totalScore!""}
${solverBenchmarkResult.averageScore!""}
${solverBenchmarkResult.standardDeviationString!""}
<#list benchmarkReport.plannerBenchmarkResult.unifiedProblemBenchmarkResultList as problemBenchmarkResult>
<#if !solverBenchmarkResult.findSingleBenchmark(problemBenchmarkResult)??>
<#else>
<#assign singleBenchmarkResult = solverBenchmarkResult.findSingleBenchmark(problemBenchmarkResult)>
<#if !singleBenchmarkResult.hasAllSuccess()>
Failed
<#else>
<#if solverBenchmarkResult.subSingleCount lte 1>
${singleBenchmarkResult.averageScore!""} <@addSolverProblemBenchmarkResultBadges solverProblemBenchmarkResult=singleBenchmarkResult/>
<#else>
#if>
#if>
#if>
#list>
#list>
Best score scalability summary
Useful for visualizing the scalability of each solver configuration.
<@addChartList chartList=benchmarkReport.bestScoreScalabilitySummaryChartList idPrefix="summary_bestScoreScalability" />
Best score distribution summary
Useful for visualizing the reliability of each solver configuration.
<#assign maximumSubSingleCount = benchmarkReport.plannerBenchmarkResult.getMaximumSubSingleCount()>
<#if maximumSubSingleCount lte 1>
Benchmarker did not run multiple subSingles, so there is no distribution and therefore no reliability indication.
#if>
<@addChartList chartList=benchmarkReport.bestScoreDistributionSummaryChartList idPrefix="summary_bestScoreDistribution" />
Winning score difference summary
Useful for zooming in on the results of the best score summary.
<@addChartList chartList=benchmarkReport.winningScoreDifferenceSummaryChartList idPrefix="summary_winningScoreDifference" />
Solver
Total
Average
Problem
<#list benchmarkReport.plannerBenchmarkResult.unifiedProblemBenchmarkResultList as problemBenchmarkResult>
${problemBenchmarkResult.name}
#list>
<#list benchmarkReport.plannerBenchmarkResult.solverBenchmarkResultList as solverBenchmarkResult>
class="table-success"#if>>
${solverBenchmarkResult.name} <@addSolverBenchmarkBadges solverBenchmarkResult=solverBenchmarkResult/>
${solverBenchmarkResult.totalWinningScoreDifference!""}
${solverBenchmarkResult.averageWinningScoreDifference!""}
<#list benchmarkReport.plannerBenchmarkResult.unifiedProblemBenchmarkResultList as problemBenchmarkResult>
<#if !solverBenchmarkResult.findSingleBenchmark(problemBenchmarkResult)??>
<#else>
<#assign singleBenchmarkResult = solverBenchmarkResult.findSingleBenchmark(problemBenchmarkResult)>
<#if !singleBenchmarkResult.hasAllSuccess()>
Failed
<#else>
${singleBenchmarkResult.winningScoreDifference} <@addSolverProblemBenchmarkResultBadges solverProblemBenchmarkResult=singleBenchmarkResult/>
#if>
#if>
#list>
#list>
Worst score difference percentage summary (ROI)
Useful for visualizing the return on investment (ROI) to decision makers.
<@addChartList chartList=benchmarkReport.worstScoreDifferencePercentageSummaryChartList idPrefix="summary_worstScoreDifferencePercentage" />
Solver
Average
Problem
<#list benchmarkReport.plannerBenchmarkResult.unifiedProblemBenchmarkResultList as problemBenchmarkResult>
${problemBenchmarkResult.name}
#list>
<#list benchmarkReport.plannerBenchmarkResult.solverBenchmarkResultList as solverBenchmarkResult>
class="table-success"#if>>
${solverBenchmarkResult.name} <@addSolverBenchmarkBadges solverBenchmarkResult=solverBenchmarkResult/>
<#if !solverBenchmarkResult.averageWorstScoreDifferencePercentage??>
<#else>
${solverBenchmarkResult.averageWorstScoreDifferencePercentage.toString(.locale_object)}
#if>
<#list benchmarkReport.plannerBenchmarkResult.unifiedProblemBenchmarkResultList as problemBenchmarkResult>
<#if !solverBenchmarkResult.findSingleBenchmark(problemBenchmarkResult)??>
<#else>
<#assign singleBenchmarkResult = solverBenchmarkResult.findSingleBenchmark(problemBenchmarkResult)>
<#if !singleBenchmarkResult.hasAllSuccess()>
Failed
<#else>
${singleBenchmarkResult.worstScoreDifferencePercentage.toString(.locale_object)} <@addSolverProblemBenchmarkResultBadges solverProblemBenchmarkResult=singleBenchmarkResult/>
#if>
#if>
#list>
#list>
Performance summary
Score calculation speed summary
Useful for comparing different score calculators and/or constraint implementations
(presuming that the solver configurations do not differ otherwise).
Also useful to measure the scalability cost of an extra constraint.
<@addChart chart=benchmarkReport.scoreCalculationSpeedSummaryChart />
Solver
Average
Problem
<#list benchmarkReport.plannerBenchmarkResult.unifiedProblemBenchmarkResultList as problemBenchmarkResult>
${problemBenchmarkResult.name}
#list>
Problem scale
${benchmarkReport.plannerBenchmarkResult.averageProblemScale!""}
<#list benchmarkReport.plannerBenchmarkResult.unifiedProblemBenchmarkResultList as problemBenchmarkResult>
${problemBenchmarkResult.problemScale!""}
#list>
<#list benchmarkReport.plannerBenchmarkResult.solverBenchmarkResultList as solverBenchmarkResult>
class="table-success"#if>>
${solverBenchmarkResult.name} <@addSolverBenchmarkBadges solverBenchmarkResult=solverBenchmarkResult/>
${solverBenchmarkResult.averageScoreCalculationSpeed!""}/s
<#list benchmarkReport.plannerBenchmarkResult.unifiedProblemBenchmarkResultList as problemBenchmarkResult>
<#if !solverBenchmarkResult.findSingleBenchmark(problemBenchmarkResult)??>
<#else>
<#assign singleBenchmarkResult = solverBenchmarkResult.findSingleBenchmark(problemBenchmarkResult)>
<#if !singleBenchmarkResult.hasAllSuccess()>
Failed
<#else>
<#if solverBenchmarkResult.subSingleCount lte 1>
${singleBenchmarkResult.scoreCalculationSpeed}/s
<#else>
#if>
#if>
#if>
#list>
#list>
Worst score calculation speed difference percentage
Useful for comparing different score calculators and/or constraint implementations
(presuming that the solver configurations do not differ otherwise).
Also useful to measure the scalability cost of an extra constraint.
<@addChart chart=benchmarkReport.worstScoreCalculationSpeedDifferencePercentageSummaryChart />
Solver
Average
Problem
<#list benchmarkReport.plannerBenchmarkResult.unifiedProblemBenchmarkResultList as problemBenchmarkResult>
${problemBenchmarkResult.name}
#list>
<#list benchmarkReport.plannerBenchmarkResult.solverBenchmarkResultList as solverBenchmarkResult>
class="table-success"#if>>
${solverBenchmarkResult.name} <@addSolverBenchmarkBadges solverBenchmarkResult=solverBenchmarkResult/>
<#if solverBenchmarkResult.averageWorstScoreCalculationSpeedDifferencePercentage??>
${solverBenchmarkResult.averageWorstScoreCalculationSpeedDifferencePercentage?string["0.00%"]!""}
<#else>
#if>
<#list benchmarkReport.plannerBenchmarkResult.unifiedProblemBenchmarkResultList as problemBenchmarkResult>
<#if !solverBenchmarkResult.findSingleBenchmark(problemBenchmarkResult)??>
<#else>
<#assign singleBenchmarkResult = solverBenchmarkResult.findSingleBenchmark(problemBenchmarkResult)>
<#if !singleBenchmarkResult.hasAllSuccess()>
Failed
<#else>
${singleBenchmarkResult.worstScoreCalculationSpeedDifferencePercentage?string["0.00%"]!""}
#if>
#if>
#list>
#list>
Time spent summary
Useful for visualizing the performance of construction heuristics (presuming that no other solver phases are configured).
<@addChart chart=benchmarkReport.timeSpentSummaryChart />
Solver
Average
Problem
<#list benchmarkReport.plannerBenchmarkResult.unifiedProblemBenchmarkResultList as problemBenchmarkResult>
${problemBenchmarkResult.name}
#list>
Problem scale
${benchmarkReport.plannerBenchmarkResult.averageProblemScale!""}
<#list benchmarkReport.plannerBenchmarkResult.unifiedProblemBenchmarkResultList as problemBenchmarkResult>
${problemBenchmarkResult.problemScale!""}
#list>
<#list benchmarkReport.plannerBenchmarkResult.solverBenchmarkResultList as solverBenchmarkResult>
class="table-success"#if>>
${solverBenchmarkResult.name} <@addSolverBenchmarkBadges solverBenchmarkResult=solverBenchmarkResult/>
${solverBenchmarkResult.averageTimeMillisSpent!0?string.@msDuration}
<#list benchmarkReport.plannerBenchmarkResult.unifiedProblemBenchmarkResultList as problemBenchmarkResult>
<#if !solverBenchmarkResult.findSingleBenchmark(problemBenchmarkResult)??>
<#else>
<#assign singleBenchmarkResult = solverBenchmarkResult.findSingleBenchmark(problemBenchmarkResult)>
<#if !singleBenchmarkResult.hasAllSuccess()>
Failed
<#else>
<#if solverBenchmarkResult.subSingleCount lte 1>
${singleBenchmarkResult.timeMillisSpent?string.@msDuration}
<#else>
#if>
#if>
#if>
#list>
#list>
Time spent scalability summary
Useful for extrapolating the scalability of construction heuristics (presuming that no other solver phases are configured).
<@addChart chart=benchmarkReport.timeSpentScalabilitySummaryChart />
Best score per time spent summary
Useful for visualizing trade-off between the best score versus the time spent for construction heuristics (presuming that no other solver phases are configured).
<@addChartList chartList=benchmarkReport.bestScorePerTimeSpentSummaryChartList idPrefix="summary_bestScorePerTimeSpent" />
Problem benchmarks
<#list benchmarkReport.plannerBenchmarkResult.unifiedProblemBenchmarkResultList as problemBenchmarkResult>
${problemBenchmarkResult.name}
<#if problemBenchmarkResult.hasAnyFailure()>
${problemBenchmarkResult.failureCount} benchmarks have failed!
#if>
Entity count
${problemBenchmarkResult.entityCount!""}
Variable count
${problemBenchmarkResult.variableCount!""}
Maximum value count
${problemBenchmarkResult.maximumValueCount!""}
Problem scale
${problemBenchmarkResult.problemScale!""}
<#if problemBenchmarkResult.inputSolutionLoadingTimeMillisSpent??>
Time to load input solution
<#if problemBenchmarkResult.inputSolutionLoadingTimeMillisSpent lt 1>< 1 ms<#else>${problemBenchmarkResult.inputSolutionLoadingTimeMillisSpent?string.@msDuration}#if>
#if>
<#if problemBenchmarkResult.averageUsedMemoryAfterInputSolution??>
Memory usage after loading input solution
${problemBenchmarkResult.averageUsedMemoryAfterInputSolution?string.number} bytes on average
#if>
<#if problemBenchmarkResult.hasAnySuccess() && problemBenchmarkResult.hasAnyStatistic()>
<#if problemBenchmarkResult.getMaximumSubSingleCount() gt 1>
Only the median sub single run of each solver is shown in the statistics below.
#if>
<#assign firstRow = true>
<#list problemBenchmarkResult.problemStatisticList as problemStatistic>
<#assign tabId = "problemStatistic_" + problemStatistic.anchorId />
<#-- TODO somehow figure out the solver name; sub single stats have it. -->
<#list problemStatistic.getWarningList() as warning>
${warning}
#list>
<#assign chartList = problemStatistic.getChartList()>
<#if chartList?size != 0>
<#if problemStatistic.problemStatisticType.hasScoreLevels()>
<@addChartList chartList=chartList idPrefix="problemStatistic_" + problemStatistic.anchorId />
<#else>
<@addChart chart=chartList[0] />
#if>
<#else>
Graph not available. Either the statistic is not available for this solver configuration, or the benchmark failed.
#if>
<#if !benchmarkReport.plannerBenchmarkResult.aggregation>
CSV files per solver:
<#list problemStatistic.subSingleStatisticList as subSingleStatistic>
#list>
#if>
<#assign firstRow = false>
#list>
<#list problemBenchmarkResult.extractSingleStatisticTypeList() as singleStatisticType>
<#assign tabId = "singleStatistic_" + problemBenchmarkResult.anchorId + "_" + singleStatisticType.anchorId />
<#list problemBenchmarkResult.extractPureSubSingleStatisticList(singleStatisticType) as pureSubSingleStatistic>
${pureSubSingleStatistic.subSingleBenchmarkResult.singleBenchmarkResult.solverBenchmarkResult.name}
<#assign chartList = pureSubSingleStatistic.getChartList()>
<#if chartList?size != 0>
<#if singleStatisticType.hasScoreLevels()>
<@addChartList chartList=chartList idPrefix="singleStatistic_" + problemBenchmarkResult.anchorId + "_" + singleStatisticType.anchorId />
<#else>
<@addChart chart=chartList[0] />
#if>
<#else>
Graph not available. Either the statistic is not available for this solver configuration, or the benchmark failed.
#if>
<#if !benchmarkReport.plannerBenchmarkResult.aggregation>
CSV file:
#if>
#list>
<#assign firstRow = false>
#list>
#if>
<#list problemBenchmarkResult.singleBenchmarkResultList as singleBenchmarkResult>
${singleBenchmarkResult.name}
<#if singleBenchmarkResult.hasAnyFailure()>
${singleBenchmarkResult.failureCount} benchmarks have failed!
<#else>
<#if singleBenchmarkResult.getScoreExplanationSummary()??>
<#if singleBenchmarkResult.getSubSingleCount() gt 1 >
Only the median sub single run of each solver is shown in the statistics below.
#if>
${singleBenchmarkResult.scoreExplanationSummary}
<#else>
Score summary not provided.
#if>
#if>
#list>
#list>
Solver benchmarks
<#assign firstRow = true>
<#list benchmarkReport.plannerBenchmarkResult.solverBenchmarkResultList as solverBenchmarkResult>
<#assign tabId="solverBenchmark_" + solverBenchmarkResult.anchorId + "_config" />
<#if solverBenchmarkResult.hasAnyFailure()>
${solverBenchmarkResult.failureCount} benchmarks have failed!
#if>
${solverBenchmarkResult.solverConfigAsString}
<#assign firstRow = false>
#list>
Environment Information
Name
${benchmarkReport.plannerBenchmarkResult.name}
Aggregation
${benchmarkReport.plannerBenchmarkResult.aggregation?string}
Failure count
${benchmarkReport.plannerBenchmarkResult.failureCount}
Starting timestamp
${(benchmarkReport.plannerBenchmarkResult.startingTimestampAsMediumString)!"Differs"}
Warm-up time spent
<#if benchmarkReport.plannerBenchmarkResult.warmUpTimeMillisSpentLimit??>
${benchmarkReport.plannerBenchmarkResult.warmUpTimeMillisSpentLimit?string.@msDuration}
<#else>
Differs
#if>
Parallel benchmark count / available processors
<#if benchmarkReport.plannerBenchmarkResult.parallelBenchmarkCount?? && benchmarkReport.plannerBenchmarkResult.availableProcessors??>
${benchmarkReport.plannerBenchmarkResult.parallelBenchmarkCount} / ${benchmarkReport.plannerBenchmarkResult.availableProcessors}
<#else>
Differs
#if>
Benchmark time spent
<#if benchmarkReport.plannerBenchmarkResult.benchmarkTimeMillisSpent??>
${benchmarkReport.plannerBenchmarkResult.benchmarkTimeMillisSpent?string.@msDuration}
<#else>
Differs
#if>
Environment mode
${benchmarkReport.plannerBenchmarkResult.environmentMode!"Differs"}
Logging level for ai.timefold.solver.core
${benchmarkReport.plannerBenchmarkResult.loggingLevelTimefoldSolverCore!"Differs"}
Solver ranking class
${benchmarkReport.solverRankingClassSimpleName!"Unknown"}
VM max memory (as in -Xmx but lower)
<#if (benchmarkReport.plannerBenchmarkResult.maxMemory?string.number)??>
${benchmarkReport.plannerBenchmarkResult.maxMemory?string.number} bytes
<#else>
Differs
#if>
Timefold Solver version
${benchmarkReport.plannerBenchmarkResult.timefoldSolverVersion!"Differs"}
Java version
${benchmarkReport.plannerBenchmarkResult.javaVersion!"Differs"}
Java VM
${benchmarkReport.plannerBenchmarkResult.javaVM!"Differs"}
Operating system
${benchmarkReport.plannerBenchmarkResult.operatingSystem!"Differs"}
Report locale
${benchmarkReport.locale_object!"Unknown"}
Report timezone
${benchmarkReport.timezoneId!"Unknown"}