diff --git a/ekg.py b/ekg.py new file mode 100644 index 0000000..2096a7e --- /dev/null +++ b/ekg.py @@ -0,0 +1,140 @@ +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() diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..dc6e7ff --- /dev/null +++ b/requirements.txt @@ -0,0 +1,5 @@ +pandas +pyomo +matplotlib +plotly +openpyxl