141 lines
4.1 KiB
Python
141 lines
4.1 KiB
Python
|
import pandas as pd #handling dataframes
|
||
|
import numpy as np #numpy for array management
|
||
|
import matplotlib.pyplot as plt #plotting
|
||
|
import plotly.express as px #generate beautiful interactive plots
|
||
|
from datetime import timedelta, date #for later datetimes processing
|
||
|
|
||
|
from itertools import product
|
||
|
import datetime
|
||
|
import openpyxl
|
||
|
|
||
|
#pyomo.environ provides the framework for build the model
|
||
|
import pyomo.environ as pyo
|
||
|
|
||
|
#SolverFactory allows to call the solver to solve
|
||
|
from pyomo.opt import SolverFactory
|
||
|
|
||
|
#store the solver in a variable to call it later, we need to tell google colab
|
||
|
#the specific path on which the solver was installed
|
||
|
opt_glpk = pyo.SolverFactory('glpk', executable='/usr/bin/glpsol')
|
||
|
|
||
|
activities = {0:'Start',
|
||
|
1: 'A',
|
||
|
2: 'B',
|
||
|
3: 'C',
|
||
|
4: 'D',
|
||
|
5: 'E',
|
||
|
6: 'F',
|
||
|
7: 'G',
|
||
|
8: 'H',
|
||
|
9: 'I',
|
||
|
10: 'J',
|
||
|
11:'End'}
|
||
|
p = [0, 5, 2, 5, 6, 5, 2, 3, 2, 4, 3, 0] # activity durations
|
||
|
|
||
|
u = [[0, 0, 0], # list of resource consumptions
|
||
|
[1, 0, 0],
|
||
|
[1, 0, 0],
|
||
|
[0, 1, 0],
|
||
|
[0, 0, 1],
|
||
|
[0, 1, 0],
|
||
|
[0, 1, 0],
|
||
|
[0, 0, 1],
|
||
|
[0, 0, 1],
|
||
|
[0, 0, 1],
|
||
|
[0, 0, 1],
|
||
|
[0, 0, 0]]
|
||
|
|
||
|
E = [[0, 1], # list of precedence constraints
|
||
|
[0, 2],
|
||
|
[1, 3],
|
||
|
[2, 4],
|
||
|
[3, 5],
|
||
|
[3, 6],
|
||
|
[3, 7],
|
||
|
[5, 8],
|
||
|
[6, 9],
|
||
|
[8, 10],
|
||
|
[9, 10],
|
||
|
[4, 11],
|
||
|
[7, 11],
|
||
|
[10, 11]]
|
||
|
|
||
|
c = [1,1,1] # max resource capacity
|
||
|
|
||
|
|
||
|
n = len(p) - 2
|
||
|
ph = sum(p)
|
||
|
(R, J, T) = (range(len(c)), range(len(p)), range(ph))
|
||
|
|
||
|
model = pyo.ConcreteModel() # create empty model (like empty canvas)
|
||
|
|
||
|
model.J = pyo.RangeSet(0,len(p)-1) # set for the activities
|
||
|
model.T = pyo.RangeSet(0,ph) # set for the days in the planning horizon
|
||
|
model.Xs = pyo.Var(model.J,model.T,within = pyo.Binary) # variables
|
||
|
Xs = model.Xs
|
||
|
|
||
|
# Objective Function Eq. (1)
|
||
|
# Next we proceed to create our objective function. Following the exact formula described by equation (1).
|
||
|
|
||
|
# sum of the dates for the final activity (the dummy end)
|
||
|
Z = sum([t*Xs[(n+1,t)] for t in model.T])
|
||
|
# we want to minimize this objective function
|
||
|
model.obj = pyo.Objective(expr = Z,sense=pyo.minimize)
|
||
|
|
||
|
# Constraint (2): Only one start time
|
||
|
|
||
|
model.one_xs = pyo.ConstraintList() #create a list of constraints
|
||
|
for j in model.J:
|
||
|
#add constraints to the list created above
|
||
|
model.one_xs.add(expr = sum(Xs[(j,t)] for t in model.T)==1)
|
||
|
|
||
|
# Constraint (3): Precedence constraints
|
||
|
|
||
|
model.pc = pyo.ConstraintList() # precedence constraints
|
||
|
for (j,s) in E:
|
||
|
model.pc.add(expr = sum(t*Xs[(s,t)] - t*Xs[(j,t)] for t in model.T) >= p[j])
|
||
|
|
||
|
# Constraint (4): Resource capacity constraints
|
||
|
|
||
|
model.rl = pyo.ConstraintList() #resource level constraints
|
||
|
print(R)
|
||
|
print(T)
|
||
|
for (r, t) in product(R, T):
|
||
|
model.rl.add(expr = sum(u[j][r]*Xs[(j,t2)] for j in model.J for t2 in range(max(0, t - p[j] + 1), t + 1)) <= c[r])
|
||
|
|
||
|
# solve
|
||
|
|
||
|
opt_glpk.options['tmlim'] = 60
|
||
|
opt_glpk.options["mipgap"] = 0
|
||
|
results = opt_glpk.solve(model,tee=True) # ask the solver to solve the model
|
||
|
results.write()
|
||
|
|
||
|
SCHEDULE = {}
|
||
|
for (j, t) in product(J, T):
|
||
|
|
||
|
if pyo.value(Xs[(j, t)]) >= 0.99: #only get the x values == 1
|
||
|
a = activities[j]
|
||
|
SCHEDULE[a] = {'s':t,'f':t+p[j]}
|
||
|
print(f'Activity {j}, begins at t={t} and finishes at {t+p[j]}')
|
||
|
|
||
|
out = pd.DataFrame(SCHEDULE).T
|
||
|
SCHEDULE = out.T.to_dict()
|
||
|
|
||
|
#start_date = datetime.date(2023, 1, 11)
|
||
|
start_date = datetime.date.today()
|
||
|
|
||
|
SCHEDULE_DF = pd.DataFrame(SCHEDULE).T
|
||
|
SCHEDULE_DF['Start_Date'] = [str(start_date + datetime.timedelta(days=s)) for s in SCHEDULE_DF['s']]
|
||
|
SCHEDULE_DF['Finish_Date'] =[str(start_date + datetime.timedelta(days=f)) for f in SCHEDULE_DF['f']]
|
||
|
|
||
|
SCHEDULE_DF.to_csv('output_schedule.csv') #export csv file
|
||
|
SCHEDULE_DF.to_excel('output_schedule.xlsx') #export excel file
|
||
|
|
||
|
fig = px.timeline(SCHEDULE_DF,
|
||
|
x_start="Start_Date",
|
||
|
x_end="Finish_Date",
|
||
|
y=SCHEDULE_DF.index,title='Project Gantt Chart')
|
||
|
fig.update_yaxes(autorange="reversed") # otherwise tasks are listed from the bottom up
|
||
|
fig.write_html("Gantt_chart.html")
|
||
|
fig.show()
|