├── NNH.py ├── README ├── main.py ├── main_2.py ├── maingams.py ├── plotsolution.py └── readvrplib.py /NNH.py: -------------------------------------------------------------------------------- 1 | from numpy import * 2 | from collections import defaultdict 3 | 4 | def generate_random_solution(numnodes, distance, capacity, demand,numofvehicles): 5 | customers = list(range(2,numnodes+1)) 6 | #random.shuffle(customers) 7 | routes = defaultdict(list) #[[] for vehicle in range(numofvehicles)] 8 | remaining_capacity = ones(numofvehicles, dtype=int) * capacity 9 | 10 | for vehicle in range(numofvehicles): 11 | 12 | # Try to feasibly add customers to the vehicle 13 | for id in customers: 14 | q = demand[id] 15 | # If there is remaining capacity, or it is the last vehicle 16 | if q <= remaining_capacity[vehicle]: #or vehicle == (numofvehicles - 1): 17 | routes[vehicle].append(id) 18 | remaining_capacity[vehicle] -= q 19 | 20 | # Remove from the list the customers actually added 21 | for id in routes[vehicle]: 22 | customers.remove(id) 23 | 24 | # Add the depot to the start and end of of the route 25 | routes[vehicle].insert(0, 1) 26 | routes[vehicle].append(1) 27 | 28 | print 'remaining_capacity',remaining_capacity 29 | return (routes) 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | This project focuses on the set covering based formulation for the capacitated vehicle routing 2 | problem (CVRP). A column generation approach based on dynamic programming has been used to find 3 | the lower bound for the optimal solution. 4 | 5 | The following libraries need to be installed for the code to run 6 | NumPy 7 | Matplotlib 8 | Networkx 9 | Gurobi(Commmercial MIP solver) 10 | 11 | 12 | 13 | 14 | For detailed analysis, please refer to 15 | The Vehicle Routing Problem 16 | 17 | By 18 | Toth & Vigo -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | from readvrplib import read_vrplib_file 2 | from NNH import generate_random_solution 3 | from plotsolution import plotsolution 4 | from gurobipy import * 5 | from collections import defaultdict 6 | from itertools import chain, combinations 7 | import time 8 | file_name = 'C:/Users/Aditya/Desktop/VRP Instances/P-n21-k2.vrp' 9 | start = time.clock() 10 | 11 | def get_routecst_ksitranspose(route,numnodes): 12 | 13 | ksit = [0] * (numnodes+1) 14 | routecst = 0 15 | routelength = len(route) 16 | lastcustomer = 1 17 | 18 | for i,cust in enumerate(route[1:]): 19 | ksit[cust] +=1 20 | routecst += distance[cust][lastcustomer] 21 | lastcustomer = cust 22 | 23 | return routecst,ksit 24 | 25 | def printSolution(m,NumberofVariable): 26 | global optimalroutes 27 | if m.status == GRB.status.OPTIMAL: 28 | print 'Optimal Objective:', m.objVal 29 | iter = 0 30 | for r in range(NumberofVariable): 31 | if y[r].x > 0.0001: 32 | optimalroutes[iter] = routes[r] 33 | iter+=1 34 | print r, y[r].x 35 | else: 36 | print 'No solution' 37 | return optimalroutes 38 | 39 | def get_fiq(i,q): 40 | 41 | if q < mindemand or q not in qlist: 42 | prev[i,q] = [-99,-99] # dummy 43 | return float('infinity'); 44 | elif fiq[i,q] != float('infinity'): 45 | return fiq[i,q] 46 | elif q == demand[i]: 47 | prev[i,q] = [1,q-demand[i]] 48 | fiq[i,q] = c[1][i] 49 | return c[1][i] 50 | else: 51 | mintemp = float('infinity') 52 | minj = 0 53 | for j in range(2,numnodes+1): 54 | if j != i: 55 | temp = get_fiq(j,q-demand[i]) + c[j][i] 56 | #print i,j,q,demand[i],prev[j,q-demand[i]][0] 57 | if i != prev[j,q-demand[i]][0] and temp < mintemp: # the first condition(remove 2-loop) might not be right, Ignores some solutions 58 | mintemp = temp 59 | minj = j 60 | fiq[i,q] = mintemp 61 | 62 | if minj != 0: 63 | prev[i,q] = [minj,q-demand[i]] 64 | else: 65 | prev[i,q] = [-99,-99] # dummy 66 | 67 | #fiq[i,q] = min((get_fiq(j,q-demand[i]) + c[j][i] for j in range(2,numnodes+1) if j != i)); Not able to extract j here 68 | return fiq[i,q] 69 | 70 | def reconstructpath(i,q): 71 | if fiq[i,q] == float('infinity'): 72 | return [] 73 | elif q == 0: 74 | return [1] 75 | else: 76 | #print i,q,prev[i,q] 77 | return reconstructpath(prev[i,q][0], prev[i,q][1]) + [i] 78 | 79 | (numnodes, coordinates, distance, capacity, demand,numofvehicles) = read_vrplib_file(file_name) 80 | #print distance 81 | 82 | 83 | 84 | 85 | (routes) = generate_random_solution(numnodes, distance, capacity, demand,numofvehicles) 86 | initialroutecount = len(routes) 87 | 88 | ksitranspose = defaultdict(list) 89 | routecost = defaultdict(float) 90 | for r in range(initialroutecount): 91 | routecost[r],ksitranspose[r] = get_routecst_ksitranspose(routes[r],numnodes) 92 | 93 | print 'ksitranspose',ksitranspose 94 | print 'routecost',sum(routecost) 95 | 96 | #plotsolution(numnodes,coordinates,routes); 97 | 98 | # Model 99 | master = Model("MASTER") 100 | master.modelSense = GRB.MINIMIZE 101 | 102 | # Create decision variables 103 | y = {} 104 | for r in range(initialroutecount): 105 | y[r] = master.addVar(lb=0.0, vtype=GRB.CONTINUOUS,obj=routecost[r],name='y_%s' % (r)) 106 | 107 | # Update model to integrate new variables 108 | master.update() 109 | 110 | # constraints 111 | custconstr = {} 112 | for i in range(2,numnodes+1): 113 | custconstr[i] = master.addConstr( 114 | quicksum(ksitranspose[r][i] * y[r] for r in range(initialroutecount)) >= 1, 115 | 'cust_%s' % (i)) 116 | 117 | vehiclecosntr = master.addConstr( 118 | -1 * quicksum(y[r] for r in range(initialroutecount)) >= - numofvehicles, 119 | 'vehicle' ) 120 | 121 | # Solve 122 | 123 | master.update() 124 | 125 | mindemand = min(demand[i] for i in range(2,numnodes+1)) 126 | 127 | #Generate the possible values of q 128 | tempq = list(demand.values()) 129 | tempq1 = list(demand.values()) 130 | stuff = list(demand.values()) 131 | stuff.sort() 132 | tempq.sort() 133 | tempq1.sort() 134 | #delete 0 135 | del stuff[0] 136 | del tempq[0] 137 | del tempq1[0] 138 | #print 'tempq',tempq 139 | 140 | #print 'stuff',stuff 141 | 142 | for i in range(len(tempq)): 143 | if sum(stuff) > capacity: 144 | stuff.pop() 145 | else: 146 | break 147 | 148 | print 'stuff2',stuff,sum(stuff) 149 | 150 | for L in range(2,len(stuff)+1): 151 | for subset in combinations(tempq1, L): 152 | tempq.append(sum(list(subset))) 153 | # get distinct values 154 | qlist1 = list(set(tempq)) 155 | #print 'qlist1',qlist1 156 | # get q which is less than the capacity 157 | qlist = [x for x in qlist1 if x <= capacity] 158 | print qlist 159 | 160 | iter = 1 161 | while (iter < 100): 162 | master.optimize() 163 | #printSolution(master) 164 | pi =[] 165 | pi = [c.Pi for c in master.getConstrs()] # dual variables 166 | theta = pi.pop() 167 | pi.insert(0, 0) 168 | 169 | c={} 170 | c = [[0 for col in range(numnodes+1)] for row in range(numnodes+1)] 171 | for i in range(1,numnodes+1): 172 | for j in range(1,numnodes+1): 173 | c[i][j] = distance[i][j] - pi[j-1] 174 | 175 | #Dynamic programming 176 | 177 | 178 | fiq = defaultdict(float) 179 | prev = defaultdict(list) 180 | cgroutes = defaultdict(list) 181 | #tempcgroutes = [] 182 | 183 | # initial conditions for fiq 184 | for q in qlist: 185 | for i in range(2,numnodes+1): 186 | fiq[i,q] = float('infinity') 187 | 188 | for q in qlist: 189 | for i in range(2,numnodes+1): 190 | #print i,q 191 | fiq[i,q] = get_fiq(i,q) 192 | 193 | 194 | fiq0 = defaultdict(float) 195 | for q in qlist: 196 | for i in range(2,numnodes+1): 197 | fiq0[i,q] = fiq[i,q] + c[i][1] 198 | 199 | print 'print fiq0, path' 200 | for q in qlist: 201 | for i in range(2,numnodes+1): 202 | if fiq0[i,q] < - theta: 203 | cgroutes[i,q] = reconstructpath(i,q) 204 | cgroutes[i,q].append(1) # append depot(1) at the end of the routes 205 | #print fiq0[i,q],i,q,cgroutes[i,q] 206 | 207 | 208 | #Add logic to discard routes with symmetry. 209 | ############################################# 210 | ################################################# 211 | 212 | # Column generation 213 | numcols = len(cgroutes) 214 | 215 | if numcols == 0: 216 | print 'numcols 0' 217 | break 218 | 219 | 220 | oldroutecount = len(routes) 221 | K=oldroutecount 222 | print 'old', len(routecost) 223 | for i in cgroutes: 224 | routecost[K],ksitranspose[K] = get_routecst_ksitranspose(cgroutes[i],numnodes) 225 | routes[K] = cgroutes[i] 226 | 227 | # add new columns to the master problem 228 | col = Column() 229 | for i in range(2,numnodes+1): 230 | col.addTerms(ksitranspose[K][i], custconstr[i]) 231 | col.addTerms(-1,vehiclecosntr) 232 | y[K] = master.addVar(lb=0.0, vtype=GRB.CONTINUOUS,obj=routecost[K], column=col,name='y_%s' % (K)) 233 | master.update() 234 | K +=1 235 | 236 | 237 | print 'new numroutes', len(routecost) 238 | print 'new numvars' ,master.numVars 239 | iter +=1 240 | 241 | master.write("VRP"+".lp") 242 | 243 | print 'K,master.numVars',K,master.numVars 244 | 245 | # solve IP 246 | 247 | # Set variable type back to binary 248 | NumberofVariable = len(routecost) 249 | for i in range(NumberofVariable): 250 | y[i].vType = GRB.BINARY 251 | 252 | master.update() 253 | master.optimize() 254 | 255 | optimalroutes = defaultdict(list) 256 | optimalroutes = printSolution(master,NumberofVariable) 257 | 258 | print optimalroutes 259 | 260 | #plot optimal routes 261 | plotsolution(numnodes,coordinates,optimalroutes); 262 | 263 | raw_input("press [enter] to continue") 264 | tottime = time.clock() - start 265 | print 'total time = ', tottime 266 | 267 | -------------------------------------------------------------------------------- /main_2.py: -------------------------------------------------------------------------------- 1 | from readvrplib import read_vrplib_file 2 | from NNH import generate_random_solution 3 | from plotsolution import plotsolution 4 | from gurobipy import * 5 | from collections import defaultdict 6 | from itertools import chain, combinations 7 | 8 | import time 9 | start = time.clock() 10 | # Read file name from command line argument 11 | if len(sys.argv) < 2: 12 | print 'Usage: main.py filename' 13 | quit() 14 | 15 | file_name = sys.argv[1] 16 | 17 | def get_routecst_ksitranspose(route,numnodes): 18 | 19 | ksit = [0] * (numnodes+1) 20 | routecst = 0 21 | routelength = len(route) 22 | lastcustomer = 1 23 | 24 | for i,cust in enumerate(route[1:]): 25 | ksit[cust] +=1 26 | routecst += distance[cust][lastcustomer] 27 | lastcustomer = cust 28 | 29 | return routecst,ksit 30 | 31 | def printSolution(m,NumberofVariable): 32 | global optimalroutes 33 | if m.status == GRB.status.OPTIMAL: 34 | print 'Optimal Objective:', m.objVal 35 | iter = 0 36 | for r in range(NumberofVariable): 37 | if y[r].x > 0.0001: 38 | optimalroutes[iter] = routes[r] 39 | iter+=1 40 | print r, y[r].x 41 | else: 42 | print 'No solution' 43 | return optimalroutes 44 | 45 | def get_fiq(i,q): 46 | 47 | if q < mindemand or q not in qlist: 48 | prev[i,q] = [-99,-99] # dummy 49 | return float('infinity'); 50 | elif fiq[i,q] != float('infinity'): 51 | return fiq[i,q] 52 | elif q == demand[i]: 53 | prev[i,q] = [1,q-demand[i]] 54 | fiq[i,q] = c[1][i] 55 | return c[1][i] 56 | else: 57 | mintemp = float('infinity') 58 | minj = 0 59 | for j in range(2,numnodes+1): 60 | if j != i: 61 | temp = get_fiq(j,q-demand[i]) + c[j][i] 62 | #print i,j,q,demand[i],prev[j,q-demand[i]][0] 63 | if i != prev[j,q-demand[i]][0] and temp < mintemp: # the first condition(remove 2-loop) might not be right, Ignores some solutions 64 | mintemp = temp 65 | minj = j 66 | fiq[i,q] = mintemp 67 | 68 | if minj != 0: 69 | prev[i,q] = [minj,q-demand[i]] 70 | else: 71 | prev[i,q] = [-99,-99] # dummy 72 | 73 | #fiq[i,q] = min((get_fiq(j,q-demand[i]) + c[j][i] for j in range(2,numnodes+1) if j != i)); Not able to extract j here 74 | return fiq[i,q] 75 | 76 | def reconstructpath(i,q): 77 | if fiq[i,q] == float('infinity'): 78 | return [] 79 | elif q == 0: 80 | return [1] 81 | else: 82 | return reconstructpath(prev[i,q][0], prev[i,q][1]) + [i] 83 | 84 | (numnodes, coordinates, distance, capacity, demand,numofvehicles) = read_vrplib_file(file_name) 85 | #print distance 86 | 87 | (routes) = generate_random_solution(numnodes, distance, capacity, demand,numofvehicles) 88 | initialroutecount = len(routes) 89 | 90 | ksitranspose = defaultdict(list) 91 | routecost = defaultdict(float) 92 | for r in range(initialroutecount): 93 | routecost[r],ksitranspose[r] = get_routecst_ksitranspose(routes[r],numnodes) 94 | 95 | #print 'ksitranspose',ksitranspose 96 | #print 'routecost',sum(routecost) 97 | 98 | #plotsolution(numnodes,coordinates,routes); 99 | 100 | # Model 101 | master = Model("MASTER") 102 | master.modelSense = GRB.MINIMIZE 103 | 104 | # Create decision variables 105 | y = {} 106 | for r in range(initialroutecount): 107 | y[r] = master.addVar(lb=0.0, vtype=GRB.CONTINUOUS,obj=routecost[r],name='y_%s' % (r)) 108 | 109 | # Update model to integrate new variables 110 | master.update() 111 | 112 | # constraints 113 | custconstr = {} 114 | for i in range(2,numnodes+1): 115 | custconstr[i] = master.addConstr( 116 | quicksum(ksitranspose[r][i] * y[r] for r in range(initialroutecount)) >= 1, 117 | 'cust_%s' % (i)) 118 | 119 | vehiclecosntr = master.addConstr( 120 | -1 * quicksum(y[r] for r in range(initialroutecount)) >= - numofvehicles, 121 | 'vehicle' ) 122 | 123 | # Solve 124 | 125 | master.update() 126 | 127 | mindemand = min(demand[i] for i in range(2,numnodes+1)) 128 | 129 | #Generate the possible values of q 130 | tempq = list(demand.values()) 131 | tempq1 = list(demand.values()) 132 | stuff = list(demand.values()) 133 | stuff.sort() 134 | tempq.sort() 135 | tempq1.sort() 136 | #delete 0 137 | del stuff[0] 138 | del tempq[0] 139 | del tempq1[0] 140 | #print 'tempq',tempq 141 | 142 | #print 'stuff',stuff 143 | 144 | for i in range(len(tempq)): 145 | if sum(stuff) > capacity: 146 | stuff.pop() 147 | else: 148 | break 149 | 150 | print 'stuff2',stuff,sum(stuff) 151 | 152 | for L in range(2,len(stuff)+1): 153 | for subset in combinations(tempq1, L): 154 | tempq.append(sum(list(subset))) 155 | 156 | qlist1 = list(set(tempq)) 157 | 158 | qlist = [x for x in qlist1 if x <= capacity] 159 | print qlist 160 | 161 | iter = 1 162 | temp = [] 163 | condition = True 164 | temp1 = [] 165 | while condition: 166 | master.optimize() 167 | #printSolution(master) 168 | pi =[] 169 | pi = [c.Pi for c in master.getConstrs()] # dual variables 170 | theta = pi.pop() 171 | pi.insert(0, 0) 172 | 173 | c={} 174 | c = [[0 for col in range(numnodes+1)] for row in range(numnodes+1)] 175 | for i in range(1,numnodes+1): 176 | for j in range(1,numnodes+1): 177 | c[i][j] = distance[i][j] - pi[j-1] 178 | 179 | #Dynamic programming 180 | 181 | 182 | fiq = defaultdict(float) 183 | prev = defaultdict(list) 184 | cgroutes = [] 185 | #tempcgroutes = [] 186 | 187 | # initial conditions for fiq 188 | for q in qlist: 189 | for i in range(2,numnodes+1): 190 | fiq[i,q] = float('infinity') 191 | 192 | for q in qlist: 193 | for i in range(2,numnodes+1): 194 | #print i,q 195 | fiq[i,q] = get_fiq(i,q) 196 | 197 | 198 | fiq0 = defaultdict(float) 199 | minfiq = (numnodes+1)*[999999] 200 | a = [1]*(numnodes+1) 201 | b = [1]*(numnodes+1) 202 | for i in range(2,numnodes+1): 203 | for q in qlist: 204 | fiq0[i,q] = fiq[i,q] + c[i][1] 205 | if fiq0[i,q] <= minfiq[i]: 206 | minfiq[i] = fiq0[i,q] 207 | a[i]= i 208 | b[i]= q 209 | 210 | print 'print fiq0, path' 211 | testcount = 0 212 | for i in range(2,numnodes +1): 213 | if minfiq[i] <= -theta: 214 | temp = reconstructpath(a[i],b[i]) 215 | temp.append(1) 216 | if temp == temp1: 217 | condition = False 218 | if temp[::-1] not in routes.values(): 219 | cgroutes.append(temp) 220 | testcount += 1 221 | temp1 = temp 222 | 223 | # Column generation 224 | numcols = len(cgroutes) 225 | 226 | if numcols == 0: 227 | print 'numcols 0' 228 | condition = False 229 | break 230 | 231 | 232 | 233 | oldroutecount = len(routes) 234 | K=oldroutecount 235 | print 'old', len(routecost) 236 | for i in cgroutes: 237 | routecost[K],ksitranspose[K] = get_routecst_ksitranspose(i,numnodes) 238 | routes[K] = i 239 | 240 | # add new columns to the master problem 241 | col = Column() 242 | for i in range(2,numnodes+1): 243 | col.addTerms(ksitranspose[K][i], custconstr[i]) 244 | col.addTerms(-1,vehiclecosntr) 245 | y[K] = master.addVar(lb=0.0, vtype=GRB.CONTINUOUS,obj=routecost[K], column=col,name='y_%s' % (K)) 246 | master.update() 247 | K +=1 248 | 249 | 250 | print 'new numroutes', len(routecost) 251 | print 'new numvars' ,master.numVars 252 | iter +=1 253 | #if iter > 100: 254 | #break 255 | #master.write("VRP"+".lp") 256 | 257 | #print 'K,master.numVars',K,master.numVars 258 | 259 | # solve IP 260 | 261 | # Set variable type back to binary 262 | NumberofVariable = len(routecost) 263 | for i in range(NumberofVariable): 264 | y[i].vType = GRB.BINARY 265 | 266 | master.update() 267 | master.optimize() 268 | 269 | optimalroutes = defaultdict(list) 270 | optimalroutes = printSolution(master,NumberofVariable) 271 | 272 | print optimalroutes 273 | print routes 274 | 275 | #plot optimal routes 276 | plotsolution(numnodes,coordinates,optimalroutes); 277 | 278 | raw_input("press [enter] to continue") 279 | tottime = time.clock() - start 280 | print 'total time = ', tottime 281 | -------------------------------------------------------------------------------- /maingams.py: -------------------------------------------------------------------------------- 1 | from readvrplib import read_vrplib_file 2 | from NNH import generate_random_solution 3 | from plotsolution import plotsolution 4 | from collections import defaultdict 5 | from itertools import chain, combinations 6 | 7 | file_name = 'C:/Users/Aditya/Desktop/VRP Instances/P-n22-k2.vrp' 8 | 9 | def get_routecst_ksitranspose(route,numnodes): 10 | 11 | ksit = [1e-250] * (numnodes+1) # GAMS does not like zeros!! 12 | routecst = 0 13 | routelength = len(route) 14 | lastcustomer = 1 15 | 16 | for i,cust in enumerate(route[1:]): 17 | ksit[cust] +=1 18 | routecst += distance[cust][lastcustomer] 19 | lastcustomer = cust 20 | 21 | return routecst,ksit 22 | 23 | def getOptimalRoutes(): 24 | global optimalroutes 25 | iter = 0 26 | for rec in t4.out_db["y"]: 27 | if rec.level > 0.0001: 28 | optimalroutes[iter] = routes[int(rec.keys[0])] 29 | iter+=1 30 | 31 | return optimalroutes 32 | 33 | def get_fiq(i,q): 34 | 35 | if q < mindemand or q not in qlist: 36 | prev[i,q] = [-99,-99] # dummy 37 | return float('infinity'); 38 | elif fiq[i,q] != float('infinity'): 39 | return fiq[i,q] 40 | elif q == demand[i]: 41 | prev[i,q] = [1,q-demand[i]] 42 | fiq[i,q] = c[1][i] 43 | return c[1][i] 44 | else: 45 | mintemp = float('infinity') 46 | minj = 0 47 | for j in range(2,numnodes+1): 48 | if j != i: 49 | temp = get_fiq(j,q-demand[i]) + c[j][i] 50 | #print i,j,q,demand[i],prev[j,q-demand[i]][0] 51 | if i != prev[j,q-demand[i]][0] and temp < mintemp: # the first condition(remove 2-loop) might not be right, Ignores some solutions 52 | mintemp = temp 53 | minj = j 54 | fiq[i,q] = mintemp 55 | 56 | if minj != 0: 57 | prev[i,q] = [minj,q-demand[i]] 58 | else: 59 | prev[i,q] = [-99,-99] # dummy 60 | 61 | #fiq[i,q] = min((get_fiq(j,q-demand[i]) + c[j][i] for j in range(2,numnodes+1) if j != i)); Not able to extract j here 62 | return fiq[i,q] 63 | 64 | def reconstructpath(i,q): 65 | if fiq[i,q] == float('infinity'): 66 | return [] 67 | elif q == 0: 68 | return [1] 69 | else: 70 | #print i,q,prev[i,q] 71 | return reconstructpath(prev[i,q][0], prev[i,q][1]) + [i] 72 | 73 | (numnodes, coordinates, distance, capacity, demand,numofvehicles) = read_vrplib_file(file_name) 74 | #print distance 75 | 76 | (routes) = generate_random_solution(numnodes, distance, capacity, demand,numofvehicles) 77 | initialroutecount = len(routes) 78 | 79 | ksitranspose = defaultdict(list) 80 | routecost = defaultdict(float) 81 | for r in range(initialroutecount): 82 | routecost[r],ksitranspose[r] = get_routecst_ksitranspose(routes[r],numnodes) 83 | 84 | print 'ksitranspose',ksitranspose 85 | print 'routecost',sum(routecost) 86 | 87 | #plotsolution(numnodes,coordinates,routes); 88 | 89 | mindemand = min(demand[i] for i in range(2,numnodes+1)) 90 | 91 | #Generate the possible values of q 92 | tempq = list(demand.values()) 93 | tempq1 = list(demand.values()) 94 | stuff = list(demand.values()) 95 | stuff.sort() 96 | tempq.sort() 97 | tempq1.sort() 98 | #delete 0 99 | del stuff[0] 100 | del tempq[0] 101 | del tempq1[0] 102 | #print 'tempq',tempq 103 | 104 | #print 'stuff',stuff 105 | 106 | for i in range(len(tempq)): 107 | if sum(stuff) > capacity: 108 | stuff.pop() 109 | else: 110 | break 111 | 112 | print 'stuff2',stuff,sum(stuff) 113 | 114 | for L in range(2,len(stuff)+1): 115 | for subset in combinations(tempq1, L): 116 | tempq.append(sum(list(subset))) 117 | # get distinct values 118 | qlist1 = list(set(tempq)) 119 | #print 'qlist1',qlist1 120 | # get q which is less than the capacity 121 | qlist = [x for x in qlist1 if x <= capacity] 122 | #print qlist 123 | 124 | # Model 125 | 126 | from gams import * 127 | import sys 128 | 129 | def get_model_text(): 130 | return ''' 131 | Set i customers; 132 | 133 | Scalar K Number of vehicles; 134 | 135 | Set 136 | r possible routes /0*250000/ 137 | rr(r) dynamic set of routes 138 | ; 139 | 140 | Parameters 141 | rc(r) Cost for each route 142 | ksi(r,i) Number of times a customer is visited in each route; 143 | 144 | $gdxin vrpdatabase 145 | $load i K rr rc ksi 146 | 147 | %currvariabletype% Variable 148 | y(r) Route selected in the optimal solution; 149 | 150 | variable 151 | z cost; 152 | 153 | y.up(r) = 1; 154 | 155 | 156 | Equations 157 | cost define objective function 158 | cover(i) All customer nodes are visited 159 | vehiclenumber satisfy demand at market j ; 160 | 161 | cost .. z =e= sum(rr, rc(rr)*y(rr)) ; 162 | 163 | cover(i) .. sum(rr, ksi(rr,i) * y(rr)) =g= 1 ; 164 | 165 | vehiclenumber .. -1 * sum(rr, y(rr)) =g= - K ; 166 | 167 | Model setpartitionvrp /all/ ; 168 | 169 | Solve setpartitionvrp using %problemtype% minimizing z ; 170 | 171 | 172 | ''' 173 | 174 | if __name__ == "__main__": 175 | if len(sys.argv) > 1: 176 | ws = GamsWorkspace(system_directory = sys.argv[1]) 177 | else: 178 | ws = GamsWorkspace() 179 | 180 | customers = range(2,numnodes+1) 181 | customers = map(str,customers) 182 | initialroutes = range(initialroutecount) 183 | initialroutes = map(str,initialroutes) 184 | 185 | 186 | db = ws.add_database('vrpdatabase') 187 | 188 | i = GamsSet(db, "i", 1, "customers") 189 | for c in customers: 190 | i.add_record(c) 191 | 192 | rr = GamsSet(db, "rr", 1, "routes") 193 | for i in initialroutes: 194 | rr.add_record(i) 195 | 196 | rc = GamsParameter(db, "rc", 1, "Cost for each route") 197 | for r in initialroutes: 198 | rc.add_record(r).value = routecost[int(r)] 199 | #print routecost[int(r)] 200 | 201 | ksi = GamsParameter(db, "ksi", 2, "Number of times a customer is visited in each route") 202 | for r in initialroutes: 203 | for c in customers: 204 | ksi.add_record((r,c)).value = ksitranspose[int(r)][int(c)] 205 | 206 | K = GamsParameter(db, "K", 0, "number of vehicles available") 207 | K.add_record().value = numofvehicles 208 | 209 | t4 = GamsJob(ws, source=get_model_text()) 210 | opt = GamsOptions(ws) 211 | 212 | opt.defines["gdxincname"] = db.name 213 | opt.defines["currvariabletype"] = "positive" 214 | opt.defines["problemtype"] = "lp" 215 | opt.all_model_types = "Gurobi" 216 | 217 | #db.export("E:/Study/spring2013/635/project/vehicle routing/720 code/result.gdx") 218 | 219 | #t4.run(opt, databases = db,output=sys.stdout) 220 | # for rec in t4.out_db["y"]: 221 | # if rec.level > 0.00001: 222 | # print "y(" + rec.keys[0] + "): level=" + str(rec.level) + " marginal=" + str(rec.marginal) 223 | #for rec in t4.out_db["z"]: 224 | # print "z=" + str(rec.level) 225 | 226 | 227 | iter = 1 228 | while (iter < 100): 229 | t4.run(opt, databases = db,output=sys.stdout) 230 | #printSolution(master) 231 | pi =[] 232 | pi = [c1.marginal for c1 in t4.out_db["cover"]] 233 | theta = [c2.marginal for c2 in t4.out_db["vehiclenumber"]] 234 | pi.insert(0, 0) 235 | print "theta", theta[0] 236 | 237 | c={} 238 | c = [[0 for col in range(numnodes+1)] for row in range(numnodes+1)] 239 | for i in range(1,numnodes+1): 240 | for j in range(1,numnodes+1): 241 | c[i][j] = distance[i][j] - pi[j-1] 242 | 243 | #Dynamic programming 244 | 245 | 246 | fiq = defaultdict(float) 247 | prev = defaultdict(list) 248 | cgroutes = defaultdict(list) 249 | #tempcgroutes = [] 250 | 251 | # initial conditions for fiq 252 | for q in qlist: 253 | for i in range(2,numnodes+1): 254 | fiq[i,q] = float('infinity') 255 | 256 | for q in qlist: 257 | for i in range(2,numnodes+1): 258 | #print i,q 259 | fiq[i,q] = get_fiq(i,q) 260 | 261 | 262 | fiq0 = defaultdict(float) 263 | for q in qlist: 264 | for i in range(2,numnodes+1): 265 | fiq0[i,q] = fiq[i,q] + c[i][1] 266 | 267 | print 'print fiq0, path' 268 | for q in qlist: 269 | for i in range(2,numnodes+1): 270 | if fiq0[i,q] < - theta[0]: 271 | cgroutes[i,q] = reconstructpath(i,q) 272 | cgroutes[i,q].append(1) # append depot(1) at the end of the routes 273 | #print fiq0[i,q],i,q,cgroutes[i,q] 274 | 275 | 276 | #Add logic to discard routes with symmetry. 277 | ############################################# 278 | ################################################# 279 | 280 | # Column generation 281 | numcols = len(cgroutes) 282 | 283 | if numcols == 0: 284 | print 'numcols 0' 285 | break 286 | 287 | 288 | oldroutecount = len(routes) 289 | K=oldroutecount 290 | print 'old', len(routecost) 291 | for i in cgroutes: 292 | routecost[K],ksitranspose[K] = get_routecst_ksitranspose(cgroutes[i],numnodes) 293 | routes[K] = cgroutes[i] 294 | 295 | # add new columns to the master problem 296 | rr.add_record(str(K)) 297 | rc.add_record(str(K)).value = routecost[K] 298 | for c in customers: 299 | ksi.add_record((str(K),c)).value = ksitranspose[K][int(c)] 300 | 301 | K +=1 302 | 303 | 304 | #print 'new numroutes', len(routecost) 305 | #print 'new numvars' ,master.numVars 306 | iter +=1 307 | 308 | #master.write("VRP"+".lp") 309 | 310 | #print 'K,master.numVars',K,master.numVars 311 | 312 | # solve IP 313 | 314 | # Set variable type back to binary 315 | opt.defines["currvariabletype"] = "binary" 316 | opt.defines["problemtype"] = "mip" 317 | opt.optcr = 0.0 # Solve to optimality 318 | 319 | t4.run(opt, databases = db,output=sys.stdout) 320 | 321 | optimalroutes = defaultdict(list) 322 | optimalroutes = getOptimalRoutes() 323 | 324 | print optimalroutes 325 | 326 | #plot optimal routes 327 | plotsolution(numnodes,coordinates,optimalroutes); 328 | 329 | raw_input("press [enter] to continue") 330 | -------------------------------------------------------------------------------- /plotsolution.py: -------------------------------------------------------------------------------- 1 | try: 2 | import matplotlib.pyplot as plt 3 | import networkx as nx 4 | except ImportError: 5 | print "install 'networkx' and 'matplotlib' for plotting" 6 | exit(0) 7 | 8 | from collections import defaultdict 9 | 10 | def plotsolution(numnodes,coordinates,routes): 11 | plt.ion() # interactive mode on 12 | G=nx.Graph() 13 | 14 | nodes = range(1,numnodes+1) 15 | nodedict = {} 16 | for i in nodes: 17 | nodedict[i] = i 18 | 19 | nodecolorlist = ['b' for i in nodes] 20 | nodecolorlist[0] = 'r' 21 | 22 | # nodes 23 | nx.draw_networkx_nodes(G, coordinates, node_color=nodecolorlist, nodelist=nodes) 24 | # labels 25 | nx.draw_networkx_labels(G,coordinates,font_size=9,font_family='sans-serif',labels = nodedict) 26 | 27 | edgelist = defaultdict(list) 28 | 29 | colors = ['Navy','PaleVioletRed','Yellow','Darkorange','Chartreuse','CadetBlue','Tomato','Turquoise','Teal','Violet','Silver','LightSeaGreen','DeepPink', 'FireBrick','Blue','Green'] 30 | 31 | for i in (routes): 32 | edge1 = 1 33 | for j in routes[i][1:]: 34 | edge2 = j 35 | edgelist[i].append((edge1,edge2)) 36 | edge1 = edge2 37 | nx.draw_networkx_edges(G,coordinates,edgelist=edgelist[i], 38 | width=6,alpha=0.5,edge_color=colors[i]) #,style='dashed' 39 | 40 | plt.savefig("path.png") 41 | 42 | plt.show() -------------------------------------------------------------------------------- /readvrplib.py: -------------------------------------------------------------------------------- 1 | from math import sqrt 2 | import re 3 | 4 | def read_vrplib_file(file_path): 5 | 6 | #returns instance_data = (numnodes, coordinates, distance, capacity, demand,numofvehicles) 7 | 8 | file = open(file_path, 'r') 9 | 10 | for line in file: 11 | 12 | if line.startswith('NAME'): 13 | file_name = line.split().pop() 14 | #matches = re.search('.+\-.+\-k(.+)$',file_name) 15 | #numofvehicles = int(matches.group(1)) 16 | numofvehicles = int(re.match('.*?([0-9]+)$', file_name).group(1)) 17 | 18 | if line.startswith('CAPACITY'): 19 | capacity = int(line.split().pop()) 20 | 21 | if line.startswith('DIMENSION'): 22 | numnodes = int(line.split().pop()) 23 | nodes = range(1,numnodes+1) 24 | 25 | if line.rstrip() == 'DEMAND_SECTION': 26 | demand = {} 27 | for i in range(1,numnodes+1): 28 | line = file.next() 29 | demand[i] = int(line.split().pop()) 30 | 31 | #print demand 32 | #print '--------' 33 | 34 | if line.rstrip() == 'NODE_COORD_SECTION': 35 | distance = [[0 for col in range(numnodes+1)] for row in range(numnodes+1)] 36 | coordinates = {} 37 | for i in range(1,numnodes+1): 38 | line = file.next() 39 | coordinates[i] = [int(line.split()[1]),int(line.split()[2])] 40 | #print coordinates 41 | #print '--------' 42 | 43 | # Calculate distances between vertices 44 | for i in range(1,numnodes+1): 45 | for j in range(i,numnodes+1): 46 | if i == j: 47 | distance[i][j] = 0 48 | else: 49 | dist = round(sqrt(((coordinates[i][0] - coordinates[j][0])**2) + ((coordinates[i][1] - coordinates[j][1])**2))) 50 | distance[i][j] = dist 51 | distance[j][i] = dist 52 | 53 | #print distance 54 | #print '--------' 55 | 56 | file.close() 57 | instance_data = (numnodes, coordinates, distance, capacity, demand,numofvehicles) 58 | return instance_data 59 | 60 | 61 | 62 | 63 | --------------------------------------------------------------------------------