链式规划实体中的 CustomChangeMove 抛出 IllegalStateException (Optaplanner)

CustomChangeMove in chained planning entities throws IllegalStateException (Optaplanner)

我想对链接的计划实体进行自定义更改。 当满足某些条件时,假设我想移动 E 使其位于 B 之后,如下例所示。

A <- B <- CD <- E <- F 变为 A <- CD <- B <- E <-F

为了实现这一点,我实现了 CustomChangeMove:

public class CustomChangeMove extends AbstractMove<VehicleRoutingSolution> {

private Customer customer;
private Customer toPreviousStandstill;

private Customer oldTrailingEntity;
private Customer newTrailingEntity;


public CustomChangemove(Customer customer, Customer toPreviousStandstill, Customer oldTrailingEntity, Customer newTrailingEntity) {
    this.customer = customer;
    this.toPreviousStandstill = toPreviousStandstill;
    this.oldTrailingEntity = oldTrailingEntity;
    this.newTrailingEntity = newTrailingEntity;
}

@Override
protected void doMoveOnGenuineVariables(ScoreDirector<VehicleRoutingSolution> scoreDirector) {
    Standstill oldPreviousStandstill = customer.getPreviousStandstill();

    scoreDirector.beforeVariableChanged(customer, "previousStandstill" );
    //fix old chain
    oldTrailingEntity.setPreviousStandstill(oldPreviousStandstill);
    // oldPreviousStandstill.setNextCustomer(oldTrailingEntity); // shadow variables are updated automatically

    // move object
    customer.setPreviousStandstill(toPreviousStandstill);
    // customer.setNextCustomer(newTrailingEntity); shadow variable

    //fix new chain
    toPreviousStandstill.setNextCustomer(customer);
    //    toPreviousStandstill.setNextCustomer(null);
    // newTrailingEntity.setPreviousStandstill(customer); // shadow variable


    scoreDirector.afterVariableChanged(customer, "previousStandstill");
}


 @Override
  public boolean isMoveDoable(ScoreDirector<VehicleRoutingSolution> scoreDirector) {
    return !Objects.equals(customer.getPreviousStandstill(), toPreviousStandstill) ||
        !Objects.equals(customer.getNextCustomer(), toPreviousStandstill);
  }

我认为以这种方式配置 previousStandstills 和 nextCustomers 会修复链条,但它却给我一个 IllegalStateException:

java.lang.IllegalStateException: The entity (Customer [shipmentId=xx, vehicle=TimeWindowedVehicle [0]]) has a variable (previousStandstill) with value (Customer [xx, vehicle=TimeWindowedVehicle [0]]) which has a sourceVariableName variable (nextCustomer) with a value (Customer [shipmentId=xxxxx-1, vehicle=TimeWindowedVehicle [0]]) which is not null.
Verify the consistency of your input problem for that sourceVariableName variable.
    at org.optaplanner.core.impl.domain.variable.inverserelation.SingletonInverseVariableListener.insert(SingletonInverseVariableListener.java:72) ~[optaplanner-core-7.0.0.Final.jar:7.0.0.Final]
    at org.optaplanner.core.impl.domain.variable.inverserelation.SingletonInverseVariableListener.afterVariableChanged(SingletonInverseVariableListener.java:51) ~[optaplanner-core-7.0.0.Final.jar:7.0.0.Final]
    at org.optaplanner.core.impl.domain.variable.listener.support.VariableListenerSupport.triggerVariableListenersInNotificationQueues(VariableListenerSupport.java:209) ~[optaplanner-core-7.0.0.Final.jar:7.0.0.Final]
    at org.optaplanner.core.impl.score.director.AbstractScoreDirector.triggerVariableListeners(AbstractScoreDirector.java:228) ~[optaplanner-core-7.0.0.Final.jar:7.0.0.Final]
    at org.optaplanner.core.impl.heuristic.move.AbstractMove.doMove(AbstractMove.java:38) ~[optaplanner-core-7.0.0.Final.jar:7.0.0.Final]
    at org.optaplanner.core.impl.heuristic.move.AbstractMove.doMove(AbstractMove.java:27) ~[optaplanner-core-7.0.0.Final.jar:7.0.0.Final]
    at org.optaplanner.core.impl.localsearch.decider.LocalSearchDecider.doMove(LocalSearchDecider.java:146) ~[optaplanner-core-7.0.0.Final.jar:7.0.0.Final]
    at org.optaplanner.core.impl.localsearch.decider.LocalSearchDecider.decideNextStep(LocalSearchDecider.java:120) ~[optaplanner-core-7.0.0.Final.jar:7.0.0.Final]
    at org.optaplanner.core.impl.localsearch.DefaultLocalSearchPhase.solve(DefaultLocalSearchPhase.java:70) ~[optaplanner-core-7.0.0.Final.jar:7.0.0.Final]
    at org.optaplanner.core.impl.solver.AbstractSolver.runPhases(AbstractSolver.java:87) ~[optaplanner-core-7.0.0.Final.jar:7.0.0.Final]
    at org.optaplanner.core.impl.solver.DefaultSolver.solve(DefaultSolver.java:163) ~[optaplanner-core-7.0.0.Final.jar:7.0.0.Final]
    at org.optaplanner.benchmark.impl.SubSingleBenchmarkRunner.call(SubSingleBenchmarkRunner.java:106) ~[optaplanner-benchmark-7.0.0.Final.jar:7.0.0.Final]
    at org.optaplanner.benchmark.impl.SubSingleBenchmarkRunner.call(SubSingleBenchmarkRunner.java:34) ~[optaplanner-benchmark-7.0.0.Final.jar:7.0.0.Final]
    at java.util.concurrent.FutureTask.run(FutureTask.java:266) ~[na:1.8.0_191]
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) ~[na:1.8.0_191]
    at java.util.concurrent.FutureTask.run(FutureTask.java:266) ~[na:1.8.0_191]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) ~[na:1.8.0_191]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) ~[na:1.8.0_191]
    at java.lang.Thread.run(Thread.java:748) ~[na:1.8.0_191]

