链式规划实体中的 CustomChangeMove 抛出 IllegalStateException (Optaplanner)
CustomChangeMove in chained planning entities throws IllegalStateException (Optaplanner)
我想对链接的计划实体进行自定义更改。
当满足某些条件时,假设我想移动 E
使其位于 B
之后,如下例所示。
A <- B <- C
和 D <- E <- F
变为 A <- C
和 D <- 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);
}
我认为以这种方式配置 previousStandstill
s 和 nextCustomer
s 会修复链条,但它却给我一个 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;
}
更好,oldTrailingEntity
和 newTrailingEntity
可以在 doMoveOnGenuinineVariables
中检索为 customer.getNextCustomer()
或 toPreviousStandstill.getNextCustomer()
。这确保检索到正确的实体,而不是某些已更改的实体。
我想对链接的计划实体进行自定义更改。
当满足某些条件时,假设我想移动 E
使其位于 B
之后,如下例所示。
A <- B <- C
和 D <- E <- F
变为 A <- C
和 D <- 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);
}
我认为以这种方式配置 previousStandstill
s 和 nextCustomer
s 会修复链条,但它却给我一个 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;
}
更好,oldTrailingEntity
和 newTrailingEntity
可以在 doMoveOnGenuinineVariables
中检索为 customer.getNextCustomer()
或 toPreviousStandstill.getNextCustomer()
。这确保检索到正确的实体,而不是某些已更改的实体。