Nearby selection with subChainChangeMoveSelector or subChainSwapMoveSelector - optaplanner

Is there any way to enable nearby selection in Optaplanner for the subChainChangeMoveSelector or subChainSwapMoveSelector?
I have successfully enabled it for the tail move selector as follows:
<tailChainSwapMoveSelector>
<entitySelector id="tcsm1"/>
<valueSelector>
<variableName>prevReq</variableName>
<nearbySelection>
<originEntitySelector mimicSelectorRef="tcsm1"/>
<nearbyDistanceMeterClass>NearbyMeterTransportChain</nearbyDistanceMeterClass>
</nearbySelection>
</valueSelector>
</tailChainSwapMoveSelector>
However, the same config for any of the two chained movemenents returns an XStream error, explaining that neither of them can have a entitySelector.

I just checked, the docs chapter "Move and Neighborhood Selection" does document the advanced configuration of subchain change/swap move selectors. Take a look.

Related

OptaPlanner vehicle routing course grained moves

I am working on a problem almost identical to the coachshuttlegathering example. I have wastebins and vehicles that should collect the wastebins. Each wastebin has a payload and each vehicle has a capacity.
I setup my solution as shown in the coachshuttlegathering example and this basically works but I end up in local optimas because the default moves are not coarse grained enough. Specifically, I need to allow 2 wastebin chains from 2 distinct vehicles to be assigned to a different vehicle in a single move.
I tried to reuse the existing subChainChangeMoveSelector in combination with a cartesianProductMoveSelector like this:
<cartesianProductMoveSelector>
<subChainChangeMoveSelector>
<entityClass>...Wastebin</entityClass>
<subChainSelector>
<valueSelector>
<variableName>previousRouteComponent</variableName>
</valueSelector>
</subChainSelector>
<valueSelector>
<variableName>previousRouteComponent</variableName>
</valueSelector>
<selectReversingMoveToo>false</selectReversingMoveToo>
</subChainChangeMoveSelector>
<subChainChangeMoveSelector>
<entityClass>...Wastebin</entityClass>
<subChainSelector>
<valueSelector>
<variableName>previousRouteComponent</variableName>
</valueSelector>
</subChainSelector>
<valueSelector>
<variableName>previousRouteComponent</variableName>
</valueSelector>
<selectReversingMoveToo>false</selectReversingMoveToo>
</subChainChangeMoveSelector>
<fixedProbabilityWeight>1.5</fixedProbabilityWeight>
</cartesianProductMoveSelector>
When I run the solver with this configuration, I get the following exception:
The entity (Wastebin{id=3}) has a variable (previousRouteComponent) with value (Wastebin{id=3}) which has a sourceVariableName variable (nextWastebin) with a value (Wastebin{id=1}) which is not null.
Verify the consistency of your input problem for that sourceVariableName variable.
Any idea what is going wrong here? One odd thing is that the planning entity apparently points to itself. I was thinking maybe the move selects 2 overlapping subChains which causes inconsistencies at some point?
EDIT
Turning on FULL_ASSERT yields more details:
Caused by: java.lang.IllegalStateException: UndoMove corruption: the beforeMoveScore (0/0/23/-2245/-12) is not the undoScore (0/0/23/-1358/-2) which is the uncorruptedScore (0/0/23/-1358/-2) of the workingSolution.
1) Enable EnvironmentMode FULL_ASSERT (if you haven't already) to fail-faster in case there's a score corruption.
2) Check the Move.createUndoMove(...) method of the moveClass (class org.optaplanner.core.impl.heuristic.move.CompositeMove). The move ([[Wastebin{id=3}..Wastebin{id=3}] {Wastebin{id=3} -> Vehicle{id=2}}, [Wastebin{id=3}..Wastebin{id=3}] {Wastebin{id=3} -> Wastebin{id=1}}]) might have a corrupted undoMove (Undo([[Wastebin{id=3}..Wastebin{id=3}] {Wastebin{id=3} -> Vehicle{id=2}}, [Wastebin{id=3}..Wastebin{id=3}] {Wastebin{id=3} -> Wastebin{id=1}}])).
3) Check your custom VariableListeners (if you have any) for shadow variables that are used by the score constraints with a different score weight between the beforeMoveScore (0/0/23/-2245/-12) and the undoScore (0/0/23/-1358/-2).
at org.optaplanner.core.impl.phase.scope.AbstractPhaseScope.assertExpectedUndoMoveScore(AbstractPhaseScope.java:145)
at org.optaplanner.core.impl.localsearch.decider.LocalSearchDecider.doMove(LocalSearchDecider.java:153)
at org.optaplanner.core.impl.localsearch.decider.LocalSearchDecider.decideNextStep(LocalSearchDecider.java:121)
at org.optaplanner.core.impl.localsearch.DefaultLocalSearchPhase.solve(DefaultLocalSearchPhase.java:72)
at org.optaplanner.core.impl.solver.DefaultSolver.runPhases(DefaultSolver.java:215)
at org.optaplanner.core.impl.solver.DefaultSolver.solve(DefaultSolver.java:176)
Unfortunately, the output contains move twice, I created PLANNER-599 for this issue. I got the correct undo move from the debugger:
[Wastebin{id=3}..Wastebin{id=3}] {Wastebin{id=3} -> Vehicle{id=1}}
[Wastebin{id=3}..Wastebin{id=3}] {Wastebin{id=3} -> Vehicle{id=1}}
I do not have any custom VariableListeners
This is a known issue in OptaPlanner 6.4.0.Final. It happens with complex CompositeMoves and it's due to the design of the Move interface. Fixing it is not trivial, see PLANNER-611.
Update: This is fixed for 7.0.0.CR1.
"The entity (Wastebin{id=3}) has a variable (previousRouteComponent) with value (Wastebin{id=3})"
That mean Wastebin-3 is pointing to itself? That would violate the chained variables principles. In theory, the cartesian production move shouldn't be able to cause that - but if your input problems is valid, that must be happening anyway...
Turn on environmentMode FULL_ASSERT and see if it fails earlier.

