Search code examples
optaplanner

Using a @PlanningId notation on an Instant @PlanningVariable


I am currently working on setting up a gantt-based planification problem, where a user can choose which tasks they want to plan, and OptaPlanner would do it for them.

I use incremental Java score calculation, and not the drools engine. My issue is that OptaPlanner won't take an Instant as a planning variable, as it isn't able to find a PlanningId for it.

I've been stuck on getting OptaPlanner to use multiple threads. My current model seems to be flawed, or I am not understanding how to use OptaPlanner properly.

I tried masking the Instant behind another class, but it still did not help.

My model uses only one PlanningEntity, which is a task.

Here's a simplified version of my @PlanningEntity :

@PlanningEntity(difficultyComparatorClass = TaskDifficultyComparator.class)
public class Task extends AbstractTask {
    private Machine machine;
    private Instant start;

    @PlanningId
    private Integer id;

    @PlanningVariable(valueRangeProviderRefs = {"machineRange"}, nullable = true, strengthComparatorClass = MachineStrengthComparator.class)
    public Machine getMachine() {
        return machine;
    }

    @PlanningVariable(valueRangeProviderRefs = {"timeRange"}, nullable = true, strengthComparatorClass = StartStengthComparator.class)
    public Instant getStart() {
        return start;
    }
}

In my config, I have this added to the solver tag :

<moveThreadCount>AUTO</moveThreadCount>

This gives me an exception:

Exception in thread "Thread-6" java.lang.IllegalStateException: The move thread with moveThreadIndex (0) has thrown an exception. Relayed here in the parent thread.
    at org.optaplanner.core.impl.heuristic.thread.OrderByMoveIndexBlockingQueue.take(OrderByMoveIndexBlockingQueue.java:142)
    at org.optaplanner.core.impl.localsearch.decider.MultiThreadedLocalSearchDecider.forageResult(MultiThreadedLocalSearchDecider.java:187)
    at org.optaplanner.core.impl.localsearch.decider.MultiThreadedLocalSearchDecider.decideNextStep(MultiThreadedLocalSearchDecider.java:157)
    at org.optaplanner.core.impl.localsearch.DefaultLocalSearchPhase.solve(DefaultLocalSearchPhase.java:70)
    at org.optaplanner.core.impl.solver.AbstractSolver.runPhases(AbstractSolver.java:87)
[...]
Caused by: java.lang.IllegalArgumentException: The externalObject (2019-04-16T20:31:17.162Z) cannot be looked up.
Maybe give the class (class java.time.Instant) a PlanningId annotation or change the PlanningSolution annotation's LookUpStrategyType or don't rely on functionality that depends on ScoreDirector.lookUpWorkingObject().
    at org.optaplanner.core.impl.domain.lookup.NoneLookUpStrategy.lookUpWorkingObject(NoneLookUpStrategy.java:47)
    at org.optaplanner.core.impl.domain.lookup.LookUpManager.lookUpWorkingObject(LookUpManager.java:74)
[...]

I expected OptaPlanner to use the tasks' ID, but it seems like it wants an id on each of the PlanningVariables. I am able to add an ID on the Machine, but not on the Instant.


Solution

  • A java.time.Instant is immutable, so any lookup can just return the same object instance. Just like Integer, Double, LocalDate, etc, there is no need for a @PlanningId to begin with. This exposes 3 issues in OptaPlanner:

    1. LookUpStrategyResolver.java's build-in decision cache must also include Instant. I've fixed this issue in this PR for 7.20.
    2. It should be possible to configure extra immutable classes.
    3. It should be possible to configure @PlanningId externally on 3th party classes.

    Please create a jira for 2. and 3. on issues.jboss.org project PLANNER.