Problem overview:
We are assigning Tasks to Users Only one Task can be assigned to User The Tasks can be variable length: 1-16 hours Users can only do one Task at a time Users have 8 hours per day We are using the Time Grain pattern - 1 grain = 1 hour. As Tasks are variable length not all TimeGrains are used for a given User
Using the default sort does not return with a solution within 10min.
I've had some success (result returning within seconds) with the following configuration
<constructionHeuristic>
<changeMoveSelector>
<selectionOrder>RANDOM</selectionOrder>
<selectedCountLimit>300</selectedCountLimit>
<valueSelector variableName="user"/>
</changeMoveSelector>
<changeMoveSelector>
<valueSelector variableName="startDate"/>
</changeMoveSelector>
</constructionHeuristic>
However this seems to lead to uneven distribution of tasks. I've tried to implement a custom sort i.e.
<sorterClass>com.example.UserSelectionSorter</sorterClass>
But this gives the following error:
Exception in thread "main" java.lang.IllegalStateException: Replay must occur after record. The recordingEntitySelector (Recording(FromSolutionEntitySelector(SubTask)))'s hasNext() has not been called yet.
at org.optaplanner.core.impl.heuristic.selector.entity.mimic.MimicReplayingEntitySelector$ReplayingEntityIterator.hasNext(MimicReplayingEntitySelector.java:117)
at org.optaplanner.core.impl.heuristic.selector.common.iterator.AbstractOriginalChangeIterator.createUpcomingSelection(AbstractOriginalChangeIterator.java:47)
at org.optaplanner.core.impl.heuristic.selector.common.iterator.AbstractOriginalChangeIterator.createUpcomingSelection(AbstractOriginalChangeIterator.java:26)
at org.optaplanner.core.impl.heuristic.selector.common.iterator.UpcomingSelectionIterator.hasNext(UpcomingSelectionIterator.java:43)
at java.base/java.util.Iterator.forEachRemaining(Iterator.java:132)
at org.optaplanner.core.impl.heuristic.selector.move.decorator.AbstractCachingMoveSelector.constructCache(AbstractCachingMoveSelector.java:77)
at org.optaplanner.core.impl.heuristic.selector.move.decorator.SortingMoveSelector.constructCache(SortingMoveSelector.java:43)
at org.optaplanner.core.impl.heuristic.selector.common.SelectionCacheLifecycleBridge.phaseStarted(SelectionCacheLifecycleBridge.java:51)
at org.optaplanner.core.impl.phase.event.PhaseLifecycleSupport.firePhaseStarted(PhaseLifecycleSupport.java:37)
at org.optaplanner.core.impl.heuristic.selector.AbstractSelector.phaseStarted(AbstractSelector.java:50)
at org.optaplanner.core.impl.phase.event.PhaseLifecycleSupport.firePhaseStarted(PhaseLifecycleSupport.java:37)
at org.optaplanner.core.impl.heuristic.selector.AbstractSelector.phaseStarted(AbstractSelector.java:50)
at org.optaplanner.core.impl.phase.event.PhaseLifecycleSupport.firePhaseStarted(PhaseLifecycleSupport.java:37)
at org.optaplanner.core.impl.constructionheuristic.placer.AbstractEntityPlacer.phaseStarted(AbstractEntityPlacer.java:44)
at org.optaplanner.core.impl.constructionheuristic.DefaultConstructionHeuristicPhase.phaseStarted(DefaultConstructionHeuristicPhase.java:118)
at org.optaplanner.core.impl.constructionheuristic.DefaultConstructionHeuristicPhase.solve(DefaultConstructionHeuristicPhase.java:65)
at org.optaplanner.core.impl.solver.AbstractSolver.runPhases(AbstractSolver.java:99)
at org.optaplanner.core.impl.solver.DefaultSolver.solve(DefaultSolver.java:192)
at com.example.App.main(App.java:50)
See the advanced configuration of a Construction heuristic. That writes the mimic recording/replay explicitly. That allows you to go into the first entitySelector (so not the ref one) and use the sorterClass there.