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

Optaplanner自定义阶段开发疑难:无法访问Indictment Map及自定义Phase配置不被识别的解决方法

解决OptaPlanner自定义清理阶段的几个关键问题

我来帮你一步步拆解这些问题,逐个找到解决方案:

1. 无法访问IndictmentMap的问题

你碰到的IllegalStateException本质是约束匹配功能默认未开启——OptaPlanner默认关闭constraintMatchEnabled来节省性能,而IndictmentMap依赖这个功能收集约束违规信息。要开启它很简单:

  • 如果用XML配置Solver:
    <solver>
      <!-- 保留你原有的其他配置 -->
      <constraintMatchEnabled>true</constraintMatchEnabled>
    </solver>
    
  • 如果用Java代码配置:
    SolverConfig solverConfig = new SolverConfig()
        // 其他配置项
        .withConstraintMatchEnabled(true);
    

⚠️ 小提醒:开启这个功能会有轻微性能开销,但对于你的清理阶段这种一次性操作来说,完全在可接受范围内。开启后就能正常调用constraintStreamScoreDirector.getIndictmentMap()获取违规实体了。

2. 定位违反硬约束实体的替代方案

如果不想开启约束匹配功能,还有两种更轻量的方式可以找到违规站点:

方式一:手动校验实体约束

直接遍历所有站点,逐一检查硬约束是否满足:

// 假设你的Solution类中有获取所有站点的方法
for (Stop stop : scoreDirector.getWorkingSolution().getAllStops()) {
    // 这里替换成你实际的硬约束校验逻辑,比如车辆合法性、时间窗等
    if (!isStopCompliantWithHardConstraints(stop)) {
        // 标记并修改实体时,必须通知ScoreDirector跟踪变化
        scoreDirector.beforeVariableChanged(stop, "vehicle");
        stop.setVehicle(dummyVehicle);
        scoreDirector.afterVariableChanged(stop, "vehicle");
    }
}

这种方式不需要额外依赖,缺点是需要你手动复现硬约束的校验逻辑。

方式二:用约束流收集违规实体

在你的约束定义中,除了计分逻辑,额外把违反硬约束的站点收集到一个全局集合里:

// 定义线程安全的集合存储违规站点
private final Set<Stop> invalidStops = Collections.synchronizedSet(new HashSet<>());

// 在约束流中添加收集逻辑
Constraint invalidStopConstraint = constraintFactory.from(Stop.class)
    .filter(stop -> /* 这里写硬约束违反的判断条件 */)
    .penalize("Invalid Stop", HardMediumSoftScore.ONE_HARD)
    .collectInto(invalidStops);

之后在清理阶段直接访问invalidStops集合,就能拿到所有需要处理的违规站点了。

3. 让PhaseFactory识别自定义阶段的问题

你遇到的IllegalArgumentException是因为自定义了CleanUpPhaseConfig,但OptaPlanner的PhaseFactory不知道如何处理这个未知的配置类型。最省心的解决方式是不用自定义Config类,直接用OptaPlanner自带的CustomPhaseConfig:

XML配置方式

<solver>
  <!-- 先保留你的构造启发式阶段配置 -->
  <constructionHeuristic>...</constructionHeuristic>
  <!-- 添加自定义清理阶段 -->
  <customPhase>
    <customPhaseCommandClass>org.acme.CleanUpPhase</customPhaseCommandClass>
  </customPhase>
  <!-- 再添加局部搜索阶段配置 -->
  <localSearch>...</localSearch>
</solver>

Java代码配置方式

SolverConfig solverConfig = new SolverConfig()
    .withSolutionClass(Schedule.class)
    .withEntityClasses(Stop.class)
    // 添加构造启发式阶段
    .addPhaseConfig(new ConstructionHeuristicPhaseConfig())
    // 添加自定义清理阶段
    .addPhaseConfig(
        new CustomPhaseConfig()
            .withCustomPhaseCommandClass(CleanUpPhase.class)
    )
    // 添加局部搜索阶段
    .addPhaseConfig(new LocalSearchPhaseConfig());

如果确实需要给自定义阶段传递额外参数,再考虑自定义Config并实现PhaseConfigurer接口,但绝大多数场景下,上面的方法足够满足需求。

额外建议:尝试让CH阶段直接生成可行解

虽然你的问题是过度约束,但可以试试给构造启发式阶段开启可行性优先模式,让它优先满足硬约束:

<constructionHeuristic>
  <constructionHeuristicType>FIRST_FIT_DECREASING</constructionHeuristicType>
  <feasibilityEnabled>true</feasibilityEnabled>
</constructionHeuristic>

开启后CH会优先选择不违反硬约束的分配方式,如果问题确实过度约束,再用你的清理阶段兜底即可。


内容的提问来源于stack exchange,提问作者Magnus Våge

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 17:18:10