Search code examples
optaplanner

ArrayIndexOutOfBoundsException during generating benchmarking report in OptaPlanner


So I want to run OptaPlanner's benchmarking and I have been able to succesfully implement this for various problems (tsp, vrp).

Now I am running it for a more complicated vrp, but I am being faced with an ArrayIndexOutOfBoundsException during the generation of the benchmark report.

// start benchmark
PlannerBenchmarkFactory plannerBenchmarkFactory = PlannerBenchmarkFactory.createFromXmlFile(
    new File("src/main/resources/vehiclerouting/benchmark/vrpBenchmarkConfig.xml"));

PlannerBenchmark plannerBenchmark = plannerBenchmarkFactory.buildPlannerBenchmark();
plannerBenchmark.benchmark(); //exception is thrown here.

Drilling down I found out it occurs in writeBestScoreSummaryChart() in BenchMarkReport.

private void writeBestScoreSummaryChart() {
    // Each scoreLevel has it's own dataset and chartFile
    List<DefaultCategoryDataset> datasetList = new ArrayList<>(CHARTED_SCORE_LEVEL_SIZE);
    for (SolverBenchmarkResult solverBenchmarkResult : plannerBenchmarkResult.getSolverBenchmarkResultList()) {
        String solverLabel = solverBenchmarkResult.getNameWithFavoriteSuffix();
        for (SingleBenchmarkResult singleBenchmarkResult : solverBenchmarkResult.getSingleBenchmarkResultList()) {
            String planningProblemLabel = singleBenchmarkResult.getProblemBenchmarkResult().getName();
            if (singleBenchmarkResult.hasAllSuccess()) {
                double[] levelValues = ScoreUtils.extractLevelDoubles(singleBenchmarkResult.getAverageScore());
                for (int i = 0; i < levelValues.length && i < CHARTED_SCORE_LEVEL_SIZE; i++) {
                    if (i >= datasetList.size()) {
                        datasetList.add(new DefaultCategoryDataset());
                    }
                    datasetList.get(i).addValue(levelValues[i], solverLabel, planningProblemLabel);
                }
            }
        }
    }
    bestScoreSummaryChartFileList = new ArrayList<>(datasetList.size());
    int scoreLevelIndex = 0;
    for (DefaultCategoryDataset dataset : datasetList) {
        String scoreLevelLabel = plannerBenchmarkResult.findScoreLevelLabel(scoreLevelIndex);
        CategoryPlot plot = createBarChartPlot(dataset,
                "Best " + scoreLevelLabel, NumberFormat.getInstance(locale));
        JFreeChart chart = new JFreeChart("Best " + scoreLevelLabel + " summary (higher is better)",
                JFreeChart.DEFAULT_TITLE_FONT, plot, true);
        bestScoreSummaryChartFileList.add(writeChartToImageFile(chart, "bestScoreSummaryLevel" + scoreLevelIndex));
        scoreLevelIndex++;
    }
}

In the above method datasetList reaches a size of 7. (We have 7 score levels -> 6 hard, 1 soft).

When plannerBenchmarkResult.findScoreLevelLabel(scoreLevelIndex); is called in the second loop findScoreLevelLabel() only returns 5 score levels. Namely: hard 0 score, hard 1 score hard 2 score hard 3 score and soft 0 score.

So once the scoreLevelLabel has a value >4 an ArrayIndexOutOfBoundsException is thrown. Any ideas / hints on why Optaplanner registers 5 score levels, whilst there should be 7.

EDIT Stacktrace:

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 5
at org.optaplanner.benchmark.impl.result.PlannerBenchmarkResult.findScoreLevelLabel(PlannerBenchmarkResult.java:242)
at org.optaplanner.benchmark.impl.report.BenchmarkReport.writeBestScoreSummaryChart(BenchmarkReport.java:365)
at org.optaplanner.benchmark.impl.report.BenchmarkReport.writeReport(BenchmarkReport.java:241)
at org.optaplanner.benchmark.impl.DefaultPlannerBenchmark.benchmarkingEnded(DefaultPlannerBenchmark.java:306)
at org.optaplanner.benchmark.impl.DefaultPlannerBenchmark.benchmark(DefaultPlannerBenchmark.java:104)
at benchmark.BenchMarker.main(BenchMarker.java:57)

Solution

  • I have found the issue.

    @PlanningScore(bendableHardLevelsSize = 4, bendableSoftLevelsSize = 1)
    public BendableLongScore getScore() {
        return score;
    }
    

    The appropriate score levels were not updated when more levels were added. I don't know how a wrong declaration affects Optaplanner though. Are these levels used for other things other than (de)serialization ?