CustomChangeMove in chained planning entities throws IllegalStateException (Optaplanner)
I want to do a custom change move to chained planning entities.
When certain conditions are met suppose I want to move E
such that it comes after B
as in the example below.
A <- B <- C
and D <- E <- F
become A <- C
and D <- B <- E <-F
To achieve this I implemented a 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);
}
I would think that configuring the previousStandstill
s and nextCustomer
s this way would fix the chains but instead it is yielding me an 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]
Another idea I have is to use the default 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;
}
}
But for that I need a variableDescriptor that I don't have.
Any ideas why my CustomChangeMove
fails ?
EDIT
After quite some digging I feel that some sort of a circular reference occurs. I find that the method runs fine a few times before throwing an exception and the exception thrown is typically undoing a change that has been done before (due to how I generate these CustomChangeMoves
this is not unthinkable).
I find that the exception is always thrown when the change is on the same chain. It occurs when the customer
still has a nextCustomer
(an @InverseRelationShadowVariable
) that is not updated to null
which normally it what it normally will be when the previousStandstill
is updated.
So, when on the same chain A <- B <- C <- D <- E
, the move will be such that to move B
behind E
: A <- C <- D <- E <- B
. This will succeed. The following move will try to put E
behind B
: A <- C <- D <- B <- E
, this is when the exception is thrown. Any ideas on how to deal with this are deeply appreciated.
Also, I don't understand why the move is even executed as isMoveDoable
should prevent this from occurring.
java optaplanner
add a comment |
I want to do a custom change move to chained planning entities.
When certain conditions are met suppose I want to move E
such that it comes after B
as in the example below.
A <- B <- C
and D <- E <- F
become A <- C
and D <- B <- E <-F
To achieve this I implemented a 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);
}
I would think that configuring the previousStandstill
s and nextCustomer
s this way would fix the chains but instead it is yielding me an 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]
Another idea I have is to use the default 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;
}
}
But for that I need a variableDescriptor that I don't have.
Any ideas why my CustomChangeMove
fails ?
EDIT
After quite some digging I feel that some sort of a circular reference occurs. I find that the method runs fine a few times before throwing an exception and the exception thrown is typically undoing a change that has been done before (due to how I generate these CustomChangeMoves
this is not unthinkable).
I find that the exception is always thrown when the change is on the same chain. It occurs when the customer
still has a nextCustomer
(an @InverseRelationShadowVariable
) that is not updated to null
which normally it what it normally will be when the previousStandstill
is updated.
So, when on the same chain A <- B <- C <- D <- E
, the move will be such that to move B
behind E
: A <- C <- D <- E <- B
. This will succeed. The following move will try to put E
behind B
: A <- C <- D <- B <- E
, this is when the exception is thrown. Any ideas on how to deal with this are deeply appreciated.
Also, I don't understand why the move is even executed as isMoveDoable
should prevent this from occurring.
java optaplanner
add a comment |
I want to do a custom change move to chained planning entities.
When certain conditions are met suppose I want to move E
such that it comes after B
as in the example below.
A <- B <- C
and D <- E <- F
become A <- C
and D <- B <- E <-F
To achieve this I implemented a 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);
}
I would think that configuring the previousStandstill
s and nextCustomer
s this way would fix the chains but instead it is yielding me an 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]
Another idea I have is to use the default 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;
}
}
But for that I need a variableDescriptor that I don't have.
Any ideas why my CustomChangeMove
fails ?
EDIT
After quite some digging I feel that some sort of a circular reference occurs. I find that the method runs fine a few times before throwing an exception and the exception thrown is typically undoing a change that has been done before (due to how I generate these CustomChangeMoves
this is not unthinkable).
I find that the exception is always thrown when the change is on the same chain. It occurs when the customer
still has a nextCustomer
(an @InverseRelationShadowVariable
) that is not updated to null
which normally it what it normally will be when the previousStandstill
is updated.
So, when on the same chain A <- B <- C <- D <- E
, the move will be such that to move B
behind E
: A <- C <- D <- E <- B
. This will succeed. The following move will try to put E
behind B
: A <- C <- D <- B <- E
, this is when the exception is thrown. Any ideas on how to deal with this are deeply appreciated.
Also, I don't understand why the move is even executed as isMoveDoable
should prevent this from occurring.
java optaplanner
I want to do a custom change move to chained planning entities.
When certain conditions are met suppose I want to move E
such that it comes after B
as in the example below.
A <- B <- C
and D <- E <- F
become A <- C
and D <- B <- E <-F
To achieve this I implemented a 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);
}
I would think that configuring the previousStandstill
s and nextCustomer
s this way would fix the chains but instead it is yielding me an 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]
Another idea I have is to use the default 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;
}
}
But for that I need a variableDescriptor that I don't have.
Any ideas why my CustomChangeMove
fails ?
EDIT
After quite some digging I feel that some sort of a circular reference occurs. I find that the method runs fine a few times before throwing an exception and the exception thrown is typically undoing a change that has been done before (due to how I generate these CustomChangeMoves
this is not unthinkable).
I find that the exception is always thrown when the change is on the same chain. It occurs when the customer
still has a nextCustomer
(an @InverseRelationShadowVariable
) that is not updated to null
which normally it what it normally will be when the previousStandstill
is updated.
So, when on the same chain A <- B <- C <- D <- E
, the move will be such that to move B
behind E
: A <- C <- D <- E <- B
. This will succeed. The following move will try to put E
behind B
: A <- C <- D <- B <- E
, this is when the exception is thrown. Any ideas on how to deal with this are deeply appreciated.
Also, I don't understand why the move is even executed as isMoveDoable
should prevent this from occurring.
java optaplanner
java optaplanner
edited Jan 2 at 3:07
k88
asked Dec 26 '18 at 7:32
k88k88
399315
399315
add a comment |
add a comment |
1 Answer
1
active
oldest
votes
I figured it out I think. So far my implementation seems to work good and the issue seems to be occurring in createUndoMove
code that I had not posted and which was wrong.
This full code seems to work (at least there are no exceptions being thrown):
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());
}
EDIT
This answer was also not right, the problem was solved coincidentally because it occured in some datasets while others were exempt.
The solution seems to be to not store the changing entities at construction time of the move as these are subjected to change later.
So a constructor of
public CustomChangemove(Customer customer, Customer toPreviousStandstill) {
this.customer = customer;
this.toPreviousStandstill = toPreviousStandstill;
}
is much better, oldTrailingEntity
and newTrailingEntity
can then be retrieved in doMoveOnGenuinineVariables
as customer.getNextCustomer()
or toPreviousStandstill.getNextCustomer()
. This ensures the correct entities are retrieved and not some entity that has been changed.
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53928884%2fcustomchangemove-in-chained-planning-entities-throws-illegalstateexception-opta%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
I figured it out I think. So far my implementation seems to work good and the issue seems to be occurring in createUndoMove
code that I had not posted and which was wrong.
This full code seems to work (at least there are no exceptions being thrown):
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());
}
EDIT
This answer was also not right, the problem was solved coincidentally because it occured in some datasets while others were exempt.
The solution seems to be to not store the changing entities at construction time of the move as these are subjected to change later.
So a constructor of
public CustomChangemove(Customer customer, Customer toPreviousStandstill) {
this.customer = customer;
this.toPreviousStandstill = toPreviousStandstill;
}
is much better, oldTrailingEntity
and newTrailingEntity
can then be retrieved in doMoveOnGenuinineVariables
as customer.getNextCustomer()
or toPreviousStandstill.getNextCustomer()
. This ensures the correct entities are retrieved and not some entity that has been changed.
add a comment |
I figured it out I think. So far my implementation seems to work good and the issue seems to be occurring in createUndoMove
code that I had not posted and which was wrong.
This full code seems to work (at least there are no exceptions being thrown):
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());
}
EDIT
This answer was also not right, the problem was solved coincidentally because it occured in some datasets while others were exempt.
The solution seems to be to not store the changing entities at construction time of the move as these are subjected to change later.
So a constructor of
public CustomChangemove(Customer customer, Customer toPreviousStandstill) {
this.customer = customer;
this.toPreviousStandstill = toPreviousStandstill;
}
is much better, oldTrailingEntity
and newTrailingEntity
can then be retrieved in doMoveOnGenuinineVariables
as customer.getNextCustomer()
or toPreviousStandstill.getNextCustomer()
. This ensures the correct entities are retrieved and not some entity that has been changed.
add a comment |
I figured it out I think. So far my implementation seems to work good and the issue seems to be occurring in createUndoMove
code that I had not posted and which was wrong.
This full code seems to work (at least there are no exceptions being thrown):
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());
}
EDIT
This answer was also not right, the problem was solved coincidentally because it occured in some datasets while others were exempt.
The solution seems to be to not store the changing entities at construction time of the move as these are subjected to change later.
So a constructor of
public CustomChangemove(Customer customer, Customer toPreviousStandstill) {
this.customer = customer;
this.toPreviousStandstill = toPreviousStandstill;
}
is much better, oldTrailingEntity
and newTrailingEntity
can then be retrieved in doMoveOnGenuinineVariables
as customer.getNextCustomer()
or toPreviousStandstill.getNextCustomer()
. This ensures the correct entities are retrieved and not some entity that has been changed.
I figured it out I think. So far my implementation seems to work good and the issue seems to be occurring in createUndoMove
code that I had not posted and which was wrong.
This full code seems to work (at least there are no exceptions being thrown):
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());
}
EDIT
This answer was also not right, the problem was solved coincidentally because it occured in some datasets while others were exempt.
The solution seems to be to not store the changing entities at construction time of the move as these are subjected to change later.
So a constructor of
public CustomChangemove(Customer customer, Customer toPreviousStandstill) {
this.customer = customer;
this.toPreviousStandstill = toPreviousStandstill;
}
is much better, oldTrailingEntity
and newTrailingEntity
can then be retrieved in doMoveOnGenuinineVariables
as customer.getNextCustomer()
or toPreviousStandstill.getNextCustomer()
. This ensures the correct entities are retrieved and not some entity that has been changed.
edited Jan 9 at 1:05
answered Jan 2 at 4:51
k88k88
399315
399315
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53928884%2fcustomchangemove-in-chained-planning-entities-throws-illegalstateexception-opta%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown