Compare commits

..

No commits in common. "1d83544eabe4ace9e45e4890dcccae2a82bb1dd3" and "edc47f65840df70c9b748da4dd9336629065a56f" have entirely different histories.

6 changed files with 227 additions and 182 deletions

29
LICENSE
View File

@ -1,29 +0,0 @@
BSD 3-Clause License
Copyright (c) 2020, Lewis Woolfson
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

26
data/case_data.csv Normal file
View File

@ -0,0 +1,26 @@
CaseID,ConsultantID,Procedure,Speciality,Median Duration,TargetDeadline
1,11,Cataract Surgery,Ophthalmology,45,2020-06-07
2,11,Vitrectomy ,Ophthalmology,70,2020-07-17
3,11,Cataract Surgery,Ophthalmology,45,2020-06-05
4,11,Cataract Surgery,Ophthalmology,45,2020-06-28
5,11,Cataract Surgery,Ophthalmology,45,2020-07-22
6,11,Cataract Surgery,Ophthalmology,45,2020-07-03
7,11,Cataract Surgery,Ophthalmology,45,2020-07-03
8,11,Eyelid Lesion Excision,Ophthalmology,30,2020-06-11
9,11,Vitrectomy ,Ophthalmology,70,2020-07-04
10,11,Cataract Surgery,Ophthalmology,45,2020-07-14
11,11,Cataract Surgery,Ophthalmology,45,2020-07-06
12,11,Cataract Surgery,Ophthalmology,45,2020-07-16
13,11,Trabeculectomy,Ophthalmology,100,2020-07-11
14,11,Vitrectomy ,Ophthalmology,70,2020-06-15
15,11,Vitrectomy ,Ophthalmology,70,2020-06-20
16,11,Cataract Surgery,Ophthalmology,45,2020-06-18
17,11,Cataract Surgery,Ophthalmology,45,2020-07-20
18,11,Trabeculectomy,Ophthalmology,100,2020-07-04
19,11,Eyelid Skin Graft,Ophthalmology,120,2020-06-15
20,11,Cataract Surgery,Ophthalmology,45,2020-07-13
21,11,Cataract Surgery,Ophthalmology,45,2020-07-04
22,11,Cataract Surgery,Ophthalmology,45,2020-06-28
23,11,Cataract Surgery,Ophthalmology,45,2020-07-22
24,11,Cataract Surgery,Ophthalmology,45,2020-07-06
25,11,Vitrectomy ,Ophthalmology,70,2020-06-08
1 CaseID ConsultantID Procedure Speciality Median Duration TargetDeadline
2 1 11 Cataract Surgery Ophthalmology 45 2020-06-07
3 2 11 Vitrectomy  Ophthalmology 70 2020-07-17
4 3 11 Cataract Surgery Ophthalmology 45 2020-06-05
5 4 11 Cataract Surgery Ophthalmology 45 2020-06-28
6 5 11 Cataract Surgery Ophthalmology 45 2020-07-22
7 6 11 Cataract Surgery Ophthalmology 45 2020-07-03
8 7 11 Cataract Surgery Ophthalmology 45 2020-07-03
9 8 11 Eyelid Lesion Excision Ophthalmology 30 2020-06-11
10 9 11 Vitrectomy  Ophthalmology 70 2020-07-04
11 10 11 Cataract Surgery Ophthalmology 45 2020-07-14
12 11 11 Cataract Surgery Ophthalmology 45 2020-07-06
13 12 11 Cataract Surgery Ophthalmology 45 2020-07-16
14 13 11 Trabeculectomy Ophthalmology 100 2020-07-11
15 14 11 Vitrectomy  Ophthalmology 70 2020-06-15
16 15 11 Vitrectomy  Ophthalmology 70 2020-06-20
17 16 11 Cataract Surgery Ophthalmology 45 2020-06-18
18 17 11 Cataract Surgery Ophthalmology 45 2020-07-20
19 18 11 Trabeculectomy Ophthalmology 100 2020-07-04
20 19 11 Eyelid Skin Graft Ophthalmology 120 2020-06-15
21 20 11 Cataract Surgery Ophthalmology 45 2020-07-13
22 21 11 Cataract Surgery Ophthalmology 45 2020-07-04
23 22 11 Cataract Surgery Ophthalmology 45 2020-06-28
24 23 11 Cataract Surgery Ophthalmology 45 2020-07-22
25 24 11 Cataract Surgery Ophthalmology 45 2020-07-06
26 25 11 Vitrectomy  Ophthalmology 70 2020-06-08

View File

