我想对链式计划实体进行自定义更改。
当满足某些条件时,假设我想移动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
和nextCustomer
可以修复链条,但是却给了我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
上时,移动将使得B
在E
之后移动:A <- C <- D <- E <- B
。这将成功。以下步骤将尝试将E
放在B
之后:A <- C <- D <- B <- E
,这是引发异常的时候。任何有关如何处理此问题的想法都深表赞赏。
此外,我不明白为什么甚至执行移动操作,因为isMoveDoable
应该阻止这种情况的发生。
答案 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
中以newTrailingEntity
或doMoveOnGenuinineVariables
的形式检索customer.getNextCustomer()
和toPreviousStandstill.getNextCustomer()
。这样可以确保检索到正确的实体,而不是某些已更改的实体。