Simplest OptaPlanner example - is only construction heuristic enough?

I'm new into OptaPlanner and I'm trying to create an as simple as possible app that assigns few employees to some shifts. The only rule is that one employee can be assigned to one shift per day. I wonder if following solver configuration is not enough:
<solver>
<solutionClass>com.test.shiftplanner.ShiftPlanningSolution</solutionClass>
<entityClass>com.test.shiftplanner.ShiftAssignment</entityClass>
<scoreDirectorFactory>
<scoreDefinitionType>HARD_SOFT</scoreDefinitionType>
<scoreDrl>rules.drl</scoreDrl>
</scoreDirectorFactory>
<!-- Solver termination -->
<termination>
<secondsSpentLimit>60</secondsSpentLimit>
</termination>
<constructionHeuristic>
<constructionHeuristicType>FIRST_FIT</constructionHeuristicType>
</constructionHeuristic>
</solver>
because the collection of ShiftAssignment at ShiftPlanningSolution class remains EMPTY even though the Solver.solve() finishes and getBestSolution() returns something. What's more it seems that my rules at rules.drl are not fired at all. I even added a dummy rule just to see if it is triggered:
rule "test"
when
shiftAssignment : ShiftAssignment()
then
System.out.println(shiftAssignment);
end
and it's not fired at all.
So what are my mistakes here? Thanks in advance!
the rule should be doing something with scoreHolder, see docs chapter 5. But despite that, you should see that rule being fired once for every ShiftAssignement instance in your dataset - check if you have any in there.

Optaplanner produces only single chain solution

I am using Optaplanner to solve an installer booking assignment problem, which is a chaining problem, similar to vehicle routing. An installer(vehicle) may have multiple bookings(customer) assigned to it. I need to implement the chain because I need to evaluate one booking in relation to another booking an a particular order.
So, I declared Booking and Installer as #PlanningEntityCollectionProperty in my Solution. Both Booking and Installer implements Standstill. But only in Booking I declared #PlanningVariable for method getPreviousStandstill().
My config:
<solver>
<!--<environmentMode>FAST_ASSERT</environmentMode>-->
<!-- Domain model configuration -->
<solutionClass>InstallationSolution</solutionClass>
<entityClass>Standstill</entityClass>
<entityClass>Booking</entityClass>
<!-- Score configuration -->
<scoreDirectorFactory>
<scoreDefinitionType>HARD_SOFT</scoreDefinitionType>
<!--<easyScoreCalculatorClass>org.optaplanner.examples.cloudbalancing.solver.score.CloudBalancingEasyScoreCalculator</easyScoreCalculatorClass>-->
<!--<easyScoreCalculatorClass>org.optaplanner.examples.cloudbalancing.solver.score.CloudBalancingMapBasedEasyScoreCalculator</easyScoreCalculatorClass>-->
<!--<incrementalScoreCalculatorClass>org.optaplanner.examples.cloudbalancing.solver.score.CloudBalancingIncrementalScoreCalculator</incrementalScoreCalculatorClass>-->
<!--<scoreDrl>com/tmrnd/pejal/opta/solver/fulfillmentScoreRules.drl</scoreDrl>-->
<initializingScoreTrend>ONLY_DOWN</initializingScoreTrend>
<!--<assertionScoreDirectorFactory>-->
<!--<easyScoreCalculatorClass>org.optaplanner.examples.cloudbalancing.solver.score.CloudBalancingMapBasedEasyScoreCalculator</easyScoreCalculatorClass>-->
<!--</assertionScoreDirectorFactory>-->
</scoreDirectorFactory>
<!-- Optimization algorithms configuration -->
<termination>
<!-- <secondsSpentLimit>20</secondsSpentLimit>-->
<unimprovedSecondsSpentLimit>15</unimprovedSecondsSpentLimit>
</termination>
<constructionHeuristic>
<constructionHeuristicType>FIRST_FIT_DECREASING</constructionHeuristicType>
</constructionHeuristic>
<localSearch>
<unionMoveSelector>
<changeMoveSelector/>
<swapMoveSelector/>
<subChainChangeMoveSelector>
<selectReversingMoveToo>true</selectReversingMoveToo>
</subChainChangeMoveSelector>
<subChainSwapMoveSelector>
<selectReversingMoveToo>true</selectReversingMoveToo>
</subChainSwapMoveSelector>
</unionMoveSelector>
<acceptor>
<entityTabuSize>20</entityTabuSize>
</acceptor>
<forager>
<acceptedCountLimit>1000</acceptedCountLimit>
</forager>
</localSearch>
</solver>
When I try to solve(), I got one installer with all bookings asssigned to him. What do I need to do to improve the situation?
My initial guess would be you have only 1 anchor (1 installer) known to OptaPlanner. Check if the Solution's List's size is bigger than 1 in size. Then check if getter has #ValueRangeProvider and if the id of that ValueRangeProvider is added in the #PlanningEntity's valueRangeProviderRefs.
Also be aware of a common pitfall that might apply here too (but shouldn't if you use value range providers properly): list.add(list) instead of list.addAll(list).

OptaPlanner : FilterClass configuration in ValueSelector

I am using OptaPlanner 6.1.0.Final. When I try to use FilterClass in ValueSelector configuration, I got this exception:
Caused by: com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter$UnknownFieldException: No such field org.optaplanner.core.config.heuristic.selector.value.ValueSelectorConfig.filterClass
Acording to the OptaPlanner docs (in 7.2.4. Filtered selection), FilterClass should also work on ValueSelector.
...apply it on a lower levels, such as a filterClass on the entitySelector or valueSelector:
<changeMoveSelector>
<entitySelector>
<filterClass>...EntityFilter</filterClass>
</entitySelector>
</changeMoveSelector>
Is this a bug on the Planner? Or any other way to have Filtering class for Planning Variables?
Appreciated for your time and suggestion in advance.
Below is my configuration:
<changeMoveSelector>
<entitySelector mimicSelectorRef="placerEntitySelector">
</entitySelector>
<valueSelector>
<variableName>room</variableName>
<cacheType>PHASE</cacheType>
<selectionOrder>SORTED</selectionOrder>
<filterClass>..SelectionFilter</filterClass>
</valueSelector>
</changeMoveSelector>
<valueSelector> does not support filters yet (see TODO ValueSelectorConfig.java). It's debatable whether that's useful: if you have a good use case, do share.
Note that the surrounding <moveSelector> does support filtering. In that case, you're filtering moves (a ChangeMove in this case), instead of values - so there's more info, namely both the entity and the value, instead of just the value.

How to use the planning entity difficulty in the local search phase?

I'm trying to sort the planning entities on decreasing difficulty in the local search phase.
I tried to add the "entitySelector" in the config file like the following but it results in a ConversionException:
<localSearch>
<termination>
<maximumUnimprovedStepCount>500</maximumUnimprovedStepCount>
</termination>
<moveListFactory>
<cacheType>PHASE</cacheType> <!-- STEP, PHASE -->
<selectionOrder>RANDOM</selectionOrder>
<moveListFactoryClass>com.abcdl.be.solver.move.factory.ParentChangeMoveFactory</moveListFactoryClass>
<entitySelector>
<cacheType>PHASE</cacheType>
<selectionOrder>SORTED</selectionOrder>
<sorterManner>DECREASING_DIFFICULTY</sorterManner>
</entitySelector>
</moveListFactory>
<acceptor>
<lateAcceptanceSize>400</lateAcceptanceSize>
<entityTabuSize>5</entityTabuSize>
</acceptor>
<forager>
<pickEarlyType>NEVER</pickEarlyType> <!-- FIRST_BEST_SCORE_IMPROVING -->
<acceptedCountLimit>3</acceptedCountLimit>
</forager>
</localSearch>
The following Comparator class in annotated on the domain model :
public class NodeDifficultyComparator implements Comparator<Node>{
public int compare(Node a, Node b) {
return new CompareToBuilder()
.append(a.getResources(), b.getResources()) // the most difficult nodes are the ones who use the most resources
.append(a.getId(), b.getId())
.toComparison();
}
}
Did I choose a wrong placement for the "entitySelector" tag ? Should I do this in an another way ??
Thanks for your help.
Implementation detail: the element <moveListFactory> is defined by the class MoveListFactoryConfig. If you look at the source of that class, it does not have a field called entitySelector, so you cannot nest an <entitySelector> element in it.
There are 2 ways to solve your problem:
Instead of using <moveListFactory>, use <changeMoveSelector>, which does support an <entitySelector> element. You can then delete your custom ParentChangeMoveFactory class. The <changeMoveSelector> has many advantages over a custom MoveListFactory, such as JIT support and much more.
If you have a good reason to have a custom MoveListFactory (for example due to some complex reasons which move filtering can't cover), then adjust your ParentChangeMoveFactory to sort the entity list before generating the moves. Remember to shallow clone that list first, as you don't want your sorting to affect the entity list references by the workingSolution instance. The point is: if you write a custom MoveListFactory you're in total control on how to generate the moves, but you do need to do everything yourself...

Resources