Originally posted by: Petr Vilím
Hello,
you can use your step function as "intensity" of an interval variable to automatically prolong it so that it continues from one shift to another shift and doesn't count the time in between the shifts as a processing time. Yes, it doesn't split the task into smaller sub-tasks. Sometimes it is not an issue: if the task requires some resource (e.g. noOverlap or cumul function) then the machine is also stopped in between the shifts. But sometimes it is a problem: the machine is used by another workers in between the particular shifts and such posponed task would block it. Is it your case?
In the worst case you can split your task. Say for example that the task can overlap 5 shifts maximum, then create 5 interval variables part[1]..part[5]. The idea is that part[1] is (chronologically) the first part, then part[2] and so on. Duration of the parts is not known in advance but minimum duration should be 1 (I guess). Since we don't know how many parts we will need the interval variables should be marked as optional. Then connect the parts together by constraints:
endBeforeStart(part[i], part[i+1]); // Parts are chronological
presenceOf(part[i+1]) <= presenceOf(part[i]); // If part[i+1] is used then part[i] must be also used
forbidOverlap(task[i], stepFunctionsForShifts); // Parts cannot overlap breaks between shifts
taskDuration == sum(i in 1..5) lengthOf(part[i]); // Total duration of the parts
if ((i-1)*maxDurationOfAPart < taskDuration) presenceOf(part[i]); // Depending on taskDuration several first parts are known to be used
Moreover only first part can start at any time, the remaining parts must start at the beginning of a shift. We can create a step function that allows only such start times and then use it for all but not the first part:
forbidStart(part[i], NotShiftStartStepFunction);
Similarly only the last part can end at any time, remaining parts must end at the end of a shift. The issue is that we don't know in advance which part is the last one and function forbidEnd cannot be used in a logical expression. So we can model it as:
!presenceOf(part[i+1]) || (endOf(part[i], 0) in ArrayOfShiftEndsAndZero)
Note that endOf(part[i], 0) returns 0 when part[i] is absent and so ArrayOfShiftEndsAndZero must contain value 0 (you can use another value if you want) to make the constraint satisfied when part[i] is absent. In other languages than OPL the "in" operator is called IloAllowedAssignments.
There should be also max-delay constraint to make sure there isn't a whole shift between two parts. It could be achieved by:
startBeforeEnd(start[i+1], start[i], -maxLengthOfABreak)
Notice that the last parameter is negative.
Best regards,
Petr
#DecisionOptimization#OPLusingCPOptimizer