@ -1,31 +1,31 @@
CaseID,ConsultantID,Procedure,Speciality,Median Duration,TargetDeadline CaseID,ConsultantID,Procedure,Speciality,Median Duration,TargetDeadline
1,C011,Cataract Surgery,Ophthalmology,45,07/07/2020 1,11,Cataract Surgery,Ophthalmology,45,07/07/2020
2,C011,Vitrectomy ,Ophthalmology,70,17/07/2020 2,11,Vitrectomy ,Ophthalmology,70,17/07/2020
3,C011,Cataract Surgery,Ophthalmology,45,05/06/2020 3,11,Cataract Surgery,Ophthalmology,45,05/06/2020
4,C011,Cataract Surgery,Ophthalmology,45,28/06/2020 4,11,Cataract Surgery,Ophthalmology,45,28/06/2020
5,C011,Cataract Surgery,Ophthalmology,45,22/07/2020 5,11,Cataract Surgery,Ophthalmology,45,22/07/2020
6,C011,Cataract Surgery,Ophthalmology,45,03/07/2020 6,11,Cataract Surgery,Ophthalmology,45,03/07/2020
7,C011,Cataract Surgery,Ophthalmology,45,03/07/2020 7,11,Cataract Surgery,Ophthalmology,45,03/07/2020
8,C011,Eyelid Lesion Excision,Ophthalmology,30,11/06/2020 8,11,Eyelid Lesion Excision,Ophthalmology,30,11/06/2020
9,C011,Vitrectomy,Ophthalmology,70,04/07/2020 9,11,Vitrectomy,Ophthalmology,70,04/07/2020
10,C011,Cataract Surgery,Ophthalmology,45,14/07/2020 10,11,Cataract Surgery,Ophthalmology,45,14/07/2020
11,C011,Cataract Surgery,Ophthalmology,45,06/07/2020 11,11,Cataract Surgery,Ophthalmology,45,06/07/2020
12,C011,Cataract Surgery,Ophthalmology,45,16/07/2020 12,11,Cataract Surgery,Ophthalmology,45,16/07/2020
13,C011,Trabeculectomy,Ophthalmology,100,01/08/2020 13,11,Trabeculectomy,Ophthalmology,100,01/08/2020
14,C011,Vitrectomy,Ophthalmology,70,15/06/2020 14,11,Vitrectomy,Ophthalmology,70,15/06/2020
15,C011,Vitrectomy,Ophthalmology,70,20/06/2020 15,11,Vitrectomy,Ophthalmology,70,20/06/2020
16,C011,Cataract Surgery,Ophthalmology,45,18/06/2020 16,11,Cataract Surgery,Ophthalmology,45,18/06/2020
17,C011,Cataract Surgery,Ophthalmology,45,20/07/2020 17,11,Cataract Surgery,Ophthalmology,45,20/07/2020
18,C011,Trabeculectomy,Ophthalmology,100,04/07/2020 18,11,Trabeculectomy,Ophthalmology,100,04/07/2020
19,C011,Eyelid Skin Graft,Ophthalmology,120,15/06/2020 19,11,Eyelid Skin Graft,Ophthalmology,120,15/06/2020
20,C011,Cataract Surgery,Ophthalmology,45,13/07/2020 20,11,Cataract Surgery,Ophthalmology,45,13/07/2020
21,C011,Cataract Surgery,Ophthalmology,45,04/07/2020 21,11,Cataract Surgery,Ophthalmology,45,04/07/2020
22,C011,Cataract Surgery,Ophthalmology,45,28/06/2020 22,11,Cataract Surgery,Ophthalmology,45,28/06/2020
23,C011,Cataract Surgery,Ophthalmology,45,22/07/2020 23,11,Cataract Surgery,Ophthalmology,45,22/07/2020
24,C011,Cataract Surgery,Ophthalmology,45,06/07/2020 24,11,Cataract Surgery,Ophthalmology,45,06/07/2020
25,C011,Vitrectomy,Ophthalmology,70,08/06/2020 25,11,Vitrectomy,Ophthalmology,70,08/06/2020
26,C011,Cataract Surgery,Ophthalmology,45,14/07/2020 26,11,Cataract Surgery,Ophthalmology,45,14/07/2020
27,C011,Cataract Surgery,Ophthalmology,45,06/07/2020 27,11,Cataract Surgery,Ophthalmology,45,06/07/2020
28,C011,Cataract Surgery,Ophthalmology,45,16/07/2020 28,11,Cataract Surgery,Ophthalmology,45,16/07/2020
29,C011,Trabeculectomy,Ophthalmology,100,11/07/2020 29,11,Trabeculectomy,Ophthalmology,100,11/07/2020
30,C011,Vitrectomy,Ophthalmology,70,27/06/2020 30,11,Vitrectomy,Ophthalmology,70,27/06/2020

