dump work
Signed-off-by: Hilmar Magnusson <hilmar.magnusson@bisdn.de>
This commit is contained in:
parent
73628577ba
commit
5d99a1fbea
11
README
11
README
|
@ -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
|
||||
|
|
|
@ -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,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,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,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
|
|
|
@ -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
|
|
244
opt.py
244
opt.py
|
@ -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)
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
pyomo
|
||||
pandas
|
||||
matplotlib
|
||||
pulp
|
||||
|
|
Loading…
Reference in New Issue