链式计划实体中的 CustomChangeMove 会抛出 IllegalStateException (Optapla



我想对链式计划实体进行自定义更改移动。 当满足某些条件时,假设我想移动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;
}
}

但是为此,我需要一个我没有的变量描述符。 知道为什么我的CustomChangeMove失败吗?

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

我发现当更改在同一链上时,总是会抛出异常。当customer仍然有一个未更新到nullnextCustomer(@InverseRelationShadowVariable)时,就会发生这种情况,这通常是更新previousStandstill时的正常情况。

因此,当在同一链A <- B <- C <- D <- E上时,移动将使得移动到E后面BA <- 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;
}

要好得多,然后可以在doMoveOnGenuinineVariables中检索oldTrailingEntitynewTrailingEntitycustomer.getNextCustomer()toPreviousStandstill.getNextCustomer()。这可确保检索正确的实体,而不是已更改的某个实体。

最新更新