1 CaseID ConsultantID Procedure Speciality Median Duration TargetDeadline
2 1 C011 11 Cataract Surgery Ophthalmology 45 07/07/2020
3 2 C011 11 Vitrectomy  Ophthalmology 70 17/07/2020
4 3 C011 11 Cataract Surgery Ophthalmology 45 05/06/2020
5 4 C011 11 Cataract Surgery Ophthalmology 45 28/06/2020
6 5 C011 11 Cataract Surgery Ophthalmology 45 22/07/2020
7 6 C011 11 Cataract Surgery Ophthalmology 45 03/07/2020
8 7 C011 11 Cataract Surgery Ophthalmology 45 03/07/2020
9 8 C011 11 Eyelid Lesion Excision Ophthalmology 30 11/06/2020
10 9 C011 11 Vitrectomy Ophthalmology 70 04/07/2020
11 10 C011 11 Cataract Surgery Ophthalmology 45 14/07/2020
12 11 C011 11 Cataract Surgery Ophthalmology 45 06/07/2020
13 12 C011 11 Cataract Surgery Ophthalmology 45 16/07/2020
14 13 C011 11 Trabeculectomy Ophthalmology 100 01/08/2020
15 14 C011 11 Vitrectomy Ophthalmology 70 15/06/2020
16 15 C011 11 Vitrectomy Ophthalmology 70 20/06/2020
17 16 C011 11 Cataract Surgery Ophthalmology 45 18/06/2020
18 17 C011 11 Cataract Surgery Ophthalmology 45 20/07/2020
19 18 C011 11 Trabeculectomy Ophthalmology 100 04/07/2020
20 19 C011 11 Eyelid Skin Graft Ophthalmology 120 15/06/2020
21 20 C011 11 Cataract Surgery Ophthalmology 45 13/07/2020
22 21 C011 11 Cataract Surgery Ophthalmology 45 04/07/2020
23 22 C011 11 Cataract Surgery Ophthalmology 45 28/06/2020
24 23 C011 11 Cataract Surgery Ophthalmology 45 22/07/2020
25 24 C011 11 Cataract Surgery Ophthalmology 45 06/07/2020
26 25 C011 11 Vitrectomy Ophthalmology 70 08/06/2020
27 26 C011 11 Cataract Surgery Ophthalmology 45 14/07/2020
28 27 C011 11 Cataract Surgery Ophthalmology 45 06/07/2020
29 28 C011 11 Cataract Surgery Ophthalmology 45 16/07/2020
30 29 C011 11 Trabeculectomy Ophthalmology 100 11/07/2020
31 30 C011 11 Vitrectomy Ophthalmology 70 27/06/2020

View File

