You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Optaplanner基准测试XML复现Java求解器时触发空指针异常

问题:OptaPlanner基准测试XML配置触发空指针异常(Probabilistic Change Move Selector场景)

基于Gradle使用OptaPlanner 9.44.0.Final开发的求解器,Java代码运行正常,包含Probabilistic Change Move Selector。转成XML配置用于基准测试时触发空指针异常,相关代码及异常信息如下,寻求排查解决方法。

Java求解器代码

public SolverFactory<OptaplannerSolution> customSolverFactory_10(OptaplannerAlgorithm params) {

VehicleDriverTimetableOptaplannerAlgorithm par = (VehicleDriverTimetableOptaplannerAlgorithm) params;
    
LOGGER.info("=============================================");
    LOGGER.info("=====================Solution===================");

    
    ChangeMoveSelectorConfig cms = new ChangeMoveSelectorConfig();      
    cms.setSelectionOrder(SelectionOrder.PROBABILISTIC);
    cms.setCacheType(SelectionCacheType.STEP);      
    cms.setSelectedCountLimit(100l);        
    cms.setProbabilityWeightFactoryClass(SelectionProbabilityWeightFactoryM.class); 
    
    ConstructionHeuristicForagerConfig forager = new ConstructionHeuristicForagerConfig();
    forager.setPickEarlyType(ConstructionHeuristicPickEarlyType.FIRST_FEASIBLE_SCORE_OR_NON_DETERIORATING_HARD);        
    ConstructionHeuristicPhaseConfig ch = new ConstructionHeuristicPhaseConfig()
            .withConstructionHeuristicType(ConstructionHeuristicType.FIRST_FIT_DECREASING)
            .withForagerConfig(forager);
    ch.setMoveSelectorConfigList(Collections.singletonList(cms));
    
    LocalSearchForagerConfig localSearchForager = new LocalSearchForagerConfig();
    localSearchForager.setAcceptedCountLimit(100);
    localSearchForager.setPickEarlyType(LocalSearchPickEarlyType.FIRST_LAST_STEP_SCORE_IMPROVING);
    
    LocalSearchAcceptorConfig localSearchAcceptor = new LocalSearchAcceptorConfig();
    localSearchAcceptor.setEntityTabuSize(10);
    
    LocalSearchPhaseConfig ls= new LocalSearchPhaseConfig().withForagerConfig(localSearchForager ).withAcceptorConfig(localSearchAcceptor);
    ls.setMoveSelectorConfig(cms);


    ScoreDirectorFactoryConfig sd = new ScoreDirectorFactoryConfig()
            .withEasyScoreCalculatorClass(VehicleDriverTimetableOptaplannerScoreCalculator.class);

    
    return SolverFactory.create(new SolverConfig().withSolutionClass(VehicleDriverTimetableOptaplannerSolution.class)
                    .withEntityClasses(OPService.class)
                    .withPhases(ch,ls)
                    .withScoreDirectorFactory(sd)
                    .withTerminationConfig(new TerminationConfig()
                            .withUnimprovedSecondsSpentLimit((long) par.maxUnimproved)
                            .withBestScoreLimit("0hard/0medium/0soft").withSecondsSpentLimit((long) par.maxTime)));
}

基准测试XML配置

<solverBenchmark>
    <name>BASE</name>
    <solver>
    <constructionHeuristic>
        <constructionHeuristicType>FIRST_FIT_DECREASING</constructionHeuristicType>
        <unionMoveSelector>
            <changeMoveSelector>
                <cacheType>STEP</cacheType>
                <selectionOrder>PROBABILISTIC</selectionOrder>
<probabilityWeightFactoryClass>org.eurecat.engine.dtos.problems.vehicle_driver_timetable.SelectionProbabilityWeightFactoryM</probabilityWeightFactoryClass>
                <selectedCountLimit>100</selectedCountLimit>
            </changeMoveSelector>
        </unionMoveSelector>
        <forager>
            <pickEarlyType>FIRST_FEASIBLE_SCORE_OR_NON_DETERIORATING_HARD</pickEarlyType>
        </forager>
    </constructionHeuristic>
    <localSearch>
        <acceptor>
            <entityTabuSize>10</entityTabuSize>
        </acceptor>
        <forager>
            <pickEarlyType>FIRST_LAST_STEP_SCORE_IMPROVING</pickEarlyType>
            <acceptedCountLimit>100</acceptedCountLimit>
        </forager>
    </localSearch>
    </solver>
</solverBenchmark>

异常信息

