How do inheritedSolverBenchmark and solverBenchmark merge? - optaplanner

I want to experiment with different local search configurations. They all use the same neighborhood, so I defined the unionMoveSelector in the inheritedSolverBenchmark.
<inheritedSolverBenchmark>
<solver>
...
<localSearch>
<termination>
<unimprovedSecondsSpentLimit>30</unimprovedSecondsSpentLimit>
</termination>
<unionMoveSelector>
<changeMoveSelector />
<swapMoveSelector />
<pillarChangeMoveSelector>
<subPillarType>ALL</subPillarType>
</pillarChangeMoveSelector>
<pillarSwapMoveSelector>
<subPillarType>ALL</subPillarType>
</pillarSwapMoveSelector>
</unionMoveSelector>
</localSearch>
</solver>
</inheritedSolverBenchmark>
Then I created various benchmarks to compare:
<solverBenchmark>
<name>Tabu</name>
<solver>
<localSearch>
<localSearchType>TABU_SEARCH</localSearchType>
</localSearch>
</solver>
</solverBenchmark>
<solverBenchmark>
<name>LateAcceptance</name>
<solver>
<localSearch>
<localSearchType>LATE_ACCEPTANCE</localSearchType>
</localSearch>
</solver>
</solverBenchmark>
<solverBenchmark>
<name>GreatDeluge</name>
<solver>
<localSearch>
<localSearchType>GREAT_DELUGE</localSearchType>
</localSearch>
</solver>
</solverBenchmark>
<solverBenchmark>
<name>Tabu_LA</name>
<solver>
<localSearch>
<acceptor>
<entityTabuSize>7</entityTabuSize>
<lateAcceptanceSize>400</lateAcceptanceSize>
</acceptor>
<forager>
<acceptedCountLimit>1000</acceptedCountLimit>
</forager>
</localSearch>
</solver>
</solverBenchmark>
But now it seems that OptaPlanner executes three phases (construction + 2 x local search). I had expected the localSearch configuration from inheritedSolverBenchmark and plannerBenchmark to be merged. Is that not the case?

No, I am afraid it's not.
When inherited, the SolverConfig's phaseList gets list-merged, not overwritten, nor list-element-merged:
Nobody want's overwritten (as that would just ignore what's in the inherited config), so that's not it.
list-merged: some cases want this. I 've used it a lot to define a CH in the inherited config and the LS variants in the single benchmarks.
list-element-merged: what you're asking. Some cases want this.
We can't detect automatically if it's a case that needs list-merged or list-element-merged, so we had to make a choice and we went with list-merged.
Fix: Either declare it more verbosely or look at the Freemarker template support.

Related

addProblemFactChange() stops working after "Bailing out of neverEnding selector (..) to avoid infinite loop."

I have in solverConfig.xml defined filterClasses
<localSearch>
<unionMoveSelector>
<changeMoveSelector>
<filterClass>...</filterClass>
</changeMoveSelector>
<swapMoveSelector>
<filterClass>...</filterClass>
</swapMoveSelector>
<tailChainSwapMoveSelector>
<filterClass>...</filterClass>
</tailChainSwapMoveSelector>
</unionMoveSelector>
(...)
<localSearch>
And in some cases, when there is no moves in log I can see WARN's:
Bailing out of neverEnding selector (..) to avoid infinite loop.
Which is fine, but after some changes are made and addProblemFactChange is called algoritms do not start working again. Restarting works fine, so it is some problem with this method.
solver.isSolving() is returning true.
What could be the reason for this behavior?

Effects of nested unionMoveSelectors in optaPlanner

During testing and benchmarking, I unintentionally introduced an additional level of unionMoveSelector in my xml solver configuration file:
<unionMoveSelector>
<unionMoveSelector>
<changeMoveSelector>
<fixedProbabilityWeight>1.0</fixedProbabilityWeight>
<valueSelector variableName="start"/>
</changeMoveSelector>
<changeMoveSelector>
<fixedProbabilityWeight>1.0</fixedProbabilityWeight>
<valueSelector variableName="duration"/>
</changeMoveSelector>
<swapMoveSelector>
<filterClass>io.solvice.shift.domain.ShiftAssignmentSwapFilter</filterClass>
<fixedProbabilityWeight>0.1</fixedProbabilityWeight>
</swapMoveSelector>
<swapMoveSelector>
<filterClass>io.solvice.shift.domain.EmployeeSwapFilter</filterClass>
<fixedProbabilityWeight>0.1</fixedProbabilityWeight>
</swapMoveSelector>
<moveIteratorFactory>
<fixedProbabilityWeight>0.1</fixedProbabilityWeight>
<moveIteratorFactoryClass>customMoveIteratorFactory1</moveIteratorFactoryClass>
</moveIteratorFactory>
<moveIteratorFactory>
<fixedProbabilityWeight>1.0</fixedProbabilityWeight>
<moveIteratorFactoryClass>customMoveIteratorFactory2</moveIteratorFactoryClass>
</moveIteratorFactory>
</unionMoveSelector>
</unionMoveSelector>
Extensive testing (albeit on a single, hard-to-tackle instance) has shown that my solver performs better with this additional level than without.
Can someone please explain to me what this does and why it might outperform the other situation?

OptaPlanner - how to specify multiple algorithms

The OptaPlanner user guide (http://docs.jboss.org/optaplanner/release/6.4.0.Final/optaplanner-docs/html_single/index.html#whichOptimizationAlgorithmsShouldIUse) mentions "combining multiple algorithms together".
How do you specifiy that in the configuration file? The XML does not allow more than one localSearchType element. I tried a run with
<acceptor>
<entityTabuSize>7</entityTabuSize>
<lateAcceptanceSize>200</lateAcceptanceSize>
<simulatedAnnealingStartingTemperature>0hard/500soft</simulatedAnnealingStartingTemperature>
</acceptor>
and it ran, but I'm not sure what algorithms were used.
You can combine LS's and also sequence them.
Combine:
<acceptor>
<entityTabuSize>7</entityTabuSize>
<lateAcceptanceSize>200</lateAcceptanceSize>
</acceptor>
// with acceptedCounLimit 1 => LA with a bit of tabu
Sequence
<localSearch>
<termination>...</termination>
<acceptor>
<lateAcceptanceSize>200</lateAcceptanceSize>
</acceptor>
...
</localSearch>
<localSearch>
<acceptor>
<entityTabuSize>7</entityTabuSize>
</acceptor>
...
</localSearch>
// First LA, then TS
Apparently, you can use a sequence of localSearch sections.

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).

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...