我的另一个想法是使用默认值 ChainedChangeMove:

public class CustomChangeMoveFactory implements MoveListFactory<VehicleRoutingSolution> {

    @Override
    public List<ChainedChangeMove> createMoveList(VehicleRoutingSolution vrpSol) {
        List<ChainedChangeMove> moveList = new ArrayList<>();
        List<Customer> customers = vrpSol.getCustomers();
        for (Customer c1 : customers) {
            for (Customer c2 : customers) {
                //if certain condition met add to movelist
                moveList.add(new ChainedChangeMove(c1, variableDescriptor ?, c2, c1.getNextCustomer(), c2.getNextCustomer()))
            }
        }
        return moveList;
    }

}

但是为此我需要一个我没有的 variableDescriptor。 知道为什么我的 CustomChangeMove 失败了吗?

编辑 经过相当多的挖掘后,我觉得发生了某种循环引用。我发现该方法在抛出异常之前运行良好几次,抛出的异常通常是撤消之前所做的更改(由于我生成这些 CustomChangeMoves 的方式,这并非不可想象)。

我发现在同一链上更改时总是抛出异常。它发生在 customer 仍然有一个 nextCustomer(一个 @InverseRelationShadowVariable)未更新为 null 时,通常它是 previousStandstill 时的正常状态已更新。

因此,当在同一条链 A <- B <- C <- D <- E 上时,移动将 B 移到 E 后面:A <- C <- D <- E <- B。这会成功。下面这一步会尝试把E放在B后面:A <- C <- D <- B <- E,这个时候就会抛出异常。非常感谢有关如何处理此问题的任何想法。

此外,我不明白为什么要执行移动,因为 isMoveDoable 应该可以防止这种情况发生。

我想我明白了。到目前为止,我的实施似乎运行良好,问题似乎出现在 createUndoMove 我没有发布的错误代码中。

这段完整的代码似乎可以工作(至少没有抛出异常):

public class CustomChangeMove extends AbstractMove<VehicleRoutingSolution> {

private Customer customer;
private Customer toPreviousStandstill;

private Customer oldTrailingEntity;
private Customer newTrailingEntity;


public CustomChangemove(Customer customer, Customer toPreviousStandstill, Customer oldTrailingEntity, Customer newTrailingEntity) {
    this.customer = customer;
    this.toPreviousStandstill = toPreviousStandstill;
    this.oldTrailingEntity = oldTrailingEntity;
    this.newTrailingEntity = newTrailingEntity;
}

@Override
protected void doMoveOnGenuineVariables(ScoreDirector<VehicleRoutingSolution> scoreDirector) {
Standstill oldPreviousStandstill = customer.getPreviousStandstill();

    // move object
    scoreDirector.beforeVariableChanged(customer, "previousStandstill" );
    customer.setPreviousStandstill(toPreviousStandstill);
    scoreDirector.afterVariableChanged(customer, "previousStandstill");

    //fix old chain
    if (oldTrailingEntity != null) {
      scoreDirector.beforeVariableChanged(oldTrailingEntity, "previousStandstill" );
      oldTrailingEntity.setPreviousStandstill(oldPreviousStandstill);
      scoreDirector.afterVariableChanged(oldTrailingEntity, "previousStandstill");
}

    //fix new chain
    if (newTrailingEntity != null) {
      scoreDirector.beforeVariableChanged(newTrailingEntity, "previousStandstill" );
      newTrailingEntity.setPreviousStandstill(customer);
      scoreDirector.afterVariableChanged(newTrailingEntity, "previousStandstill");
    }
}


 @Override
  public boolean isMoveDoable(ScoreDirector<VehicleRoutingSolution> scoreDirector) {
    return !Objects.equals(customer.getPreviousStandstill(), toPreviousStandstill) ||
        !Objects.equals(customer.getNextCustomer(), toPreviousStandstill);
  }

  @Override
  protected AbstractMove<VehicleRoutingSolution> createUndoMove(ScoreDirector<VehicleRoutingSolution> scoreDirector) {
    return new MultidropChangeMove(customer, customer.getPreviousStandstill(), newTrailingEntity, oldTrailingEntity);
  }

 @Override
  public Collection<?> getPlanningEntities() {
//    return Arrays.asList(customer, newTrailingEntity, oldTrailingEntity);
    return Collections.singletonList(customer);
  }

  @Override
  public Collection<?> getPlanningValues() {
//    return Arrays.asList(customer.getPreviousStandstill(), newTrailingEntity.getPreviousStandstill(), oldTrailingEntity.getPreviousStandstill());
    return Collections.singleton(customer.getPreviousStandstill());
  }

编辑 这个答案也是不对的,这个问题恰好解决了,因为它出现在一些数据集中,而其他数据集中没有。

解决方案似乎是在移动的构建时不存储变化的实体,因为这些实体稍后会发生变化。

所以

的构造函数
public CustomChangemove(Customer customer, Customer toPreviousStandstill) {
    this.customer = customer;
    this.toPreviousStandstill = toPreviousStandstill;
}

更好,oldTrailingEntitynewTrailingEntity 可以在 doMoveOnGenuinineVariables 中检索为 customer.getNextCustomer()toPreviousStandstill.getNextCustomer()。这确保检索到正确的实体,而不是某些已更改的实体。