I currently have OptaPlanner solving a TSPTW problem. For the following it will help to think of the destinations as tasks.
Each task currently has a chained planning variable called previousTask. The tasks can be categorized as Type A or Type B. What I now want to do is allow Type B tasks to optionally overlap Type A tasks, letting OptaPlanner decide whether overlap is the right choice.
For example, given the tasks A1, A2, B1, OptaPlanner may decide that A1 -> B1 -> A2 is best or that A1 -> (A2 with B1 overlapping) is best.
The way I thought I could achieve this is:
- Give each Type A task a second (non-chained) planning variable called overlappingTask.
- Split the current "tasks" ValueRangeProvider into two ValueRangeProviders, "typeATasks" and "typeBTasks".
- Annotate previousTask's ValueRangeProviders to be both typeATasks and typeBTasks.
- Annotate overlappingTask's ValueRangeProviders to only be typeBTasks.
The problem I am solving will always have at least one Type A task but may not have any Type B tasks. This caused a problem with my proposed solution because the "typeBTasks" ValueRangeProvider is sometimes empty, which throws an IllegalStateException for the previousTask planning variable.
Is there a better way to approach this problem? Is there a way to get around the empty ValueRangeProvider issue? The empty complaint against previousTask seems odd given that the combination of the ValueRangeProviders isn't empty. It seems like it would be better for OptaPlanner to check whether the combination is empty, rather than each input separately.
Here are some code snippets to clarify the current design:
public Solution
{
@PlanningEntityCollectionProperty
@ValueRangeProvider(id = "typeATasks")
public List<TypeA> getTypeATasks)
@PlanningEntityCollectionProperty
@ValueRangeProvider(id = "typeBTasks")
public List<TypeB> getTypeBTasks()
}
public class Task
{
@PlanningVariable(valueRangeProviderRefs = { "typeATasks", "typeBTasks" },
graphType = PlanningVariableGraphType.CHAINED)
public Task getPreviousTask()
}
public class TaskB extends Task {}
public class TaskA extends Task
{
@PlanningVariable(valueRangeProviderRefs = { "typeBTasks" }, nullable = true)
public TaskB getOverlappingTask()
}