@ -1,4 +1,5 @@
import pandas as pd import pandas as pd
import numpy as np
import pyomo.environ as pe import pyomo.environ as pe
import pyomo.gdp as pyogdp import pyomo.gdp as pyogdp
import os import os
@ -7,6 +8,7 @@ import matplotlib.cm as cm
from itertools import product from itertools import product
# check with Taha if code is too similar to Alstom?
class TheatreScheduler: class TheatreScheduler:
def __init__(self, case_file_path, session_file_path): def __init__(self, case_file_path, session_file_path):
@ -51,33 +53,10 @@ class TheatreScheduler:
(dict): dictionary with SessionID as key and start time in minutes since midnight as value (dict): dictionary with SessionID as key and start time in minutes since midnight as value
""" """
# Convert session start time from HH:MM:SS format into seconds elapsed since midnight # Convert session start time from HH:MM:SS format into seconds elapsed since midnight
print(self.df_sessions.loc[:, "Start"])
self.df_sessions.loc[:, "Start"] = pd.to_timedelta(self.df_sessions["Start"]) self.df_sessions.loc[:, "Start"] = pd.to_timedelta(self.df_sessions["Start"])
# pd.to_timedelta(s.dt.time.astype(str)).dt.total_seconds()
print(self.df_sessions.loc[:, "Start"])
self.df_sessions.loc[:, "Start"] = self.df_sessions["Start"].dt.total_seconds() / 60 self.df_sessions.loc[:, "Start"] = self.df_sessions["Start"].dt.total_seconds() / 60
return pd.Series(self.df_sessions["Start"].values, index=self.df_sessions["SessionID"]).to_dict() return pd.Series(self.df_sessions["Start"].values, index=self.df_sessions["SessionID"]).to_dict()
def _get_ordinal_case_deadlines(self):
"""
#TODO
Returns:
"""
self.df_cases.loc[:, "TargetDeadline"] = pd.to_datetime(self.df_cases["TargetDeadline"], format="%d/%m/%Y")
self.df_cases.loc[:, "TargetDeadline"] = self.df_cases["TargetDeadline"].apply(lambda date: date.toordinal())
return pd.Series(self.df_cases["TargetDeadline"].values, index=self.df_cases["CaseID"]).to_dict()
def _get_ordinal_session_dates(self):
"""
#TODO
Returns:
"""
self.df_sessions.loc[:, "Date"] = pd.to_datetime(self.df_sessions["Date"], format="%d/%m/%Y")
self.df_sessions.loc[:, "Date"] = self.df_sessions["Date"].apply(lambda date: date.toordinal())
return pd.Series(self.df_sessions["Date"].values, index=self.df_sessions["SessionID"]).to_dict()
def _generate_disjunctions(self): def _generate_disjunctions(self):
""" """
#TODO #TODO
@ -97,57 +76,38 @@ class TheatreScheduler:
model = pe.ConcreteModel() model = pe.ConcreteModel()
# Model Data # Model Data
print('test4')
# List of case IDs in surgical waiting list
model.CASES = pe.Set(initialize=self.df_cases["CaseID"].tolist()) model.CASES = pe.Set(initialize=self.df_cases["CaseID"].tolist())
print('test5')
print(model.CASES)
# List of sessions IDs
model.SESSIONS = pe.Set(initialize=self.df_sessions["SessionID"].tolist()) model.SESSIONS = pe.Set(initialize=self.df_sessions["SessionID"].tolist())
print('test6')
print(model)
# List of sessions IDs
# List of tasks - all possible (caseID, sessionID) combination
model.TASKS = pe.Set(initialize=model.CASES * model.SESSIONS, dimen=2) model.TASKS = pe.Set(initialize=model.CASES * model.SESSIONS, dimen=2)
print(model.TASKS)
print('test7')
# The duration (median case time) for each operation
model.CASE_DURATION = pe.Param(model.CASES, initialize=self._generate_case_durations()) model.CASE_DURATION = pe.Param(model.CASES, initialize=self._generate_case_durations())
print('test8')
# The duration of each theatre session
model.SESSION_DURATION = pe.Param(model.SESSIONS, initialize=self._generate_session_durations()) model.SESSION_DURATION = pe.Param(model.SESSIONS, initialize=self._generate_session_durations())
print('test9')
# The start time of each theatre session
model.SESSION_START_TIME = pe.Param(model.SESSIONS, initialize=self._generate_session_start_times()) model.SESSION_START_TIME = pe.Param(model.SESSIONS, initialize=self._generate_session_start_times())
print('te0')
# The deadline of each case
model.CASE_DEADLINES = pe.Param(model.CASES, initialize=self._get_ordinal_case_deadlines())
print('te1')
# The date of each theatre session
model.SESSION_DATES = pe.Param(model.SESSIONS, initialize=self._get_ordinal_session_dates())
model.DISJUNCTIONS = pe.Set(initialize=self._generate_disjunctions(), dimen=3) model.DISJUNCTIONS = pe.Set(initialize=self._generate_disjunctions(), dimen=3)
ub = 1440 # seconds in a day ub = 1440 # seconds in a day
model.M = pe.Param(initialize=1e3*ub) # big M model.M = pe.Param(initialize=1e3*ub) # big M
max_util = 0.85 max_util = 0.85
num_cases = self.df_cases.shape[0] num_sessions = self.df_sessions.shape[0]
# Decision Variables # Decision Variables
model.SESSION_ASSIGNED = pe.Var(model.TASKS, domain=pe.Binary) model.SESSION_ASSIGNED = pe.Var(model.TASKS, domain=pe.Binary)
model.CASE_START_TIME = pe.Var(model.TASKS, bounds=(0, ub), within=pe.PositiveReals) model.CASE_START_TIME = pe.Var(model.TASKS, bounds=(0, ub), within=pe.PositiveReals)
model.CASES_IN_SESSION = pe.Var(model.SESSIONS, bounds=(0, num_cases), within=pe.PositiveReals) model.UTILISATION = pe.Var(model.SESSIONS, bounds=(0, 1), within=pe.PositiveReals)
model.MEDIAN_UTIL = pe.Var(bounds=(0, ub), within=pe.PositiveReals)
model.DUMMY_BINARY = pe.Var(model.SESSIONS, domain=pe.Binary)
model.CANCEL_SESSION = pe.Var(model.SESSIONS, domain=pe.Binary, within=pe.PositiveReals)
# Objective # Objective
def objective_function(model): def objective_function(model):
return pe.summation(model.CASES_IN_SESSION) #return pe.summation(model.UTILISATION)
#return sum([model.SESSION_ASSIGNED[case, session] for case in model.CASES for session in model.SESSIONS]) return model.MEDIAN_UTIL
model.OBJECTIVE = pe.Objective(rule=objective_function, sense=pe.maximize) model.OBJECTIVE = pe.Objective(rule=objective_function, sense=pe.maximize)
# Constraints # Constraints
# TODO add constraint to complete before deadline if it is assigned
# TODO add constraint to make tasks follow each other without gaps?
# Case start time must be after start time of assigned theatre session # Case start time must be after start time of assigned theatre session
def case_start_time(model, case, session): def case_start_time(model, case, session):
return model.CASE_START_TIME[case, session] >= model.SESSION_START_TIME[session] - \ return model.CASE_START_TIME[case, session] >= model.SESSION_START_TIME[session] - \
@ -165,10 +125,6 @@ class TheatreScheduler:
return sum([model.SESSION_ASSIGNED[(case, session)] for session in model.SESSIONS]) <= 1 return sum([model.SESSION_ASSIGNED[(case, session)] for session in model.SESSIONS]) <= 1
model.SESSION_ASSIGNMENT = pe.Constraint(model.CASES, rule=session_assignment) model.SESSION_ASSIGNMENT = pe.Constraint(model.CASES, rule=session_assignment)
def set_deadline_condition(model, case, session):
return model.SESSION_DATES[session] <= model.CASE_DEADLINES[case] + ((1 - model.SESSION_ASSIGNED[case, session])*model.M)
model.APPLY_DEADLINE = pe.Constraint(model.TASKS, rule=set_deadline_condition)
def no_case_overlap(model, case1, case2, session): def no_case_overlap(model, case1, case2, session):
return [model.CASE_START_TIME[case1, session] + model.CASE_DURATION[case1] <= model.CASE_START_TIME[case2, session] + \ return [model.CASE_START_TIME[case1, session] + model.CASE_DURATION[case1] <= model.CASE_START_TIME[case2, session] + \
((2 - model.SESSION_ASSIGNED[case1, session] - model.SESSION_ASSIGNED[case2, session])*model.M), ((2 - model.SESSION_ASSIGNED[case1, session] - model.SESSION_ASSIGNED[case2, session])*model.M),
@ -178,16 +134,31 @@ class TheatreScheduler:
model.DISJUNCTIONS_RULE = pyogdp.Disjunction(model.DISJUNCTIONS, rule=no_case_overlap) model.DISJUNCTIONS_RULE = pyogdp.Disjunction(model.DISJUNCTIONS, rule=no_case_overlap)
def theatre_util(model, session): def theatre_util(model, session):
return model.CASES_IN_SESSION[session] == \ return model.UTILISATION[session] == (1 / model.SESSION_DURATION[session]) * \
sum([model.SESSION_ASSIGNED[case, session] for case in model.CASES]) sum([model.SESSION_ASSIGNED[case, session]*model.CASE_DURATION[case] for case in model.CASES])
model.THEATRE_UTIL = pe.Constraint(model.SESSIONS, rule=theatre_util) model.THEATRE_UTIL = pe.Constraint(model.SESSIONS, rule=theatre_util)
def cancel_sessions(model, session): # TODO
return model.CANCEL_SESSION[session] <= 1 - model.M*sum([model.SESSION_ASSIGNED[case, session] for case in model.CASES])
model.SET_CANCEL_SESSIONS = pe.Constraint(model.SESSIONS, rule=cancel_sessions)
def force_cancel_sessions(model, session):
return sum([model.SESSION_ASSIGNED[case, session] for case in model.CASES]) <= 0 + model.M*(1-model.CANCEL_SESSION[session])
#model.FORCE_CANCEL_SESSIONS = pe.Constraint(model.SESSIONS, rule=force_cancel_sessions)
def set_dummy_variable(model):
return sum([model.DUMMY_BINARY[session] for session in model.SESSIONS]) == np.floor(num_sessions/2)
model.FLOOR = pe.Constraint(rule=set_dummy_variable)
def set_median_util(model, session):
return model.MEDIAN_UTIL <= model.UTILISATION[session] + model.DUMMY_BINARY[session]*model.M
model.SET_MEDIAN_UTIL = pe.Constraint(model.SESSIONS, rule=set_median_util)
pe.TransformationFactory("gdp.bigm").apply_to(model) pe.TransformationFactory("gdp.bigm").apply_to(model)
return model return model
def solve(self, solver_name, options=None, solver_path=None, local=True): def solve(self, solver_name, options=None, solver_path=None):
if solver_path is not None: if solver_path is not None:
solver = pe.SolverFactory(solver_name, executable=solver_path) solver = pe.SolverFactory(solver_name, executable=solver_path)
@ -199,17 +170,10 @@ class TheatreScheduler:
for key, value in options.items(): for key, value in options.items():
solver.options[key] = value solver.options[key] = value
if local:
solver_results = solver.solve(self.model, tee=True) solver_results = solver.solve(self.model, tee=True)
else:
solver_manager = pe.SolverManagerFactory("neos")
solver_results = solver_manager.solve(self.model, opt=solver)
results = [{"Case": case, results = [{"Case": case,
"Session": session, "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](), "Start": self.model.CASE_START_TIME[case, session](),
"Assignment": self.model.SESSION_ASSIGNED[case, session]()} "Assignment": self.model.SESSION_ASSIGNED[case, session]()}
for (case, session) in self.model.TASKS] for (case, session) in self.model.TASKS]
@ -218,6 +182,7 @@ class TheatreScheduler:
all_cases = self.model.CASES.value_list all_cases = self.model.CASES.value_list
cases_assigned = [] cases_assigned = []
cases_missed = []
for (case, session) in self.model.SESSION_ASSIGNED: for (case, session) in self.model.SESSION_ASSIGNED:
if self.model.SESSION_ASSIGNED[case, session] == 1: if self.model.SESSION_ASSIGNED[case, session] == 1:
cases_assigned.append(case) cases_assigned.append(case)
@ -227,11 +192,11 @@ class TheatreScheduler:
print("Cases assigned: ", cases_assigned) print("Cases assigned: ", cases_assigned)
print("Number of cases missed = {} out of {}:".format(len(cases_missed), len(all_cases))) print("Number of cases missed = {} out of {}:".format(len(cases_missed), len(all_cases)))
print("Cases missed: ", cases_missed) print("Cases missed: ", cases_missed)
self.model.CASES_IN_SESSION.pprint() self.model.UTILISATION.pprint()
print("Total Objective = {}".format(sum(self.model.CASES_IN_SESSION.get_values().values()))) print("Total Utilisation = {}".format(sum(self.model.UTILISATION.get_values().values())))
print("Number of constraints = {}".format(solver_results["Problem"].__getitem__(0)["Number of constraints"])) print("Number of constraints = {}".format(solver_results["Problem"].__getitem__(0)["Number of constraints"]))
#self.model.SESSION_ASSIGNED.pprint() #self.model.SESSION_ASSIGNED.pprint()
print(self.df_times[self.df_times["Assignment"] == 1].to_string()) #print(self.df_times.to_string())
self.draw_gantt() self.draw_gantt()
def draw_gantt(self): def draw_gantt(self):
@ -257,7 +222,7 @@ class TheatreScheduler:
ax.plot([xs, xf], [s] * 2, c=colors[c_ix % 7], **bar_style) ax.plot([xs, xf], [s] * 2, c=colors[c_ix % 7], **bar_style)
ax.text((xs + xf) / 2, s, c, **text_style) ax.text((xs + xf) / 2, s, c, **text_style)
ax.set_title('Assigning Ophthalmology Cases to Theatre Sessions') ax.set_title('Session Schedule')
ax.set_xlabel('Time') ax.set_xlabel('Time')
ax.set_ylabel('Sessions') ax.set_ylabel('Sessions')
ax.grid(True) ax.grid(True)
@ -267,13 +232,10 @@ class TheatreScheduler:
if __name__ == "__main__": if __name__ == "__main__":
print('tet') case_path = os.path.join(os.path.dirname(os.getcwd()), "data", "case_data_long.csv")
case_path = os.path.join(os.path.dirname(os.getcwd()), "data", "cases.csv") session_path = os.path.join(os.path.dirname(os.getcwd()), "data", "session_data.csv")
case_path = "/home/hmag/code/theatre-scheduling/data/cases.csv" cbc_path = "C:\\Users\\LONLW15\\Documents\\Linear Programming\\Solvers\\cbc.exe"
session_path = "/home/hmag/code/theatre-scheduling/data/sessions.csv"
# cbc_path = "C:\\Users\\LONLW15\\Documents\\Linear Programming\\Solvers\\cbc.exe"
options = {"seconds": 300} options = {"seconds": 30}
scheduler = TheatreScheduler(case_file_path=case_path, session_file_path=session_path) scheduler = TheatreScheduler(case_file_path=case_path, session_file_path=session_path)
scheduler.solve(solver_name="cbc", solver_path=cbc_path, options=options) scheduler.solve(solver_name="cbc", solver_path=cbc_path, options=options)
#scheduler.solve(solver_name="cbc", local=False, options=None)

View File

@ -1,54 +1,143 @@
name: lp_pyomo name: collider_env_v1
channels: channels:
- plotly
- anaconda
- conda-forge - conda-forge
- defaults - defaults
dependencies: dependencies:
- aniso8601=8.0.0=py_0
- appdirs=1.4.3=py_1 - appdirs=1.4.3=py_1
- attrs=19.3.0=py_0
- backcall=0.1.0=py37_0
- blas=1.0=mkl - blas=1.0=mkl
- ca-certificates=2020.4.5.1=hecc5488_0 - bleach=3.1.0=py37_0
- certifi=2020.4.5.1=py38h32f6830_0 - ca-certificates=2020.1.1=0
- cycler=0.10.0=py_2 - click=7.0=py37_0
- freetype=2.10.2=hd328e21_0 - colorama=0.4.3=py_0
- coverage=5.0.3=py37h8055547_1
- cycler=0.10.0=py37_0
- decorator=4.4.1=py_0
- defusedxml=0.6.0=py_0
- entrypoints=0.3=py37_0
- flask=1.1.1=py_0
- flask-restplus=0.13.0=py37_0
- freetype=2.9.1=ha9979f8_1
- glpk=4.65=h2fa13f4_1002 - glpk=4.65=h2fa13f4_1002
- icc_rt=2019.0.0=h0cc432a_1 - icc_rt=2019.0.0=h0cc432a_1
- icu=64.2=he025d50_1 - icu=58.2=vc14hc45fdbb_0
- intel-openmp=2020.1=216 - importlib_metadata=1.5.0=py37_0
- jpeg=9c=hfa6e2cd_1001 - intel-openmp=2020.0=166
- kiwisolver=1.2.0=py38heaebd3c_0 - ipopt=3.11.1=2
- libclang=9.0.1=default_hf44288c_0 - ipykernel=5.1.4=py37h39e3cac_0
- libpng=1.6.37=hfe6a214_1 - ipython=7.12.0=py37h5ca1d4c_0
- matplotlib=3.2.1=0 - ipython_genutils=0.2.0=py37_0
- matplotlib-base=3.2.1=py38h1626042_0 - ipywidgets=7.5.1=py_0
- mkl=2020.1=216 - itsdangerous=1.1.0=py37_0
- mkl-service=2.3.0=py38hb782905_0 - jedi=0.16.0=py37_0
- mkl_fft=1.0.15=py38h14836fe_0 - jinja2=2.11.1=py_0
- mkl_random=1.1.0=py38hf9181ef_0 - joblib=0.14.1=py_0
- nose=1.3.7=py38h32f6830_1004 - jpeg=9b=vc14h4d7706e_1
- numpy=1.18.1=py38h93ca92e_0 - json5=0.9.1=py_0
- numpy-base=1.18.1=py38hc3f5095_1 - jsonschema=3.2.0=py37_0
- jupyter=1.0.0=py37_7
- jupyter_client=5.3.4=py37_0
- jupyter_console=6.1.0=py_0
- jupyter_core=4.6.1=py37_0
- jupyterlab=1.2.6=pyhf63ae98_0
- jupyterlab_server=1.0.6=py_0
- kiwisolver=1.1.0=py37ha925a31_0
- libpng=1.6.37=h2a8f88b_0
- libsodium=1.0.16=h9d3ae62_0
- m2w64-gcc-libgfortran=5.3.0=6
- m2w64-gcc-libs=5.3.0=7
- m2w64-gcc-libs-core=5.3.0=7
- m2w64-gmp=6.1.0=2
- m2w64-libwinpthread-git=5.0.0.4634.697f757=2
- markupsafe=1.1.1=py37he774522_0
- matplotlib=3.1.3=py37_0
- matplotlib-base=3.1.3=py37h64f37c6_0
- mistune=0.8.4=py37he774522_0
- mkl=2019.5=281
- mkl-service=2.3.0=py37hb782905_0
- mkl_fft=1.0.15=py37h14836fe_0
- mkl_random=1.1.0=py37h675688f_0
- msys2-conda-epoch=20160418=1
- nb_conda=2.2.1=py37_0
- nb_conda_kernels=2.2.2=py37_0
- nbconvert=5.6.1=py37_0
- nbformat=5.0.4=py_0
- networkx=2.4=py_0
- nose=1.3.7=py37hc8dfbb8_1004
- nose2=0.9.2=py_0
- notebook=6.0.3=py37_0
- numpy=1.18.1=py37h93ca92e_0
- numpy-base=1.18.1=py37hc3f5095_1
- openssl=1.1.1g=he774522_0 - openssl=1.1.1g=he774522_0
- pandas=1.0.3=py38h47e9c7a_0 - pandas=1.0.1=py37h47e9c7a_0
- pip=20.0.2=py38_3 - pandoc=2.2.3.2=0
- pandocfilters=1.4.2=py37_1
- parso=0.6.1=py_0
- patsy=0.5.1=py37_0
- pickleshare=0.7.5=py37_0
- pip=20.0.2=py37_1
- plotly=4.5.4=py_0
- plotly_express=0.4.1=py_0
- ply=3.11=py_1 - ply=3.11=py_1
- pyomo=5.6.9=py38h32f6830_2 - prometheus_client=0.7.1=py_0
- pyparsing=2.4.7=pyh9f0ad1d_0 - prompt_toolkit=3.0.3=py_0
- pyqt=5.12.3=py38h7ae7562_3 - pygments=2.5.2=py_0
- python=3.8.3=he1778fa_0 - pyodbc=4.0.30=py37ha925a31_0
- pyomo=5.6.8=py37_0
- pyparsing=2.4.6=py_0
- pyqt=5.9.2=py37ha878b3d_0
- pyrsistent=0.15.7=py37he774522_0
- python=3.7.6=h60c2a47_2
- python-dateutil=2.8.1=py_0 - python-dateutil=2.8.1=py_0
- python_abi=3.8=1_cp38 - python_abi=3.7=1_cp37m
- pytz=2020.1=py_0 - pytz=2019.3=py_0
- pyutilib=5.8.0=pyh9f0ad1d_1 - pyutilib=5.7.3=py37_4
- qt=5.12.5=h7ef1ec2_0 - pywin32=227=py37he774522_1
- setuptools=46.4.0=py38_0 - pywinpty=0.5.7=py37_0
- six=1.14.0=py38_0 - pyzmq=18.1.1=py37ha925a31_0
- sqlite=3.31.1=h2a8f88b_1 - qt=5.9.7=vc14h73c81de_0
- tornado=6.0.4=py38hfa6e2cd_0 - qtconsole=4.7.2=py_0
- qtpy=1.9.0=py_0
- retrying=1.3.3=py37_2
- scikit-learn=0.22.1=py37h6288b17_0
- scipy=1.4.1=py37h9439919_0
- seaborn=0.10.0=py_0
- send2trash=1.5.0=py37_0
- setuptools=46.0.0=py37_0
- sip=4.19.13=py37ha925a31_0
- sqlite=3.31.1=he774522_0
- statsmodels=0.11.0=py37he774522_0
- terminado=0.8.3=py37_0
- testpath=0.4.4=py_0
- tornado=6.0.3=py37he774522_3
- traitlets=4.3.3=py37_0
- vc=14.1=h0510ff6_4 - vc=14.1=h0510ff6_4
- vs2015_runtime=14.16.27012=hf0eaf9b_2 - vs2015_runtime=14.16.27012=hf0eaf9b_1
- wheel=0.34.2=py38_0 - waitress=1.4.3=py_0
- wincertstore=0.2=py38_0 - wcwidth=0.1.8=py_0
- zlib=1.2.11=h62dcd97_4 - webencodings=0.5.1=py37_1
- werkzeug=0.16.1=py_0
- wheel=0.34.2=py37_0
- widgetsnbextension=3.5.1=py37_0
- wincertstore=0.2=py37_0
- winpty=0.4.3=4
- xlrd=1.2.0=py37_0
- zeromq=4.3.1=h33f27b4_3
- zipp=2.2.0=py_0
- zlib=1.2.11=vc14h1cdd9ab_1
- pip: - pip:
- pyqt5-sip==4.19.18 - cbcpy==2.10.3a2
- pyqtchart==5.12 - certifi==2017.7.27.1
- pyqtwebengine==5.12.1 - cplex==12.9.0.0
- docloud==1.0.257
- docplex==2.9.133
- func-timeout==4.3.5
- requests==2.18.4
- six==1.10.0
- urllib3==1.22
prefix: C:\Users\200234360\AppData\Local\Continuum\anaconda3\envs\collider_env_v1

View File

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