Basically, I have two employees, who could each be allocated to one of two roster patterns, which in turns allocates a set of shifts for each employee.
Allocate an employee a roster pattern -> that employee can only work shifts stated on that roster pattern, and must work all of them.
To that end, I have a constraint which basically states that the sum of all shifts each employee does should equal the input shiftsPerRP
. So if a roster pattern, e.g. 0, is selected for say employee Allan, then Allan should work exactly 45 shifts.
However this constraint keeps failing and causing an infeasible solution. I can't work out what I'm missing here. Any help is appreciated.
Relevant DAT:
days={
1, 2, 3, 4, 5, 6, 7
}
People = {
Allan,
Joe,
};
rosterPatterns = {
0,1
};
shiftsPerRP = {
<0,45>
<1,5>
};
Skills= {
A,
B,
C
};
Relevant Code:
dvar boolean Assign[days][Hours][People][Skills]; // Indicates a shift assignment
range Hours = 0..23;
forall(h in Hours, d in days, p in People, rp in rosterPatterns, shiftsInRP in shiftsPerRP : shiftsInRP.ID == rp)
sum(s in Skills)
Assign[d][h][p][s] == shiftsInRP.shifts;
In order to analyze this, label your constraints (see the added 'c1'):
forall(h in Hours,
d in days,
p in People,
rp in rosterPatterns,
shiftsInRP in shiftsPerRP
: shiftsInRP.ID == rp)
c1:
sum(s in Skills)
Assign[d][h][p][s] == shiftsInRP.shifts;
With that OPL should compute a minimal conflict that shows which constraints are infeasible. Doing that (on the command line) I found that the very first constraint generated is infeasible. The constraint has only three variables on its left-hand side but the right-hand side is 5. This can never be satisfied.
Given that you have only three elements in your set Skills
, the left-hand side of the constraint will always have 3 variables. Thus it can take values in {0, 1, 2, 3}. If for any tuple shifts
has a value greater than 3 then this constraint can obviously not be satisfied.
EDIT: It looks like you may have mixed up what should go into the forall
and what should go into the sum
. In order to formulate that correctly I think you will need an auxiliary variable
dvar boolean RosterAssigned[People][rosterPatterns];
that is 1 if a person is assigned to a roster pattern and 0 otherwise. With that you can formulate the constraint as
forall(p in People)
sum(h in Hours, s in Skills, d in days)
Assign[d][h][p][s]
== sum(rp in rosterPatterns, shiftsInRP in shiftsPerRP : shitsInRP.ID == rp) (RosterAssigned[p][rp] * shiftsInRP.shifts);
The left-hand side gives the number of shifts a person p
actually works. The right-hand side gives the number of shifts required by the roster pattern. Of course you also have to add constraints that guarantee that each employee is assigned to exactly one roster pattern.