001: // --------------------------------------------------------------------------
002: // Licensed Materials - Property of IBM
003: //
004: // 5725-A06 5725-A29 5724-Y48 5724-Y49 5724-Y54 5724-Y55
005: // Copyright IBM Corporation 1998, 2013. All Rights Reserved.
006: //
007: // Note to U.S. Government Users Restricted Rights:
008: // Use, duplication or disclosure restricted by GSA ADP Schedule
009: // Contract with IBM Corp.
010: // --------------------------------------------------------------------------
011: 
012: {string} Weekdays = ...;  
013: tuple nurse {
014:   key string name;
015:   int seniority;
016:   int qualification;
017:   int payRate;
018: }
019: 
020: tuple shift {
021:    key string departmentName;
022:    key string day;
023:    key int startTime;
024:    key int endTime;
025:    int minRequirement;
026:    int maxRequirement;   
027: }
028: 
029: tuple nurseCouple {
030:   nurse Nurse1;
031:   nurse Nurse2;
032: }
033: 
034: tuple departmentIncompat {
035:   nurse Nurse;
036:   string department;
037: }
038: 
039: //tuple workTimeByNurse {
040: //   string Nurse;
041: //   int minTime;
042: //   int maxTime;
043: //}
044: 
045: tuple skillRequirement {
046:   key string department;
047:   key string skill;
048:   int number;
049: }
050: 
051: int MaxWorkTime = ...;
052: {nurse} Nurses = ...;
053: {shift} Shifts = ...;
054: {string} Departments = ...;
055: 
056: {skillRequirement} SkillRequirements = ...;
057: 
058: {nurseCouple} NurseAssoc 
059:   with Nurse1 in Nurses,Nurse2 in Nurses = ...;
060: {nurseCouple} NurseIncompat 
061:   with Nurse1 in Nurses, Nurse2 in Nurses = ...;
062: {departmentIncompat} DepartmentIncompat
063:   with Nurse in Nurses, department in Departments  = ...;
064:   
065: int Vacations[Nurses][Weekdays] = ...;
066: //{WorkTimeByNurse} WorkTimeByNurses = ...;
067: 
068: {string} Skills = ...;
069: int NurseSkill[Nurses][Skills] = ...;
070: 
071: int RequiredAssignments[Nurses][Shifts] = ...;
072: 
073: int AbsStartTime[s in Shifts]= s.startTime + ord(Weekdays,s.day)*24;
074: int AbsEndTime[s in Shifts] = 
075:   s.endTime + ord(Weekdays,s.day)*24
076:   +(( s.startTime > s.endTime )?24:0);
077:   
078: int Duration[s in Shifts] = AbsEndTime[s] - AbsStartTime[s];
079: 
080: int incompatShifts[Shifts][Shifts] = [ s1 : [ s2 : 1 ] | s1,s2 in Shifts: AbsStartTime[s2]>=AbsStartTime[s1] && AbsStartTime[s2]<AbsEndTime[s1] ];
081: 
082: dvar int NurseAssignments[Nurses][Shifts] in 0..1;
083: dvar float+ NurseWorkTime[Nurses];
084: dvar float+ NurseAvgHours;
085: dvar float+ NurseMoreThanAvgHours[Nurses];
086: dvar float+ NurseLessThanAvgHours[Nurses];
087: dvar float+ Fairness;
088: dvar int CostByDepartments[Departments];
089: dvar int AllocationByDepartments[Departments];
090: 
091: 
092: minimize 
093:   sum(d in Departments) 
094:     CostByDepartments[d];
095: 
096: subject to {
097:    // cost by department
098:    forall( d in Departments )
099:      CostByDepartments[d] == 
100:      sum( s in Shifts , n in Nurses : s.departmentName == d ) 
101:        NurseAssignments[n][s] * Duration[s] * n.payRate;
102:    
103:    // allocation by department
104:    forall(d in Departments)
105:       AllocationByDepartments[d] == sum(s in Shifts, n in Nurses : s.departmentName == d) NurseAssignments[n][s];
106:    
107:    // a shift require between min and max Nurses 
108:    forall(s in Shifts) 
109:      s.minRequirement <= sum(n in Nurses) NurseAssignments[n][s] <= s.maxRequirement;
110:    
111:    forall(n in Nurses) {      
112:        // time worked by a Nurse
113:       NurseWorkTime[n] == sum( s in Shifts ) 
114:         NurseAssignments[n][s]*Duration[s];
115: 
116:       // two shifts at the same time are incompatible
117:       forall( s in Shifts )
118:          sum( s2 in Shifts : incompatShifts[s,s2]==1 )
119:            NurseAssignments[n][s2] <=1;         
120:    }
121:    // respect required assignments
122:      forall( n in Nurses, s in Shifts : RequiredAssignments[n][s] == 1 )
123:        ctRequiredAssignmentConstraints:
124:          NurseAssignments[n][s] == 1;
125:    //global max worked time
126:      forall(n in Nurses)
127:        ctNurseMaxTimeConstraints: 
128:          NurseWorkTime[n] <= MaxWorkTime;
129: 
130:    // Nurse-Nurse incompatibility
131:      forall( < n1 , n2 > in NurseIncompat , s in Shifts )
132:        ctNurseIncompatConstraints:
133:          NurseAssignments[n1][s] + NurseAssignments[n2][s] <= 1; 
134: 
135:    // Nurse association
136:      forall( < n1 , n2 > in NurseAssoc, s in Shifts)
137:        ctNurseAssocConstraints:
138:          NurseAssignments[n1][s] == NurseAssignments[n2][s]; 
139:   
140:    // Nurse-shift incompatibility
141:    forall(s in Shifts, <n, s.departmentName > in DepartmentIncompat)
142:       NurseAssignments[n][s] == 0;
143:    
144:    // max worked time
145:    // forall( t in workTimeByNurses , n in Nurses : n.name == t.Nurse )
146:    //   WorkTimeByNurseConstraints[n]=t.minTime <= NurseWorkTime[n] <= t.maxTime;
147: 
148:    // Nurse vacations
149:    forall( n in Nurses , d in Weekdays : Vacations[n][d] == 1 )
150:      ctNurseVacationConstraints:
151:        sum(s in Shifts : s.day == d) NurseAssignments[n][s] == 0;
152: 
153:    // skills
154:    forall(r in SkillRequirements, s in Shifts : r.department == s.departmentName)
155:      ctSkillRequirementConstraints:
156:        sum(n in Nurses : NurseSkill[n][r.skill] == 1) NurseAssignments[n][s] >= r.number;   
157:   
158:    forall( n in Nurses )
159:       NurseWorkTime[n] == NurseAvgHours + NurseMoreThanAvgHours[n] - NurseLessThanAvgHours[n];
160: 
161:    Fairness == sum(n in Nurses) (NurseMoreThanAvgHours[n] + NurseLessThanAvgHours[n]);
162:       NurseAvgHours == ((card(Nurses) == 0) ? 0  : sum(n in Nurses) NurseWorkTime[n] / card(Nurses));
163: }
164: 
165: 
166: tuple CostByDepartmentsSolutionT{ 
167:         string Departments; 
168:         int value; 
169: };
170: {CostByDepartmentsSolutionT} CostByDepartmentsSolution = {<i0,CostByDepartments[i0]> | i0 in Departments};
171: tuple NurseAssignmentsSolutionT{ 
172:         nurse Nurses; 
173:         shift Shifts;   
174:         int value; 
175: };
176: {NurseAssignmentsSolutionT} NurseAssignmentsSolution = {<i0,i1,NurseAssignments[i0][i1]> | i0 in Nurses,i1 in Shifts};
177: tuple AllocationByDepartmentsSolutionT{ 
178:         string Departments; 
179:         int value; 
180: };
181: {AllocationByDepartmentsSolutionT} AllocationByDepartmentsSolution = {<i0,AllocationByDepartments[i0]> | i0 in Departments};
182: tuple NurseWorkTimeSolutionT{ 
183:         nurse Nurses;   
184:         float value; 
185: };
186: {NurseWorkTimeSolutionT} NurseWorkTimeSolution = {<i0,NurseWorkTime[i0]> | i0 in Nurses};
187: tuple NurseMoreThanAvgHoursSolutionT{ 
188:         nurse Nurses;   
189:         float value; 
190: };
191: {NurseMoreThanAvgHoursSolutionT} NurseMoreThanAvgHoursSolution = {<i0,NurseMoreThanAvgHours[i0]> | i0 in Nurses};
192: tuple NurseLessThanAvgHoursSolutionT{ 
193:         nurse Nurses;   
194:         float value; 
195: };
196: {NurseLessThanAvgHoursSolutionT} NurseLessThanAvgHoursSolution = {<i0,NurseLessThanAvgHours[i0]> | i0 in Nurses};
197: