dump work

Signed-off-by: Hilmar Magnusson <hilmar.magnusson@bisdn.de>
This commit is contained in:
Hilmar Magnusson 2024-12-02 16:39:18 +01:00
parent 73628577ba
commit 5d99a1fbea
8 changed files with 161 additions and 192 deletions

11
README
View File

@ -1,8 +1,3 @@
# Create EKG Schedule
based on https://python.plainenglish.io/solving-the-resource-constrained-project-scheduling-problem-rcpsp-with-python-and-pyomo-001cffd5344a
$ python -m venv ekg
$ source ekg/bin/activate
$ pip install -r requirements.txt
python -m venv venv
source venv/bin/activate
pip install -r requirements.txt

33
data/blocks.csv Normal file
View File

@ -0,0 +1,33 @@
day,time,available
03/12/2024,08:00:00,1
03/12/2024,08:20:00,1
03/12/2024,08:40:00,1
03/12/2024,09:00:00,1
03/12/2024,09:20:00,1
03/12/2024,09:40:00,1
03/12/2024,10:00:00,1
03/12/2024,10:20:00,1
03/12/2024,10:40:00,0
03/12/2024,11:00:00,1
03/12/2024,11:20:00,1
03/12/2024,11:40:00,0
03/12/2024,12:00:00,1
03/12/2024,12:20:00,1
03/12/2024,12:40:00,1
03/12/2024,13:00:00,1
04/12/2024,08:00:00,0
04/12/2024,08:20:00,0
04/12/2024,08:40:00,0
04/12/2024,09:00:00,1
04/12/2024,09:20:00,1
04/12/2024,09:40:00,1
04/12/2024,10:00:00,1
04/12/2024,10:20:00,1
04/12/2024,10:40:00,1
04/12/2024,11:00:00,0
04/12/2024,11:20:00,1
04/12/2024,11:40:00,1
04/12/2024,12:00:00,1
04/12/2024,12:20:00,1
04/12/2024,12:40:00,1
04/12/2024,13:00:00,0
1 day time available
2 03/12/2024 08:00:00 1
3 03/12/2024 08:20:00 1
4 03/12/2024 08:40:00 1
5 03/12/2024 09:00:00 1
6 03/12/2024 09:20:00 1
7 03/12/2024 09:40:00 1
8 03/12/2024 10:00:00 1
9 03/12/2024 10:20:00 1
10 03/12/2024 10:40:00 0
11 03/12/2024 11:00:00 1
12 03/12/2024 11:20:00 1
13 03/12/2024 11:40:00 0
14 03/12/2024 12:00:00 1
15 03/12/2024 12:20:00 1
16 03/12/2024 12:40:00 1
17 03/12/2024 13:00:00 1
18 04/12/2024 08:00:00 0
19 04/12/2024 08:20:00 0
20 04/12/2024 08:40:00 0
21 04/12/2024 09:00:00 1
22 04/12/2024 09:20:00 1
23 04/12/2024 09:40:00 1
24 04/12/2024 10:00:00 1
25 04/12/2024 10:20:00 1
26 04/12/2024 10:40:00 1
27 04/12/2024 11:00:00 0
28 04/12/2024 11:20:00 1
29 04/12/2024 11:40:00 1
30 04/12/2024 12:00:00 1
31 04/12/2024 12:20:00 1
32 04/12/2024 12:40:00 1
33 04/12/2024 13:00:00 0

View File

@ -1,9 +0,0 @@
CaseID,PatientID,Station,EKT,Erhaltung,Ket,Date
1,21-239,1,15,0,no,28/11/2024
2,21-237,0,22,1,no,29/11/2024
3,21-238,3,12,1,no,30/11/2024
4,21-248,2,12,1,no,30/11/2024
5,21-243,3,12,1,no,30/11/2024
6,21-233,3,12,1,no,30/11/2024
7,21-218,3,12,1,no,30/11/2024
8,21-208,3,12,1,no,30/11/2024
1 CaseID PatientID Station EKT Erhaltung Ket Date
2 1 21-239 1 15 0 no 28/11/2024
3 2 21-237 0 22 1 no 29/11/2024
4 3 21-238 3 12 1 no 30/11/2024
5 4 21-248 2 12 1 no 30/11/2024
6 5 21-243 3 12 1 no 30/11/2024
7 6 21-233 3 12 1 no 30/11/2024
8 7 21-218 3 12 1 no 30/11/2024
9 8 21-208 3 12 1 no 30/11/2024

View File

@ -1,12 +1,17 @@
Id,PatientID,Patient,Station
1,21-239,Hans Zimmer,1
2,21-237,Lina Gruber,0
3,21-238,Victoria J,3
4,21-229,Hilmario Maggio,3
5,21-249,Egon Schiele,2
6,21-219,Hans Landa,1
7,21-213,Gregor Samsa,1
8,21-222,Johanne,2
9,21-223,Angela Merkel,1
10,21-240,Britney Spears,3
11,21-241,Madonna,0
patient,id,station
Hr. Y (9),21-237,08b
Hr. X (5),21-239,16A
Hr. Q (12),21-235,08B
Hr. N (9),21-238,16A
Hr. Li (16/4E),21-228,16A
Hr. J,21-176,16A
Hr. Ä (17/3E),21-223,16A
Fr.O (1),21-242,16b
Fr. S (18/1E),21-227,08B
Fr. R (15/3E),21-220,16B
Fr. P (8E),21-213,16B
Fr. M (7E),21-216,16A
Fr. L (7E),21-214,16B
Fr. F (12/1E),21-234,08b
Fr. E (13),21-233,16B
Fr. A (7),21-236,16A

1 Id patient PatientID id Patient station Station
2 1 Hr. Y (9) 21-239 21-237 Hans Zimmer 08b 1
3 2 Hr. X (5) 21-237 21-239 Lina Gruber 16A 0
4 3 Hr. Q (12) 21-238 21-235 Victoria J 08B 3
5 4 Hr. N (9) 21-229 21-238 Hilmario Maggio 16A 3
6 5 Hr. Li (16/4E) 21-249 21-228 Egon Schiele 16A 2
7 6 Hr. J 21-219 21-176 Hans Landa 16A 1
8 7 Hr. Ä (17/3E) 21-213 21-223 Gregor Samsa 16A 1
9 8 Fr.O (1) 21-222 21-242 Johanne 16b 2
10 9 Fr. S (18/1E) 21-223 21-227 Angela Merkel 08B 1
11 10 Fr. R (15/3E) 21-240 21-220 Britney Spears 16B 3
12 11 Fr. P (8E) 21-241 21-213 Madonna 16B 0
13 Fr. M (7E) 21-216 16A
14 Fr. L (7E) 21-214 16B
15 Fr. F (12/1E) 21-234 08b
16 Fr. E (13) 21-233 16B
17 Fr. A (7) 21-236 16A

View File

@ -1,13 +0,0 @@
SessionID,Date,Start,End,Duration,ConsultantID,Specialty
1001,29/11/2024,08:00:00,08:20:00,20,11,Ophthalmology
1002,29/11/2024,08:20:00,08:40:00,20,11,Ophthalmology
1003,29/11/2024,08:40:00,09:00:00,20,11,Ophthalmology
1004,29/11/2024,09:00:00,09:20:00,20,11,Ophthalmology
1005,29/11/2024,09:20:00,09:40:00,20,11,Ophthalmology
1006,29/11/2024,09:40:00,10:00:00,20,11,Ophthalmology
1007,29/11/2024,10:00:00,10:20:00,20,11,Ophthalmology
1008,29/11/2024,10:20:00,10:40:00,20,11,Ophthalmology
1009,29/11/2024,10:40:00,11:00:00,20,11,Ophthalmology
1010,29/11/2024,11:00:00,11:20:00,20,11,Ophthalmology
1011,29/11/2024,11:20:00,11:40:00,20,11,Ophthalmology
1012,29/11/2024,11:40:00,12:00:00,20,11,Ophthalmology
1 SessionID Date Start End Duration ConsultantID Specialty
2 1001 29/11/2024 08:00:00 08:20:00 20 11 Ophthalmology
3 1002 29/11/2024 08:20:00 08:40:00 20 11 Ophthalmology
4 1003 29/11/2024 08:40:00 09:00:00 20 11 Ophthalmology
5 1004 29/11/2024 09:00:00 09:20:00 20 11 Ophthalmology
6 1005 29/11/2024 09:20:00 09:40:00 20 11 Ophthalmology
7 1006 29/11/2024 09:40:00 10:00:00 20 11 Ophthalmology
8 1007 29/11/2024 10:00:00 10:20:00 20 11 Ophthalmology
9 1008 29/11/2024 10:20:00 10:40:00 20 11 Ophthalmology
10 1009 29/11/2024 10:40:00 11:00:00 20 11 Ophthalmology
11 1010 29/11/2024 11:00:00 11:20:00 20 11 Ophthalmology
12 1011 29/11/2024 11:20:00 11:40:00 20 11 Ophthalmology
13 1012 29/11/2024 11:40:00 12:00:00 20 11 Ophthalmology

12
data/tasks.csv Normal file
View File

@ -0,0 +1,12 @@
id,patient,priority,date,time,duration,station
1001,21-237,1,03/12/2024,08:30:00,1,0
1002,21-239,1,04/12/2024,08:30:00,1,2
1003,21-235,11,03/12/2024,08:30:00,1,1
1004,21-227,1,03/12/2024,08:30:00,1,0
1005,21-238,2,03/12/2024,08:30:00,1,1
1006,21-240,1,04/12/2024,08:30:00,1,3
1007,21-236,1,03/12/2024,08:30:00,1,2
1008,21-228,1,03/12/2024,08:30:00,1,1
1009,21-249,1,03/12/2024,08:30:00,1,2
1010,21-241,1,03/12/2024,08:30:00,1,3
1011,21-247,1,03/12/2024,08:30:00,1,3
1 id patient priority date time duration station
2 1001 21-237 1 03/12/2024 08:30:00 1 0
3 1002 21-239 1 04/12/2024 08:30:00 1 2
4 1003 21-235 11 03/12/2024 08:30:00 1 1
5 1004 21-227 1 03/12/2024 08:30:00 1 0
6 1005 21-238 2 03/12/2024 08:30:00 1 1
7 1006 21-240 1 04/12/2024 08:30:00 1 3
8 1007 21-236 1 03/12/2024 08:30:00 1 2
9 1008 21-228 1 03/12/2024 08:30:00 1 1
10 1009 21-249 1 03/12/2024 08:30:00 1 2
11 1010 21-241 1 03/12/2024 08:30:00 1 3
12 1011 21-247 1 03/12/2024 08:30:00 1 3

244
opt.py
View File

