链式规划实体中的CustomChangeMove引发IllegalStateException(Optaplanner)

时间:2018-12-26 07:32:25

标签: java 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);
  }

我认为以这种方式配置previousStandstillnextCustomer可以修复链条,但是却给了我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的{​​{1}}(@InverseRelationShadowVariable)(通常为null时的previousStandstill已更新。

因此,当在同一链A <- B <- C <- D <- E上时,移动将使得BE之后移动:A <- C <- D <- E <- B。这将成功。以下步骤将尝试将E放在B之后:A <- C <- D <- B <- E,这是引发异常的时候。任何有关如何处理此问题的想法都深表赞赏。

此外,我不明白为什么甚至执行移动操作,因为isMoveDoable应该阻止这种情况的发生。

1 个答案:

答案 0 :(得分:0)

我想出来了。到目前为止,我的实现似乎运行良好,并且问题似乎发生在我尚未发布的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;
}

好得多,然后可以在oldTrailingEntity中以newTrailingEntitydoMoveOnGenuinineVariables的形式检索customer.getNextCustomer()toPreviousStandstill.getNextCustomer()。这样可以确保检索到正确的实体,而不是某些已更改的实体。