so basically my problem is, that OptaPlanner is throwing this:
java.lang.IllegalStateException: The entity (...) has a variable (previousEntry) with value (.
experienced the same when creating a CompositeMove with multiple ChainedChange and ChainedSwapMoves. The reason for this is, that your first move might Change the PlanningValue of a PlanningEntity manipulated in a Move following that first one. While the creation of the moves already captures the Status of all the variables and values, the second move anticipates a not longer existing planning Status.
Example: chain before your Move:
(I) A -> B -> C -> D
your Composite Move: 1) ChainedChangeMove for Moving B in front of A 2) ChainedChangeMove for Moving D in front of C
in the move creation the values will be stored a shown in (I) in the moves. Move 1 will execute correct.
Solution after move 1): (II) B -> A -> C -> D
the second move will now try to move D behind B, but B is already referenced in the previousStandstill variable of A
You can create similiar scenarios with other versions of composite moves. As long as the chained moves save the status of the chain at creation of the move, union moves and compsite moves will potentially fail.
I have the same problem and managed to avoid the issue with a MoveFilter that prevents the situation Frank explains in his answer (one move affecting another in a compositMove). Here is my badly coded proof of concept for the workaround:
public class ParallelTaskMoveFilter
implements SelectionFilter<EngineerRoutingSolution, CompositeMove<EngineerRoutingSolution>> {
@Override
public boolean accept(ScoreDirector<EngineerRoutingSolution> scoreDirector,
CompositeMove<EngineerRoutingSolution> selection) {
Move<EngineerRoutingSolution>[] childMoves = selection.getMoves();
ChangeMove<EngineerRoutingSolution> firstMove = (ChangeMove<EngineerRoutingSolution>) childMoves[0];
ChangeMove<EngineerRoutingSolution> secondMove = (ChangeMove<EngineerRoutingSolution>) childMoves[1];
DailyRoute srcRoute1 = ((RouteItem) firstMove.getEntity()).getDailyRoute();
DailyRoute destRoute1 = ((RouteItem) firstMove.getToPlanningValue()).getDailyRoute();
DailyRoute srcRoute2 = ((RouteItem) secondMove.getEntity()).getDailyRoute();
DailyRoute destRoute2 = ((RouteItem) secondMove.getToPlanningValue()).getDailyRoute();
return !srcRoute1.equals(srcRoute2) && !srcRoute1.equals(destRoute2) && !destRoute2.equals(destRoute1)
&& !srcRoute2.equals(destRoute1);
}
}