-
Notifications
You must be signed in to change notification settings - Fork 0
/
msci555.py
107 lines (83 loc) · 3.65 KB
/
msci555.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# -*- coding: utf-8 -*-
"""MSCI555.ipynb
Automatically generated by Colaboratory.
Original file is located at
https://colab.research.google.com/drive/1lr7r9t6_LT4on65U6SwCNN-5Jd2mEhPn
"""
pip install pulp
import pulp
import random
# Constants
num_full_time_nurses = 34
num_part_time_nurses = 20
num_nurses = num_full_time_nurses + num_part_time_nurses
num_days = 42
num_shifts = num_days * 2 # Day and night shifts for each day
max_consecutive_days = 3
max_consecutive_shifts = 5
# Generate cost matrix
# Full-time nurses have 1.5 times higher cost than part-time nurses
cost_matrix = [
[random.randint(50, 100) * (1.5 if i < num_full_time_nurses else 1) for j in range(num_shifts)] for i in range(num_nurses)
]
print(cost_matrix)
# Decision Variables
x = pulp.LpVariable.dicts("x", (range(num_nurses), range(num_shifts)), cat="Binary")
# Create Model
model = pulp.LpProblem("Nurse Scheduling", pulp.LpMinimize)
# Objective Function
model += pulp.lpSum([cost_matrix[i][j] * x[i][j] for i in range(num_nurses) for j in range(num_shifts)])
# Constraints for maximum consecutive shifts
for i in range(num_nurses):
for j in range(0, num_shifts - max_consecutive_shifts + 1):
model += pulp.lpSum([x[i][j+k] for k in range(max_consecutive_shifts + 1)]) <= max_consecutive_shifts
# Constraints for minimum nurses per shift
for j in range(0, num_shifts, 2):
model += pulp.lpSum([x[i][j] for i in range(num_nurses)]) >= 12 # At least 12 nurses for day shifts (even shifts)
for j in range(1, num_shifts, 2):
model += pulp.lpSum([x[i][j] for i in range(num_nurses)]) >= 8 # At least 8 nurses for night shifts (odd shifts)
# Constraints for maximum consecutive days
for i in range(num_nurses):
for j in range(0, num_shifts - max_consecutive_days * 2 + 1, 2):
model += pulp.lpSum([x[i][j+k] + x[i][j+k+1] for k in range(0, (max_consecutive_days + 1) * 2, 2)]) <= 2 * max_consecutive_days
# Constraints for minimum and maximum shifts per 42 days
min_shifts = 9
max_shifts = 30
for i in range(num_nurses):
model += pulp.lpSum([x[i][j] for j in range(num_shifts)]) >= min_shifts
model += pulp.lpSum([x[i][j] for j in range(num_shifts)]) <= max_shifts
# Solve Model
model.solve()
print("Status:", pulp.LpStatus[model.status])
print("Solver used:", model.solver) # Print the solver used
# Print Solution
for i in range(num_nurses):
assigned_shifts = [j for j in range(num_shifts) if x[i][j].varValue == 1]
nurse_type = "Full-time" if i < num_full_time_nurses else "Part-time"
print(f"{nurse_type} Nurse {i+1}: {', '.join(map(str, assigned_shifts))}")
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
# Prepare data for the heatmap
heatmap_data = np.zeros((num_nurses, num_days * 2))
for i in range(num_nurses):
for j in range(num_shifts):
if x[i][j].varValue == 1:
heatmap_data[i, j] = 2 if i < num_full_time_nurses else 1
# Create a custom colormap to differentiate between full-time and part-time nurses
from matplotlib.colors import ListedColormap
custom_cmap = ListedColormap(['white', 'orange', 'blue'])
# Create a heatmap
plt.figure(figsize=(15, 5))
ax = sns.heatmap(heatmap_data, cmap=custom_cmap, cbar=False, linewidths=0.5, linecolor='black')
# Set labels and title
ax.set_xlabel('Shifts')
ax.set_ylabel('Nurses')
ax.set_title('Nurse Scheduling Heatmap')
# Customize tick labels
shift_labels = ['D' + str(j//2 + 1) if j % 2 == 0 else 'N' + str(j//2 + 1) for j in range(num_shifts)]
ax.set_xticks(np.arange(num_shifts)) # Add this line to set tick locations explicitly
ax.set_xticklabels(shift_labels, rotation=90)
ax.set_yticks(np.arange(num_nurses) + 0.5)
ax.set_yticklabels(range(1, num_nurses + 1))
plt.show()