├── vehicle_params.csv ├── README.md ├── demand.csv └── new_genetic_11_10.py /vehicle_params.csv: -------------------------------------------------------------------------------- 1 | vehicle_type,fix_cost,max_load_t,fuel_consumption,ref_cost_door_closed,ref_cost_door_open,carb_emission,vehicle_quantity 2 | 1,100,2.5,0.2,4.5,5,2.75,6 3 | 2,150,3,0.25,5,5.5,2.8,4 4 | 3,200,3.5,0.3,6,6.5,2.9,3 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # hetero_fleet_cvrp 2 | Optimization of Green Fresh Food Logistics with Heterogeneous Fleet Vehicle Route Problem by Genetic Algorithm 3 | 4 | The purpose of this project is to develop an optimal distribution route for the fresh food logistics 5 | considering the reduction of total distribution cost. The distribution cost includes the following: 6 | * fixed cost 7 | * fuel consumption cost 8 | * refrigeration cost 9 | * overtime cost 10 | * carbon emission cost 11 | * damage cost 12 | -------------------------------------------------------------------------------- /demand.csv: -------------------------------------------------------------------------------- 1 | num,x_axis_m,y_axis_m,demand_per_t,early_time_hour,late_time_hour,service_time_hour 2 | 0,11.33,2,0,0,0,0 3 | 1,15.21,17.42,1000,1,1.17,0.25 4 | 2,14.35,13.03,1300,0.67,2,0.17 5 | 3,15.19,16.02,2000,0.75,1,0.17 6 | 4,5.07,15.69,2100,0.5,0.83,0.13 7 | 5,10.12,17.26,1700,0.5,0.83,0.25 8 | 6,9.17,17.34,1200,0.5,1,0.17 9 | 7,10.03,19.32,1500,0.67,1,0.13 10 | 8,12.2,21.27,2000,1.17,1.67,0.13 11 | 9,9.97,13.41,1400,0.5,1,0.17 12 | 10,18.72,9.52,900,0.5,0.83,0.25 13 | 11,19.89,17.07,800,0.67,1.17,0.25 14 | 12,20.48,10.99,1300,1.17,1.5,0.17 15 | 13,2,19.78,1900,0.5,1,0.13 16 | 14,4.01,7.82,2200,1,1.5,0.17 17 | 15,20.08,9.52,1300,0.5,0.83,0.13 18 | 16,17.4,11.89,700,0.5,1.17,0.17 19 | 17,5.5,6.76,1600,0.5,1.5,0.25 20 | 18,20.46,16.26,1800,0.67,1,0.17 21 | 19,20.83,8.91,2200,1.17,1.5,0.17 22 | 20,21.94,5.39,1100,0.5,0.83,0.17 23 | -------------------------------------------------------------------------------- /new_genetic_11_10.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sat Nov 7 15:52:09 2020 4 | 5 | @author: StarkPC 6 | """ 7 | 8 | ### Reference website for genetic algo using roulette wheel selection: 9 | ### https://github.com/data-cat-ghub/datacat_blog 10 | 11 | import pandas as pd 12 | import numpy as np 13 | import math 14 | import random 15 | import matplotlib.pyplot as plt 16 | import mpld3 17 | # from gurobipy import Model, GRB, quicksum 18 | import collections 19 | # import parameters 20 | import time 21 | 22 | start_time = time.time() 23 | random.seed(210) 24 | 25 | class Simulation: 26 | 27 | def Module_Initialization(self): 28 | # reading files 29 | self.df_demand = pd.read_csv("demand.csv") 30 | self.df_vehicle = pd.read_csv("vehicle_params.csv") 31 | 32 | # input vehicle parameters 33 | self.v_type_dict = {1:1, 2:1, 3:1, 4:1, 5:1, 6:1, 7:2, 8:2, 9:2, 10:2, 11:3, 12:3, 13:3} 34 | self.v_cost_dict = {1: 100, 2: 150, 3: 200} 35 | self.v_max_load_dict = {1: 2500, 2: 3000, 3: 3500} 36 | self.v_unit_fuel_cost_dict = {1: 0.2, 2: 0.25, 3: 0.3} 37 | self.v_c_energy_dict = {1: 4.5, 2: 5, 3: 6} 38 | self.v_o_energy_dict = {1: 5, 2: 5.5, 3: 6.5} 39 | self.v_carbon_coeff_dict = {1: 2.75, 2: 2.8, 3: 2.9} 40 | 41 | # input demand parameters 42 | self.d_x_dict = dict(zip(list(self.df_demand["num"]), list(self.df_demand["x_axis_m"]))) 43 | self.d_y_dict = dict(zip(list(self.df_demand["num"]), list(self.df_demand["y_axis_m"]))) 44 | self.d_demand_dict = dict(zip(list(self.df_demand["num"]), list(self.df_demand["demand_per_t"]))) 45 | self.d_e_time_dict = dict(zip(list(self.df_demand["num"]), list(self.df_demand["early_time_hour"]))) 46 | self.d_l_time_dict = dict(zip(list(self.df_demand["num"]), list(self.df_demand["late_time_hour"]))) 47 | self.d_service_time_dict = dict(zip(list(self.df_demand["num"]), list(self.df_demand["service_time_hour"]))) 48 | 49 | # cumulative travel time to each point 50 | self.w_time_dict = dict(zip(list(self.df_demand["num"]), [0]*21)) 51 | 52 | ############################################################################### 53 | def Module_Vehicle_Quantity(self): 54 | list1 = [] 55 | for val in self.vehicle_order_list: 56 | list1.append(self.v_max_load_dict[self.v_type_dict[val]]) 57 | return list1 58 | 59 | ############################################################################### 60 | def Module_Demand_Quantity(self): 61 | list1 = [] 62 | for val in self.demand_order_list: 63 | list1.append(self.d_demand_dict[val]) 64 | return list1 65 | 66 | ############################################################################### 67 | def Module_Assignment(self, gene_sequence): 68 | 69 | self.vehicle_order_list = gene_sequence[1] 70 | self.vehicle_max_load_list = self.Module_Vehicle_Quantity() 71 | # eg of vehicle_order_list: [9, 8, 4, 7, 13, 5, 11, 3, 10, 6, 1, 12, 2] 72 | # eg of vehicle_max_load_list: [3, 3, 3.5, 2.5, 3, 3.5, 2.5, 2.5, 2.5, 3.5, 2.5, 3, 2.5] 73 | 74 | self.demand_order_list = gene_sequence[0] 75 | self.demand_quant_list = self.Module_Demand_Quantity() 76 | 77 | ############################################################################### 78 | def Module_Route_Planner(self): 79 | 80 | l1 = []; l2 = [] 81 | l_overall = []; l2_overall = [] 82 | cum_sum = 0; car = 0 83 | 84 | vqt_copy = [] 85 | vqt_copy = self.vehicle_max_load_list + [3]*10 86 | for i in range(len(self.demand_quant_list)): 87 | dem = self.demand_quant_list[i] 88 | cum_sum += dem 89 | if (cum_sum > vqt_copy[car]): 90 | l_overall.append(l1) 91 | l2_overall.append(l2) 92 | l1 = []; l2 = [] 93 | cum_sum = dem 94 | l1.append(dem) 95 | l2.append(self.demand_order_list[i]) 96 | car += 1 97 | else: 98 | l1.append(dem) 99 | l2.append(self.demand_order_list[i]) 100 | l_overall.append(l1) 101 | l2_overall.append(l2) 102 | self.route = l2_overall 103 | 104 | ############################################################################### 105 | def Module_Best_Route_Planner(self, gene_sequence): 106 | l1 = []; l2 = [] 107 | l_overall = []; l2_overall = [] 108 | cum_sum = 0; car = 0 109 | 110 | vqt_copy = [] 111 | vqt_copy = self.vehicle_max_load_list + [3]*10 112 | for i in range(len(self.demand_quant_list)): 113 | dem = self.demand_quant_list[i] 114 | cum_sum += dem 115 | if (cum_sum > vqt_copy[car]): 116 | l_overall.append(l1) 117 | l2_overall.append(l2) 118 | l1 = []; l2 = [] 119 | cum_sum = dem 120 | l1.append(dem) 121 | l2.append(self.demand_order_list[i]) 122 | car += 1 123 | else: 124 | l1.append(dem) 125 | l2.append(self.demand_order_list[i]) 126 | l_overall.append(l1) 127 | l2_overall.append(l2) 128 | self.route = l2_overall 129 | return self.route 130 | 131 | ############################################################################### 132 | def Module_Two_Point_Distance(self, pt1, pt2): 133 | x1 = self.d_x_dict[pt1] 134 | x2 = self.d_x_dict[pt2] 135 | y1 = self.d_y_dict[pt1] 136 | y2 = self.d_y_dict[pt2] 137 | 138 | distance = np.sqrt(((x2-x1)**2) + ((y2-y1)**2)) 139 | return distance 140 | 141 | ############################################################################### 142 | def Module_Two_Point_Time(self, pt1, pt2): 143 | time = self.Module_Two_Point_Distance(pt1, pt2)/50 144 | return time 145 | 146 | ############################################################################### 147 | def Module_Distance_Travelled(self): 148 | route_appended = self.route 149 | for i in range(len(route_appended)): 150 | route_appended[i].insert(0,0) 151 | route_appended[i].append(0) 152 | 153 | distance_travelled_list = [] 154 | for i in range(len(route_appended)): 155 | dist_travelled = 0 156 | for j in range(len(route_appended[i])-1): 157 | dist_travelled += self.Module_Two_Point_Distance(route_appended[i][j], route_appended[i][j+1]) 158 | distance_travelled_list.append(dist_travelled) 159 | 160 | return distance_travelled_list 161 | 162 | ############################################################################### 163 | def Module_Fixed_Cost(self): 164 | length = len(self.route) 165 | cost = 0 166 | if (length <= 13): 167 | for val in self.vehicle_order_list[0:length]: 168 | cost += self.v_cost_dict[self.v_type_dict[val]] 169 | else: 170 | cost = 1000000 171 | return cost 172 | 173 | ############################################################################### 174 | def Module_Fuel_Cost(self): 175 | self.distance_travelled_list = self.Module_Distance_Travelled() 176 | cost = 0 177 | 178 | if (len(self.route) <= 13): 179 | for i in range(len(self.route)): 180 | v_number = self.vehicle_order_list[i] 181 | v_type = self.v_type_dict[v_number] 182 | unit_fuel_cons = self.v_unit_fuel_cost_dict[v_type] 183 | dist_travelled = self.distance_travelled_list[i] 184 | cost += unit_fuel_cons * dist_travelled 185 | 186 | fuel_cost = 6.7 * cost 187 | else: 188 | fuel_cost = 1000000 189 | return fuel_cost 190 | 191 | ############################################################################### 192 | def Module_Carbon_Cost(self): 193 | #distance_travelled_list = Module_Distance_Travelled(route, vehicle_order_list) 194 | 195 | cost = 0 196 | if (len(self.route) <= 13): 197 | for i in range(len(self.distance_travelled_list)): 198 | unit_fuel_cons = self.v_unit_fuel_cost_dict[self.v_type_dict[self.vehicle_order_list[i]]] 199 | carb_coeff = self.v_carbon_coeff_dict[self.v_type_dict[self.vehicle_order_list[i]]] 200 | cost += carb_coeff * unit_fuel_cons * self.distance_travelled_list[i] 201 | 202 | fuel_cost = 0.5 * cost 203 | else: 204 | fuel_cost = 1000000 205 | return fuel_cost 206 | ############################################################################### 207 | def Module_Penalty_Cost(self): 208 | cum_time = 0 209 | if (len(self.route) <= 13): 210 | for pt in self.df_demand["num"]: 211 | if (pt == 0): 212 | continue 213 | late_time = max((self.w_time_dict[pt] - self.d_l_time_dict[pt]), 0) 214 | cum_time += late_time 215 | pen_cost = 2 * cum_time * 60 # hour to minute conversion 216 | else: 217 | pen_cost = 1000000 218 | 219 | return (pen_cost) 220 | 221 | ############################################################################### 222 | def Module_Damage_Cost(self): 223 | cum_cost = 0 224 | if (len(self.route) <= 13): 225 | for pt in self.df_demand["num"]: 226 | # skip the inventory location 227 | if (pt == 0): 228 | continue 229 | cost = (self.d_demand_dict[pt] * (math.exp(1/60 * self.w_time_dict[pt]) - 1)) 230 | cum_cost += cost 231 | damage_cost = 1 * cum_cost 232 | else: 233 | damage_cost = 1000000 234 | 235 | return damage_cost 236 | 237 | ############################################################################### 238 | def Module_Refrigeration_Cost(self): 239 | self.travel_times_list = [x/50 for x in self.distance_travelled_list] 240 | 241 | if (len(self.route) <= 13): 242 | cost_travel = 0 243 | cost_service = 0 244 | cost_waiting = 0 245 | # travel time ref costs 246 | for i in range(len(self.route)): 247 | travel_time = self.travel_times_list[i] 248 | v_type = self.v_type_dict[self.vehicle_order_list[i]] 249 | v_c_energy = self.v_c_energy_dict[v_type] 250 | cost_travel += (v_c_energy * travel_time) 251 | # service time ref costs 252 | for i in range(len(self.route)): 253 | route_single = self.route[i] 254 | v_type = self.v_type_dict[self.vehicle_order_list[i]] 255 | v_o_energy = self.v_o_energy_dict[v_type] 256 | for pt in route_single: 257 | service_time = self.d_service_time_dict[pt] 258 | cost_service += (v_o_energy * service_time) 259 | # waiting time ref costs 260 | for i in range(len(self.route)): 261 | route_single = self.route[i] 262 | v_type = self.v_type_dict[self.vehicle_order_list[i]] 263 | v_c_energy = self.v_c_energy_dict[v_type] 264 | 265 | for pt in route_single: 266 | wait_time = max(self.d_e_time_dict[pt] - self.w_time_dict[pt], 0) 267 | cost_waiting += v_c_energy * wait_time 268 | 269 | ref_cost = 3 * (cost_travel + cost_service + cost_waiting) 270 | else: 271 | ref_cost = 1000000 272 | 273 | return ref_cost 274 | 275 | ############################################################################### 276 | def Module_Cum_Time_Calculate(self): 277 | route_appended = self.route 278 | for i in range(len(route_appended)): 279 | route_appended[i].insert(0,0) 280 | 281 | for i in range(len(route_appended)): 282 | route_single = route_appended[i] 283 | 284 | for i in range(len(route_single)): 285 | if (route_single[i] == 0): 286 | continue 287 | else: 288 | prev_pt = route_single[i-1] 289 | current_pt = route_single[i] 290 | if (prev_pt == 0): 291 | travel_time_recent = self.Module_Two_Point_Time(prev_pt, current_pt) 292 | self.w_time_dict[current_pt] = travel_time_recent 293 | else: 294 | travel_time_recent = self.Module_Two_Point_Time(prev_pt, current_pt) 295 | service_time_previous = self.d_service_time_dict[prev_pt] 296 | waiting_time_previous = max(self.d_e_time_dict[prev_pt] - self.w_time_dict[prev_pt], 0) 297 | self.w_time_dict[current_pt] = self.w_time_dict[prev_pt] + waiting_time_previous + \ 298 | service_time_previous + travel_time_recent 299 | 300 | ############################################################################### 301 | def Module_Calculate_Cost(self, gene_sequence): 302 | 303 | self.Module_Initialization() 304 | # Assign modules 305 | self.Module_Assignment(gene_sequence) 306 | self.Module_Route_Planner() 307 | 308 | 309 | if (len(self.route) <= 13): 310 | self.Module_Cum_Time_Calculate() 311 | 312 | fixed_cost = self.Module_Fixed_Cost() 313 | fuel_cost = self.Module_Fuel_Cost() 314 | carbon_cost = self.Module_Carbon_Cost() 315 | pen_cost = self.Module_Penalty_Cost() 316 | damage_cost = self.Module_Damage_Cost() 317 | ref_cost = self.Module_Refrigeration_Cost() 318 | 319 | total_cost = fixed_cost + fuel_cost + carbon_cost + pen_cost + damage_cost + ref_cost 320 | return total_cost,fixed_cost,fuel_cost,carbon_cost,pen_cost,damage_cost,ref_cost 321 | 322 | else: 323 | return 6000000, 1000000, 1000000, 1000000, 1000000, 1000000, 1000000 324 | 325 | ############################################################################### 326 | # main part of the code 327 | # ======================================================== 328 | # ============ DEFINING MAIN FUNCTIONS (OUTSIDE Sim CLASS) ============ 329 | # ======================================================== 330 | ############################################################################### 331 | # initial decision variables 332 | sim = Simulation() 333 | decision_variables = [list(range(1,21)), list(range(1,14))] 334 | n_children = 40 335 | colors_dict = {1:'b', 2:'g', 3:'r'} 336 | shapes_list = ["o", "v", "s", "^", "<", "P"] 337 | def initialize(): 338 | pop_bag = [[[], []], [[], []], [[], []], [[], []], [[], []], 339 | [[], []], [[], []], [[], []], [[], []], [[], []], 340 | [[], []], [[], []], [[], []], [[], []], [[], []], 341 | [[], []], [[], []], [[], []], [[], []], [[], []], 342 | [[], []], [[], []], [[], []], [[], []], [[], []], 343 | [[], []], [[], []], [[], []], [[], []], [[], []], 344 | [[], []], [[], []], [[], []], [[], []], [[], []], 345 | [[], []], [[], []], [[], []], [[], []], [[], []]] 346 | # demand 347 | for i in range(n_children): 348 | for j in range(len(decision_variables)): 349 | rnd_sol = decision_variables[j].copy() 350 | random.shuffle(rnd_sol) 351 | pop_bag[i][j] = (rnd_sol) 352 | return np.array(pop_bag) 353 | 354 | ############################################################################### 355 | def fitness_function(solution): 356 | all_cost = sim.Module_Calculate_Cost(solution) 357 | total_cost = all_cost[0] 358 | return total_cost 359 | 360 | ############################################################################### 361 | def eval_fit_population(pop_bag): 362 | result = {} 363 | fit_vals_lst = [] 364 | solutions = [] 365 | for solution in pop_bag: 366 | fit_vals_lst.append(fitness_function(solution)) 367 | solutions.append(solution) 368 | result["fit_vals"] = fit_vals_lst 369 | min_wgh = [np.max(list(result["fit_vals"]))-i for i in list(result["fit_vals"])] 370 | result["fit_wgh"] = [val/sum(min_wgh) for val in min_wgh] 371 | result["solution"] = np.array(solutions) 372 | return result 373 | 374 | ############################################################################### 375 | def pickOne(pop_bag): 376 | fit_bag_evals = eval_fit_population(pop_bag) 377 | a=True 378 | while a: 379 | rnIndex = random.randint(0, len(pop_bag)-1) # length of pop_bag = 10 380 | rnPick = fit_bag_evals["fit_wgh"][rnIndex] 381 | r = random.random() # between 0 and 1 382 | if r <= rnPick: 383 | pickedSol = fit_bag_evals["solution"][rnIndex] 384 | a = False 385 | return pickedSol 386 | 387 | ############################################################################### 388 | def crossover(solA, solB): 389 | 390 | d_solA = solA[0] 391 | d_solB = solB[0] 392 | v_solA = solA[1] 393 | v_solB = solA[1] 394 | ####################### 395 | n1 = len(d_solA) 396 | d_child = [np.nan for i in range(n1)] 397 | d_num_els = np.ceil(n1*(random.randint(10,90)/100)) 398 | d_str_pnt = random.randint(0, n1-2) 399 | d_end_pnt = n1 if int(d_str_pnt+d_num_els) > n1 else int(d_str_pnt+d_num_els) 400 | d_blockA = list(d_solA[d_str_pnt:d_end_pnt]) 401 | d_child[d_str_pnt:d_end_pnt] = d_blockA 402 | for i in range(n1): 403 | if list(d_blockA).count(d_solB[i]) == 0: 404 | for j in range(n1): 405 | if np.isnan(d_child[j]): 406 | d_child[j] = d_solB[i] 407 | break 408 | ####################### 409 | n2 = len(v_solA) 410 | v_child = [np.nan for i in range(n2)] 411 | v_num_els = np.ceil(n2*(random.randint(10,90)/100)) 412 | v_str_pnt = random.randint(0, n2-2) 413 | v_end_pnt = n2 if int(v_str_pnt+v_num_els) > n2 else int(v_str_pnt+v_num_els) 414 | v_blockA = list(v_solA[v_str_pnt:v_end_pnt]) 415 | v_child[v_str_pnt:v_end_pnt] = v_blockA 416 | for i in range(n2): 417 | if list(v_blockA).count(v_solB[i]) == 0: 418 | for j in range(n2): 419 | if np.isnan(v_child[j]): 420 | v_child[j] = v_solB[i] 421 | break 422 | child = [d_child, v_child] 423 | return child 424 | 425 | ############################################################################### 426 | def mutation(sol): 427 | n1 = len(sol[0]) 428 | d_pos_1 = random.randint(0,n1-1) 429 | d_pos_2 = random.randint(0,n1-1) 430 | d_result = swap(sol[0], d_pos_1, d_pos_2) 431 | ####################### 432 | n2 = len(sol[1]) 433 | v_pos_1 = random.randint(0,n2-1) 434 | v_pos_2 = random.randint(0,n2-1) 435 | v_result = swap(sol[1], v_pos_1, v_pos_2) 436 | result = [d_result, v_result] 437 | return result 438 | 439 | ############################################################################### 440 | def swap(sol, posA, posB): 441 | d_result = sol.copy() 442 | elA = sol[posA] 443 | elB = sol[posB] 444 | d_result[posA] = elB 445 | d_result[posB] = elA 446 | return d_result 447 | 448 | ############################################################################### 449 | def Closed_Route(best_route_1): 450 | list_2 = [] 451 | for i in range(len(best_route_1)): 452 | single_route = best_route_1[i] 453 | single_route.insert(0,0) 454 | single_route.append(0) 455 | list_2.append(single_route) 456 | 457 | print (list_2) 458 | return (list_2) 459 | 460 | ############################################################################### 461 | def Plotter(best_route_1): 462 | k = 0 463 | for i in range(len(best_route_1)): 464 | single_route = best_route_1[i] 465 | for j in range(len(single_route)-1): 466 | cur_pt = single_route[j] 467 | nxt_pt = single_route[j+1] 468 | point1 = [sim.d_x_dict[cur_pt], sim.d_y_dict[cur_pt]] 469 | point2 = [sim.d_x_dict[nxt_pt], sim.d_y_dict[nxt_pt]] 470 | x_values = [point1[0], point2[0]] 471 | y_values = [point1[1], point2[1]] 472 | 473 | v_number = sim.vehicle_order_list[i] 474 | v_type = sim.v_type_dict[v_number] 475 | color_plot = colors_dict[v_type] 476 | plt.plot(x_values, y_values, color=color_plot, linestyle='dashed', marker=shapes_list[k%6]) 477 | 478 | k+=1 479 | 480 | ############################################################################### 481 | # ======================================================== 482 | # ============ START THE EVOLUTIONARY PROCESS ============ 483 | # ======================================================== 484 | 485 | # Create the initial population bag 486 | pop_bag = initialize() 487 | overall_best_fit = [] 488 | # Iterate over all generations 489 | for g in range(10): 490 | print ("generation = ", g) 491 | # Calculate the fitness of elements in population bag 492 | pop_bag_fit = eval_fit_population(pop_bag) 493 | 494 | # Best individual in the current population bag 495 | best_fit = np.min(pop_bag_fit["fit_vals"]) 496 | best_fit_index = pop_bag_fit["fit_vals"].index(best_fit) 497 | best_solution = pop_bag_fit["solution"][best_fit_index] 498 | 499 | # Check if we have a new best 500 | if g == 0: 501 | best_fit_global = best_fit 502 | best_solution_global = best_solution 503 | else: 504 | if best_fit <= best_fit_global: 505 | best_fit_global = best_fit 506 | best_solution_global = best_solution 507 | overall_best_fit.append(best_fit_global) 508 | 509 | print ("best cost = ", best_fit_global) 510 | # Create the new population bag 511 | new_pop_bag = [] 512 | 513 | for i in range(n_children): 514 | # Pick 2 parents from the bag 515 | pA = pickOne(pop_bag) 516 | pB = pickOne(pop_bag) 517 | new_element = pA 518 | 519 | # Crossover the parents 520 | if random.random() <= 0.87: 521 | new_element = crossover(pA, pB) 522 | # Mutate the child 523 | if random.random() <= 0.7: 524 | new_element = mutation(new_element) 525 | # Append the child to the bag 526 | new_pop_bag.append(new_element) 527 | 528 | # Set the new bag as the population bag 529 | pop_bag = np.array(new_pop_bag) 530 | 531 | # Best fitness and solution 532 | print(f"Best Fitness: {best_fit_global}") 533 | print(f"Best Solution: {best_solution_global}") 534 | 535 | # Post processing 536 | sim.Module_Assignment(best_solution_global) 537 | best_route = sim.Module_Best_Route_Planner(best_solution_global) 538 | print ("best route = ", best_route) 539 | best_route_1 = Closed_Route(best_route) 540 | print("best route = ", best_route_1) 541 | Plotter(best_route_1) 542 | 543 | print("--- %s seconds ---" % (time.time() - start_time)) 544 | --------------------------------------------------------------------------------