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()