I am trying to solve a scheduling problem in OR-Tools. There are lots of "events" that need to be scheduled to "blocks" (essentially half-days). I am naturally using optional intervals for the events. One of the requirements is that the "run" of events scheduled to a particular block needs to be consecutive, i.e. there must be no gap between the end of one interval and the start of the next. I tried defining it with a new IntervalVar
for each block, representing the start, duration and end of the "run" of events and an additional constraint that the duration is equal to the sum of lengths of the events which are active for the given block. Also a NoOverlap
constraint is needed to prevent collisions between the events in the run.
Pseudo-C# code:
foreach (var group in events.GroupBy(e => e.Block))
{
var anyActive = _cpModel.NewBoolVar($"any_active_in_block_{group.Key.Block}");
_cpModel.Add(LinearExpr.Sum(group.Select(i => i.IsActive)) > 0).OnlyEnforceIf(anyActive);
_cpModel.Add(LinearExpr.Sum(group.Select(i => i.IsActive)) == 0).OnlyEnforceIf(anyActive.Not());
var runLength = _cpModel.NewIntVar(0, BLOCK_END - BLOCK_START, $"run_length_in_block_{group.Key.Block}");
var minEventStart = _cpModel.NewIntVar(BLOCK_START, BLOCK_END, $"minimum_event_start_in_block_{group.Key.Block}");
var maxEventEnd = _cpModel.NewIntVar(BLOCK_START, BLOCK_END, $"maximum_event_end_in_block_{group.Key.Block}");
_cpModel.NewIntervalVar(minEventStart, runLength, maxEventEnd, $"block_{group.Key.Block}_interval");
_cpModel.AddMinEquality(minEventStart, group.Select(e => e.Start));
_cpModel.AddMaxEquality(maxEventEnd, group.Select(e => e.End));
_cpModel.Add(runLength == LinearExpr.ScalProd(group.Select(e => e.IsActive), group.Select(e => e.Length))).OnlyEnforceIf(anyActive);
_cpModel.AddNoOverlap(group.Select(o => o.Interval));
}
While that seems to work, it causes the complexity to explode, with over 10x the number of branches in a solution when I use this code. Is this just the consequence of the size of the search space? Or is there a better way to achieve the goal? There are obviously more constraints in the problem but this one seems to have the biggest impact on performance.