Exception in thread "main" org.optaplanner.benchmark.api.PlannerBenchmarkException: Benchmarking failed: failureCount (1). The exception of the firstFailureSingleBenchmarkRunner (Problem_0_BASE_0) is chained.at org.optaplanner.benchmark.impl.DefaultPlannerBenchmark.benchmarkingEnded(DefaultPlannerBenchmark.java:311)at org.optaplanner.benchmark.impl.DefaultPlannerBenchmark.benchmark(DefaultPlannerBenchmark.java:84)at org.optaplanner.benchmark.impl.DefaultPlannerBenchmark.benchmarkAndShowReportInBrowser(DefaultPlannerBenchmark.java:409)at org.eurecat.engine.Benchmarking.main(Benchmarking.java:225)Caused by: java.lang.NullPointerException: Cannot invoke "java.util.NavigableMap.size()" because "this.cachedMoveMap" is nullat org.optaplanner.core.impl.heuristic.selector.move.decorator.ProbabilityMoveSelector.getSize(ProbabilityMoveSelector.java:87)at org.optaplanner.core.impl.heuristic.selector.move.decorator.SelectedCountLimitMoveSelector.getSize(SelectedCountLimitMoveSelector.java:42)at org.optaplanner.core.impl.heuristic.selector.IterableSelector.spliterator(IterableSelector.java:24)at org.optaplanner.core.impl.heuristic.selector.move.composite.UnionMoveSelector.toStream(UnionMoveSelector.java:114)at org.optaplanner.core.impl.heuristic.selector.move.composite.UnionMoveSelector.iterator(UnionMoveSelector.java:94)at org.optaplanner.core.impl.constructionheuristic.placer.QueuedEntityPlacer$QueuedEntityPlacingIterator.createUpcomingSelection(QueuedEntityPlacer.java:56)at org.optaplanner.core.impl.constructionheuristic.placer.QueuedEntityPlacer$QueuedEntityPlacingIterator.createUpcomingSelection(QueuedEntityPlacer.java:31)at org.optaplanner.core.impl.heuristic.selector.common.iterator.UpcomingSelectionIterator.hasNext(UpcomingSelectionIterator.java:27)at org.optaplanner.core.impl.constructionheuristic.DefaultConstructionHeuristicPhase.solve(DefaultConstructionHeuristicPhase.java:45)at org.optaplanner.core.impl.solver.AbstractSolver.runPhases(AbstractSolver.java:83)at org.optaplanner.core.impl.solver.DefaultSolver.solve(DefaultSolver.java:193)at org.optaplanner.benchmark.impl.SubSingleBenchmarkRunner.call(SubSingleBenchmarkRunner.java:108)at org.optaplanner.benchmark.impl.SubSingleBenchmarkRunner.call(SubSingleBenchmarkRunner.java:28)at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)at java.base/java.lang.Thread.run(Thread.java:833)

排查与解决方法

异常原因分析

空指针异常来自ProbabilityMoveSelector.getSize()方法,因cachedMoveMap未初始化导致。对比Java代码与XML配置的核心差异:

  1. Java代码中,构造启发式阶段直接将ChangeMoveSelectorConfig设置为阶段的移动选择器列表,未使用UnionMoveSelector;但XML配置中用<unionMoveSelector>包裹了changeMoveSelector,这会改变OptaPlanner的初始化逻辑,导致概率选择器的缓存未被正确初始化。
  2. XML的LocalSearch阶段缺少Java代码中配置的ChangeMoveSelector,配置不一致可能引发其他潜在问题。

修复步骤

  1. 移除构造启发式中的UnionMoveSelector,直接将changeMoveSelector作为constructionHeuristic的子元素,与Java代码对齐:
<constructionHeuristic>
    <constructionHeuristicType>FIRST_FIT_DECREASING</constructionHeuristicType>
    <changeMoveSelector>
        <cacheType>STEP</cacheType>
        <selectionOrder>PROBABILISTIC</selectionOrder>
        <probabilityWeightFactoryClass>org.eurecat.engine.dtos.problems.vehicle_driver_timetable.SelectionProbabilityWeightFactoryM</probabilityWeightFactoryClass>
        <selectedCountLimit>100</selectedCountLimit>
    </changeMoveSelector>
    <forager>
        <pickEarlyType>FIRST_FEASIBLE_SCORE_OR_NON_DETERIORATING_HARD</pickEarlyType>
    </forager>
</constructionHeuristic>
  1. 补充LocalSearch阶段的移动选择器配置,确保与Java版本的求解器配置完全一致:
<localSearch>
    <acceptor>
        <entityTabuSize>10</entityTabuSize>
    </acceptor>
    <forager>
        <pickEarlyType>FIRST_LAST_STEP_SCORE_IMPROVING</pickEarlyType>
        <acceptedCountLimit>100</acceptedCountLimit>
    </forager>
    <changeMoveSelector>
        <cacheType>STEP</cacheType>
        <selectionOrder>PROBABILISTIC</selectionOrder>
        <probabilityWeightFactoryClass>org.eurecat.engine.dtos.problems.vehicle_driver_timetable.SelectionProbabilityWeightFactoryM</probabilityWeightFactoryClass>
        <selectedCountLimit>100</selectedCountLimit>
    </changeMoveSelector>
</localSearch>
  1. 验证概率权重工厂类的可访问性:确保SelectionProbabilityWeightFactoryM是public类,XML中的全类名拼写正确,且基准测试的类路径能加载到该类。

  2. 确认缓存与选择顺序的兼容性:PROBABILISTIC选择器搭配STEP缓存类型合法,移除UnionMoveSelector后,构造启发式的初始化逻辑会与Java代码一致,触发cachedMoveMap的正确创建。


内容的提问来源于stack exchange,提问作者Cristian Alarcón Sanabria

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 08:33:10