Skip to content

Refactoring code using OOP pattern and numpy #2

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions 20241012CVRP_Gurobi_Python/CVRP_OOP/data.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import random
import math
import numpy as np


class Data:
def __init__(self, K: int, N: int, Q: int, M: int = 99999):
self.t_nn1 = None
self.y_n = None
self.x_n = None
self.q_n = None
self.__is_initialized = False

self.K = K
self.N = N
self.Q = Q
self.M = M

self.initialize()

def initialize(self):

if not self.__is_initialized:
"""use numpy to accelerate and simplify the data generation process"""

self.q_n = np.random.randint(10, 15, self.N) # the demand of customers [low, high, size]
self.x_n = np.random.randint(0, 50, self.N + 2)
self.y_n = np.random.randint(0, 50, self.N + 2)
# the travel time from n to n1
self.t_nn1 = (np.abs(np.expand_dims(self.x_n, axis=1) - np.expand_dims(self.x_n, axis=0)) +
np.abs(np.expand_dims(self.y_n, axis=1) - np.expand_dims(self.y_n, axis=0)))

self.__is_initialized = True
print("Data initialized!")
else:
print("Data already initialized!")

34 changes: 34 additions & 0 deletions 20241012CVRP_Gurobi_Python/CVRP_OOP/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import numpy as np
from data import Data
from model import CVRP

"""
10.20 modification:

1. use numpy to generate data rather than "for" loop for fast speed and simplification.
2. Implement a Data class wrap generated data to avoid conflicts with global variables.
3. Implement a CVRP class that accepts data as input and provides a solve() method to execute the solution process.
"""


def main(k, n, q, s):

np.random.seed(s)
data = Data(K=k, N=n, Q=q)
model = CVRP(data)

model.solve()


if __name__ == '__main__':

"""define hyperparameter"""
K = 5 # the number of the vehicles
N = 4 # the number of customers
Q = 50 # the capacity of the vehicles
seed = 1

main(K, N, Q, seed)



141 changes: 141 additions & 0 deletions 20241012CVRP_Gurobi_Python/CVRP_OOP/model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import gurobipy as grb
import data
import time


class CVRP:
"""
Use gurobi to solve CVRP
call solve() method to use this model
"""

def __init__(self, data, time_limit=3600, method=1, output_flag=1, isprint=True):
self.model = grb.Model("CVRP")
self.model.setParam("TimeLimit", time_limit)
self.model.setParam("Method", method) # method1: Simplex
self.model.setParam("outputFlag", output_flag)

# whether to print answer
self.isprint = isprint

self.data = data

def add_variables(self):

# add variables
self.gamma_nk = {}
self.alpha_nk = {}
self.beta_nn1k = {}

# gamma_nk
for n in range(self.data.N + 2):
for k in range(self.data.K):
name1 = "gamma_nk[{0}][{1}]".format(n, k)
self.gamma_nk[n, k] = self.model.addVar(0, grb.GRB.INFINITY, vtype=grb.GRB.CONTINUOUS, name=name1)
# alpha_nk
for n in range(self.data.N + 2):
for k in range(self.data.K):
name1 = "alpha_nk[{0}][{1}]".format(n, k)
self.alpha_nk[n, k] = self.model.addVar(0, 1, vtype=grb.GRB.BINARY, name=name1)
# beta_nn1k
for n in range(self.data.N + 2):
for n1 in range(self.data.N + 2):
for k in range(self.data.K):
name1 = "beta_nn1k[{0}][{1}][{2}]".format(n, n1, k)
self.beta_nn1k[n, n1, k] = self.model.addVar(0, 1, vtype=grb.GRB.BINARY, name=name1)

def add_constraints(self):
# add constraints
for n in range(1, self.data.N + 1):
expr1 = grb.LinExpr(0)
for k in range(self.data.K):
expr1.addTerms(1, self.alpha_nk[n, k])
self.model.addConstr(expr1 == 1, name="Constr1")

for n1 in range(1, self.data.N + 1):
for k in range(self.data.K):
expr2_1 = grb.LinExpr(0)
expr2_2 = grb.LinExpr(0)
for n in range(1, self.data.N + 2):
expr2_1.addTerms(1, self.beta_nn1k[n1, n, k])
for n in range(0, self.data.N + 1):
expr2_2.addTerms(1, self.beta_nn1k[n, n1, k])
self.model.addConstr(expr2_1 == expr2_2, name="Constr2_1")
self.model.addConstr(expr2_1 == self.alpha_nk[n1, k], name="Constr2_2")

for k in range(self.data.K):
expr3_1 = grb.LinExpr(0)
expr3_2 = grb.LinExpr(0)
for n in range(0, self.data.N + 1):
expr3_1.addTerms(1, self.beta_nn1k[n, self.data.N + 1, k])
for n in range(1, self.data.N + 2):
expr3_2.addTerms(1, self.beta_nn1k[0, n, k])
self.model.addConstr(expr3_1 == expr3_1, name="Constr3_1")
self.model.addConstr(expr3_1 == 1, name="Constr3_2")

for n in range(1, self.data.N + 1):
for n1 in range(0, self.data.N):
for k in range(self.data.K):
self.model.addConstr(self.gamma_nk[n, k] >= self.gamma_nk[n1, k] + self.data.t_nn1[n][n1] -
self.data.M * (1 - self.beta_nn1k[n1, n, k]), name="Constr4")

def add_terms(self):

for k in range(self.data.K):
expr5 = grb.LinExpr(0)
for n in range(1, self.data.N):
expr5.addTerms(self.data.q_n[n], self.alpha_nk[n, k])
# OBJ
obj = grb.LinExpr(0)
for k in range(self.data.K):
for n in range(0, self.data.N):
for n1 in range(1, self.data.N + 1):
obj.addTerms(self.data.t_nn1[n][n1], self.beta_nn1k[n, n1, k])
self.model.setObjective(obj, grb.GRB.MAXIMIZE)

def solve(self):

self.add_variables()
self.add_constraints()
self.add_terms()

self.model.optimize()

if self.isprint:
self.print_info()

def print_info(self):

if self.model.status == grb.GRB.Status.INFEASIBLE:
print("CVRP IS INFEASIBLE")
self.model.computeIIS()
self.model.write("CVRP + {0}.ilp".format(time.time()))
return self.model
elif self.model.status == grb.GRB.TIME_LIMIT:
print("RUN OUT OF TIME LIMIT")
return self.model
elif self.model.status == grb.GRB.OPTIMAL:
print("solveByGurobi_obj = {0}".format(self.model.ObjVal))

def printVars():
"""
print the optimal solution
:return:
"""
for n in range(1, self.data.N + 1):
for k in range(self.data.K):
if self.gamma_nk[n, k].x > 0:
print("gamma_nk[{0}][{1}] = {2}".format(n, k, self.gamma_nk[n, k].x))

for n in range(1, self.data.N + 1):
for k in range(self.data.K):
if self.alpha_nk[n, k].x > 0.9:
print("alpha_nk[{0}][{1}] = {2}".format(n, k, self.alpha_nk[n, k].x))

for n in range(self.data.N + 2):
for n1 in range(self.data.N + 2):
for k in range(self.data.K):
if self.beta_nn1k[n, n1, k].x > 0.9:
print("beta_nn1k[{0}][{1}][{2}] = {3}".format(n, n1, k, self.beta_nn1k[n, n1, k].x))
printVars()