@ -1,175 +1,121 @@
import pyomo
import pandas as pd
import pyomo.environ as pyo
import datetime
import matplotlib.cm as cm
import matplotlib.pyplot as plt
from pulp import *
class Scheduler:
def __init__(self, case_file_path, session_file_path, patient_file_path):
def __init__(self,
task_path='data/tasks.csv',
block_path='data/blocks.csv',
patient_path='data/patients.csv'):
"""
Read case and session data into Pandas DataFrames
Args:
case_file_path (str): path to case data in CSV format
session_file_path (str): path to theatre session data in CSV format
session_file_path (str): path to theatre session data in CSV format
"""
try:
self.df_cases = pd.read_csv(case_file_path)
self.df_tasks = pd.read_csv(task_path)
except FileNotFoundError:
print(f"Case data not found. {case_file_path}")
print("Task data not found.")
try:
self.df_sessions = pd.read_csv(session_file_path)
self.df_blocks = pd.read_csv(block_path)
except FileNotFoundError:
print("Session data not found")
try:
self.df_patients = pd.read_csv(patient_file_path)
self.df_patients = pd.read_csv(patient_path)
except FileNotFoundError:
print("Patient data not found")
self.solver = pyo.SolverFactory('glpk')
self.model = self.create_model()
self.build_model()
def solve_model(self):
self.solver_results = self.solver.solve(self.model, tee=True)
self.create_lists()
def extract_results(self):
results = [{"Case": case,
"Session": session,
#"Session Date": self.model.SESSION_DATES[session],
#"Case Deadline": self.model.CASE_DEADLINES[case],
#"Days before deadline": self.model.CASE_DEADLINES[case] - self.model.SESSION_DATES[session],
#"Start": self.model.CASE_START_TIME[case, session](),
"Assignment": self.model.SESSION_ASSIGNED[case, session]()}
for (case, session) in self.model.TASKS]
self.df_times = pd.DataFrame(results)
def create_lists(self):
self.s = self.df_tasks['priority'].tolist()
self.d = self.df_tasks['duration'].tolist()
self.dates = self.df_tasks['date'].tolist()
self.b = self.df_blocks['available'].tolist()
self.block_dates= self.df_blocks['day'].tolist()
def create_model(self):
return pyo.ConcreteModel()
def set_constraint(self):
self.model.c = pyo.ConstraintList()
def solve(self):
result = self.solver.solve(self.model)
print(f'result was {result}')
def build_model(self):
self.add_cases()
#self.set_options()
self.add_sessions()
self.add_tasks()
self.set_decisions()
self.set_obj()
self.patient_ids = self.df_patients['patient'].tolist()
self.task_patient= self.df_tasks['patient'].tolist()
# convert dates of tasks and blocks to (week,day)
self.convert_dates()
return
def add_cases(self):
# List of case IDs in surgical waiting list
self.model.CASES = pyo.Set(initialize=self.df_cases["CaseID"].tolist())
return
def convert_dates(self):
self.week_day = [self.date2int(date) for date in self.dates]
self.block_week_day = [self.date2int(date) for date in self.block_dates]
def set_options(self):
# Add solver parameters (time limit)
options = {"seconds": 6}
for key, value in options.items():
self.solver.options[key] = value
def date2int(self, date):
d0 = pd.to_datetime("25/11/2024", dayfirst=True)
delta = (pd.to_datetime(date, dayfirst=True) - d0).days
day = delta%7
week = int((delta - day)/7)
return week,day
def add_sessions(self):
# List of sessions IDs
# TODO: Generate more sessions based on EKT Erhaltung
self.model.SESSIONS = pyo.Set(initialize=self.df_sessions["SessionID"].tolist())
return
# List of job shop tasks
# all possible combinations of cases and sessions
def add_tasks(self):
self.model.TASKS = pyo.Set(initialize=self.model.CASES * self.model.SESSIONS, dimen=2)
return
# Decision Variables
## Upper bound (minutes in a day)
#ub = 1440
## Upper bound of session utilisation set to 85%
#max_util = 0.85
def set_decisions(self):
# Binary flag, 1 if case is assigned to session, 0 otherwise
self.model.SESSION_ASSIGNED = pyo.Var(self.model.TASKS, domain=pyo.Binary)
# Start time of a case
#self.model.CASE_START_TIME = pe.Var(self.model.TASKS, bounds=(0, ub), within=pe.PositiveReals)
# Session utilisation
num_cases = self.df_cases.shape[0]
self.model.CASES_IN_SESSION = pyo.Var(self.model.SESSIONS, bounds=(0, num_cases), within=pyo.PositiveReals)
self.model.UTILISATION = pyo.Var(self.model.SESSIONS, bounds=(0, 1), within=pyo.PositiveReals)
def set_obj(self):
# Objective
def objective_function(model):
return pyo.summation(model.CASES_IN_SESSION)
self.model.OBJECTIVE = pyo.Objective(rule=objective_function, sense=pyo.maximize)
def draw_gantt(self):
df = self.df_times[self.df_times["Assignment"] == 1]
cases = sorted(list(df['Case'].unique()))
sessions = sorted(list(df['Session'].unique()))
bar_style = {'alpha': 1.0, 'lw': 25, 'solid_capstyle': 'butt'}
text_style = {'color': 'white', 'weight': 'bold', 'ha': 'center', 'va': 'center'}
colors = cm.Dark2.colors
df.sort_values(by=['Case', 'Session'])
df.set_index(['Case', 'Session'], inplace=True)
fig, ax = plt.subplots(1, 1)
for c_ix, c in enumerate(cases, 1):
for s_ix, s in enumerate(sessions, 1):
if (c, s) in df.index:
xs = df.loc[(c, s), 'Start']
xf = df.loc[(c, s), 'Start'] + \
self.df_cases[self.df_cases["CaseID"] == c]["Median Duration"]
ax.plot([xs, xf], [s] * 2, c=colors[c_ix % 7], **bar_style)
ax.text((xs + xf) / 2, s, c, **text_style)
ax.set_title('Assigning Ophthalmology Cases to Theatre Sessions')
ax.set_xlabel('Time')
ax.set_ylabel('Sessions')
ax.grid(True)
fig.tight_layout()
plt.show()
def date2int(date):
d0 = pd.to_datetime("25/11/2024", dayfirst=True)
delta = (pd.to_datetime(date, dayfirst=True) - d0).days
day = delta%7
week = int((delta - day)/7)
return week,day
def int2date(week, day):
d0 = pd.to_datetime("25/11/2024", dayfirst=True)
delta = datetime.timedelta(days = 7*week + day)
date = d0 + delta
return date
path = '/home/hmag/git/octopusx/ekt/data'
my = Scheduler(path+'/cases.csv', path+'/sessions.csv', path+'/patients.csv')
print(my.df_cases)
print(my.df_sessions)
#print(my.df_patients)
#for i in my.df_patients:
# print(i)
print(my.df_cases['Date'][0])
date = my.df_cases['Date'][0]
print(date2int(date))
print(int2date(1,0))
def int2date(week, day):
d0 = pd.to_datetime("25/11/2024", dayfirst=True)
delta = datetime.timedelta(days = 7*week + day)
date = d0 + delta
return date
#my.set_constraint()
#my.set_obj()
my.solve_model()
sc = Scheduler()
print(sc.df_tasks)
print(sc.df_blocks)
print(sc.df_patients)
my.extract_results()
print(my.df_times)
my.draw_gantt()
# Declare variables for optimization
s = sc.s
print(f's = {s}')
d = sc.d
print(f'd = {d}')
b = sc.b
print(f'b = {b}')
B = len(b)
n = len(s)
A = sum(b)
# Import PuLP
# Define the problem
prob = LpProblem("Schedule_Tasks", LpMaximize)
# Define y
y = LpVariable.dicts('Block', [(i, t) for i in range(n) for t in range(B)], cat = 'Binary')
# Definite objective function
prob += lpSum(s[i]*b[t]*y[(i,t)] for i in range(n) for t in range(B))
# CONSTRAINTS
# Constraint #1
prob += lpSum(y[(i,t)] for i in range(n) for t in range(B)) <= A
# Constraint #2
for i in range(n):
prob += lpSum(y[(i,t)] for t in range(B)) <= d[i]
# Constraint #3
for t in range(B):
prob += lpSum(y[(i,t)] for i in range(n)) <= 1
prob.solve()
# Visualize solution
tasks_blocks = pd.DataFrame(columns=['Task', 'Block'])
for i in range(n):
for t in range(B):
if y[(i,t)].varValue == 1:
tasks_blocks = pd.concat([tasks_blocks,
pd.DataFrame({'Task': [i], 'Block': [t], 'Patient': sc.task_patient[i] })],
ignore_index=True)
print(tasks_blocks)
print(sc.dates)
print(sc.week_day)
print(sc.block_dates)
print(sc.block_week_day)

View File

@ -1,3 +1,3 @@
pyomo
pandas
matplotlib
pulp