Compare commits
No commits in common. "1d83544eabe4ace9e45e4890dcccae2a82bb1dd3" and "edc47f65840df70c9b748da4dd9336629065a56f" have entirely different histories.
1d83544eab
...
edc47f6584
29
LICENSE
29
LICENSE
|
@ -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.
|
|
|
@ -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,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,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)
|
|
171
pyomo_env.yml
171
pyomo_env.yml
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
pandas
|
|
||||||
pyomo
|
|
||||||
matplotlib
|
|
Loading…
Reference in New Issue