├── Images ├── more.png ├── van_0.png ├── van_1.png ├── van_2.png ├── AS_delta.png ├── AS_prob.png ├── cyclic_2.png ├── exchange.png ├── GENI_types.png ├── crossover.png ├── relocation.png ├── Type_II_GENI.png ├── Type_I_GENI.png ├── VRP_Overview.png ├── fisher_demo.png ├── insertion_ext.png ├── petal_demo_0.png ├── petal_demo_1.png ├── sweep_demo_0.png ├── sweep_demo_1.png ├── beasley_demo_0.png ├── beasley_demo_1.png ├── beasley_demo_2.png ├── cvrp_annealing.png ├── cyclic_transfer_demo.png └── Adaptative_memory_demo.png ├── Exact ├── __pycache__ │ └── class_info.cpython-36.pyc ├── README.md ├── demo_exact.py └── class_info.py ├── Heuristic ├── 2-Phase │ ├── __pycache__ │ │ ├── Petal.cpython-36.pyc │ │ └── Sweep.cpython-36.pyc │ ├── demo.py │ ├── README.md │ ├── Petal.py │ └── Sweep.py └── Constructive │ ├── __pycache__ │ ├── CW_parallel.cpython-36.pyc │ ├── CW_sequential.cpython-36.pyc │ └── Clarke_Wright.cpython-36.pyc │ ├── demo.py │ ├── CW_parallel.py │ ├── CW_sequential.py │ └── README.md ├── Meta-Heuristic ├── __pycache__ │ └── Ant_Colony.cpython-36.pyc ├── demo.py ├── Tabu.py ├── Simulated_Annealing.py ├── Ant_Colony.py └── README.md ├── Variants └── README.md ├── Common.md ├── README.md └── References.md /Images/more.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caiyangcy/VRP-Algorithms/HEAD/Images/more.png -------------------------------------------------------------------------------- /Images/van_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caiyangcy/VRP-Algorithms/HEAD/Images/van_0.png -------------------------------------------------------------------------------- /Images/van_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caiyangcy/VRP-Algorithms/HEAD/Images/van_1.png -------------------------------------------------------------------------------- /Images/van_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caiyangcy/VRP-Algorithms/HEAD/Images/van_2.png -------------------------------------------------------------------------------- /Images/AS_delta.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caiyangcy/VRP-Algorithms/HEAD/Images/AS_delta.png -------------------------------------------------------------------------------- /Images/AS_prob.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caiyangcy/VRP-Algorithms/HEAD/Images/AS_prob.png -------------------------------------------------------------------------------- /Images/cyclic_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caiyangcy/VRP-Algorithms/HEAD/Images/cyclic_2.png -------------------------------------------------------------------------------- /Images/exchange.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caiyangcy/VRP-Algorithms/HEAD/Images/exchange.png -------------------------------------------------------------------------------- /Images/GENI_types.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caiyangcy/VRP-Algorithms/HEAD/Images/GENI_types.png -------------------------------------------------------------------------------- /Images/crossover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caiyangcy/VRP-Algorithms/HEAD/Images/crossover.png -------------------------------------------------------------------------------- /Images/relocation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caiyangcy/VRP-Algorithms/HEAD/Images/relocation.png -------------------------------------------------------------------------------- /Images/Type_II_GENI.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caiyangcy/VRP-Algorithms/HEAD/Images/Type_II_GENI.png -------------------------------------------------------------------------------- /Images/Type_I_GENI.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caiyangcy/VRP-Algorithms/HEAD/Images/Type_I_GENI.png -------------------------------------------------------------------------------- /Images/VRP_Overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caiyangcy/VRP-Algorithms/HEAD/Images/VRP_Overview.png -------------------------------------------------------------------------------- /Images/fisher_demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caiyangcy/VRP-Algorithms/HEAD/Images/fisher_demo.png -------------------------------------------------------------------------------- /Images/insertion_ext.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caiyangcy/VRP-Algorithms/HEAD/Images/insertion_ext.png -------------------------------------------------------------------------------- /Images/petal_demo_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caiyangcy/VRP-Algorithms/HEAD/Images/petal_demo_0.png -------------------------------------------------------------------------------- /Images/petal_demo_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caiyangcy/VRP-Algorithms/HEAD/Images/petal_demo_1.png -------------------------------------------------------------------------------- /Images/sweep_demo_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caiyangcy/VRP-Algorithms/HEAD/Images/sweep_demo_0.png -------------------------------------------------------------------------------- /Images/sweep_demo_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caiyangcy/VRP-Algorithms/HEAD/Images/sweep_demo_1.png -------------------------------------------------------------------------------- /Images/beasley_demo_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caiyangcy/VRP-Algorithms/HEAD/Images/beasley_demo_0.png -------------------------------------------------------------------------------- /Images/beasley_demo_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caiyangcy/VRP-Algorithms/HEAD/Images/beasley_demo_1.png -------------------------------------------------------------------------------- /Images/beasley_demo_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caiyangcy/VRP-Algorithms/HEAD/Images/beasley_demo_2.png -------------------------------------------------------------------------------- /Images/cvrp_annealing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caiyangcy/VRP-Algorithms/HEAD/Images/cvrp_annealing.png -------------------------------------------------------------------------------- /Images/cyclic_transfer_demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caiyangcy/VRP-Algorithms/HEAD/Images/cyclic_transfer_demo.png -------------------------------------------------------------------------------- /Images/Adaptative_memory_demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caiyangcy/VRP-Algorithms/HEAD/Images/Adaptative_memory_demo.png -------------------------------------------------------------------------------- /Exact/__pycache__/class_info.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caiyangcy/VRP-Algorithms/HEAD/Exact/__pycache__/class_info.cpython-36.pyc -------------------------------------------------------------------------------- /Heuristic/2-Phase/__pycache__/Petal.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caiyangcy/VRP-Algorithms/HEAD/Heuristic/2-Phase/__pycache__/Petal.cpython-36.pyc -------------------------------------------------------------------------------- /Heuristic/2-Phase/__pycache__/Sweep.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caiyangcy/VRP-Algorithms/HEAD/Heuristic/2-Phase/__pycache__/Sweep.cpython-36.pyc -------------------------------------------------------------------------------- /Meta-Heuristic/__pycache__/Ant_Colony.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caiyangcy/VRP-Algorithms/HEAD/Meta-Heuristic/__pycache__/Ant_Colony.cpython-36.pyc -------------------------------------------------------------------------------- /Heuristic/Constructive/__pycache__/CW_parallel.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caiyangcy/VRP-Algorithms/HEAD/Heuristic/Constructive/__pycache__/CW_parallel.cpython-36.pyc -------------------------------------------------------------------------------- /Heuristic/Constructive/__pycache__/CW_sequential.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caiyangcy/VRP-Algorithms/HEAD/Heuristic/Constructive/__pycache__/CW_sequential.cpython-36.pyc -------------------------------------------------------------------------------- /Heuristic/Constructive/__pycache__/Clarke_Wright.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caiyangcy/VRP-Algorithms/HEAD/Heuristic/Constructive/__pycache__/Clarke_Wright.cpython-36.pyc -------------------------------------------------------------------------------- /Heuristic/2-Phase/demo.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Mon Dec 23 23:51:20 2019 4 | 5 | @author: Cai 6 | """ 7 | import Sweep 8 | import Petal 9 | import numpy as np 10 | 11 | def demo_Sweep(): 12 | depot = np.array([[0,0]]) 13 | customers_location = np.array([[1,1], [1,-1], [-1,1], [-1,-1]]) 14 | customers = [] 15 | 16 | label = 0 17 | for cl in customers_location: 18 | customers.append(Sweep.Customer(cl, label)) 19 | label += 1 20 | 21 | demand = np.array([1,2,3,4]) 22 | 23 | cvrp = Sweep.Sweep(depot, customers, demand) 24 | 25 | capacity = np.array([6]) 26 | num_vehicles = 1 27 | vehicle = Sweep.Vehicle(capacity, num_vehicles) 28 | 29 | cvrp.fit(vehicle) 30 | 31 | solution, distance = cvrp.solve() 32 | 33 | for idx in range(len(solution)): 34 | print('visiting order {} : {} with distance {}'.format(idx, solution[idx], distance[idx])) 35 | 36 | def demo_Petal(): 37 | depot = np.array([[0,0]]) 38 | customers = np.array([[1,1], [1,-1], [-1,1], [-1,-1]]) 39 | 40 | demand = np.array([1,2,3,4]) 41 | 42 | cvrp = Petal.Petal(depot, customers, demand) 43 | 44 | capacity = np.array([6]) 45 | num_vehicles = 1 46 | vehicle = Petal.Vehicle(capacity, num_vehicles) 47 | 48 | cvrp.fit(vehicle) 49 | 50 | cvrp.solve() 51 | 52 | 53 | if __name__ == "__main__": 54 | # demo_Sweep()s 55 | demo_Petal() -------------------------------------------------------------------------------- /Heuristic/Constructive/demo.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Implementation of Clarke Wright Savings Algorithm. 3 | No optimisation yet. 4 | CVRP 5 | ''' 6 | 7 | import numpy as np 8 | import matplotlib.pyplot as plt 9 | import CW_sequential as CWS 10 | import CW_parallel as CWP 11 | from matplotlib.animation import FuncAnimation 12 | from matplotlib import animation 13 | 14 | 15 | def demo_S(): 16 | depot = np.array([[0,0]]) 17 | customers = np.array([[1,1], [1,-1], [-1,1], [-1,-1]]) 18 | # customers = np.array([[1,1], [1,2], [-1,2]]) 19 | 20 | demand = np.array([1,2,3,4]) 21 | 22 | cvrp = CWS.Clark_Wright_Sequential(depot, customers, demand) 23 | 24 | capacity = np.array([6]) 25 | num_vehicles = 1 26 | vehicle = CWS.Vehicle(capacity, num_vehicles) 27 | 28 | cvrp.fit(vehicle) 29 | 30 | solution = cvrp.solve() 31 | 32 | for idx in range(len(solution)): 33 | print('visiting order {}: '.format(idx), solution[idx]) 34 | 35 | def demo_P(): 36 | depot = np.array([[0,0]]) 37 | customers_location = np.array([[1,1], [1,-1], [-1,1], [-1,-1]]) 38 | customers = [] 39 | 40 | for cl in customers_location: 41 | customers.append(CWP.Customer(cl)) 42 | 43 | demand = np.array([1,2,3,4]) 44 | 45 | cvrp = CWP.Clark_Wright_Parallel(depot, customers, demand, customers_location) 46 | 47 | capacity = np.array([6]) 48 | num_vehicles = 1 49 | vehicle = CWP.Vehicle(capacity, num_vehicles) 50 | 51 | cvrp.fit(vehicle) 52 | 53 | solution = cvrp.solve() 54 | 55 | for idx in range(len(solution)): 56 | print('visiting order {}: '.format(idx), solution[idx]) 57 | 58 | if __name__ == '__main__': 59 | 60 | # demo_S() 61 | demo_P() 62 | -------------------------------------------------------------------------------- /Exact/README.md: -------------------------------------------------------------------------------- 1 | Exact algorithms try all the possible solutions of the problem and then find out the best one. 2 | 3 | ## Branch and bound 4 | 5 | Branch and bound algorithm divides the problems into subproblems and solve each of them individually. Once a better solution is found, it replaces the current solution with the better one. 6 | 7 | If a branch is found to have no solutions then its discarded. 8 | 9 | ## Branch and cut 10 | 11 | Similar to branch and bound but with pruning. If we know the best feasible solution from a branch is no better than the current solution, then we prune the subproblem and continue to next one. 12 | 13 | ## Resoureces 14 | * **M.L.Fisher**. [Optimal Solution of Vehicle Routing Problems Using Minimum K-trees](http://users.mai.liu.se/torla64/MAI0127/Fisher1994.pdf), 1994. 15 | 16 | * **P. Toth, and D. Vigo**. [Models, relaxations and exact approaches for the capacitated vehicle routing problem](http://www-dimat.unipv.it/~gualandi/famo2conti/papers/routing_models.pdf). 2000. 17 | 18 | * **P. Toth, and D. Vigo**. [Branch-and-bound algorithms for the capacitated VRP](https://www.jstor.org/stable/171544?seq=1#metadata_info_tab_contents). 2001. 19 | 20 | * **U. Blasum, and W. Hochstattler**. [Application of the Branch and Cut Method to the Vehicle Routing Problem](https://pdfs.semanticscholar.org/f2b6/7aab794949152bc73d3f606e4ad36f1d6390.pdf). 2002. 21 | 22 | * **R. Fukasawa1, J. Lysgaard2, M. Poggi de Aragao, M. Reis3, E. Uchoa4, and R. F. Werneck**. [Robust Branch-and-Cut-and-Price for the Capacitated Vehicle Routing Problem](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.90.394&rep=rep1&type=pdf) as well as this [slides](https://pdfs.semanticscholar.org/6b1a/e2c40e0e9eef4c367416053b78d2d7ebaf0d.pdf). 23 | 24 | * **AN Letchford, J Lysgaard and RW Eglese**. [A new branch-and-cut algorithm for the capacitated vehicle routing problem](https://www.lancaster.ac.uk/staff/letchfoa/articles/2007-covrp.pdf) and 25 | [Slides](https://symposia.cirrelt.ca/system/documents/000/000/112/Lysgaard_original.pdf?1441306917). 26 | -------------------------------------------------------------------------------- /Exact/demo_exact.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Implementation of some exact algorithms. 3 | No optimisation yet. 4 | CVRP 5 | ''' 6 | 7 | import numpy as np 8 | import matplotlib.pyplot as plt 9 | import class_info as Exact 10 | from matplotlib.animation import FuncAnimation 11 | from matplotlib import animation 12 | 13 | 14 | 15 | def plot_solutions(depot, customers): 16 | plt.scatter(depot[:,0], depot[:,1], s=60, c='r', label='depot') 17 | plt.scatter(customers[:,0], customers[:,1], s=50, label='customers') 18 | 19 | starting_edge_x = np.concatenate((depot[:,0], np.array([customers[0, 0]])), axis=0) 20 | starting_edge_y = np.concatenate((depot[:,1], np.array([customers[0, 1]])), axis=0) 21 | plt.plot(starting_edge_x, starting_edge_y, c='#FF5733') 22 | 23 | for idx in range(1, len(customers)): 24 | prev_customer = customers[idx-1] 25 | curr_customer = customers[idx] 26 | 27 | x_coord = np.concatenate((np.array([prev_customer[0]]), np.array([curr_customer[0]])), axis=0) 28 | y_coord = np.concatenate((np.array([prev_customer[1]]), np.array([curr_customer[1]])), axis=0) 29 | 30 | plt.plot(x_coord, y_coord, c='#FF5733') 31 | 32 | ending_edge_x = np.concatenate((depot[:,0], np.array([customers[-1, 0]])), axis=0) 33 | ending_edge_y = np.concatenate((depot[:,1], np.array([customers[-1, 1]])), axis=0) 34 | plt.plot(ending_edge_x, ending_edge_y, c='#FF5733') 35 | plt.legend() 36 | plt.show() 37 | 38 | 39 | def demo(ps=True): 40 | 41 | 42 | depot = np.array([[0,0]]) 43 | # customers = np.array([[1,1], [1,-1], [-1,1], [-1,-1]]) 44 | customers = np.array([[1,1], [1,2], [-1,2]]) 45 | demand = np.array([1,2,3,4]) 46 | 47 | cvrp = Exact.CVRP_exact(depot, customers, demand) 48 | 49 | capacity = np.array([15]) 50 | num_vehicles = 1 51 | vehicle = Exact.Vehicle(capacity, num_vehicles) 52 | 53 | cvrp.fit(vehicle) 54 | 55 | customer_order, solution, shortest_distance = cvrp.solve() 56 | 57 | print('\ncustomer visiting order:\n ', customer_order) 58 | print('\nsolution: ', solution) 59 | print('\nshortest_distance: ', shortest_distance) 60 | 61 | if ps: 62 | plot_solutions(depot, customer_order) 63 | 64 | if __name__ == '__main__': 65 | 66 | demo() 67 | -------------------------------------------------------------------------------- /Meta-Heuristic/demo.py: -------------------------------------------------------------------------------- 1 | # https://github.com/pjmattingly/ant-colony-optimization 2 | 3 | import Ant_Colony 4 | import numpy as np 5 | 6 | 7 | def demo_Ant(): 8 | # customer_nodes = {0: (1, 1), 1: (1, -1), 2: (-1, 1), 3: (-1, -1)} 9 | # demand = np.array([1,2,3,4]) 10 | 11 | customer_nodes = {0: (0, 0), 1: (1, 1), 2: (1, -1), 3: (-1, 1), 4: (-1, -1)} 12 | demand = np.array([0,1,2,3,4]) 13 | 14 | capacity = 4 15 | def distance(a, b): 16 | if a == b: 17 | return np.inf 18 | node_a = customer_nodes[a] 19 | node_b = customer_nodes[b] 20 | return np.sqrt(np.abs(node_a[0]-node_b[0])**2 + np.abs(node_a[1]-node_b[1])**2) 21 | 22 | 23 | colony = Ant_Colony.Ant_Colony(nodes=customer_nodes, distance_callback=distance, capacity=capacity, demand=demand, start=0, 24 | alpha=5, beta=5, gamma=0, ant_count=10, pheromone_evaporation_coefficient=.25, iterations=10) 25 | 26 | result = colony.solve()+[0] 27 | 28 | print('result: \n', result) 29 | 30 | remaining_customers = {0: (0, 0)} 31 | new_demand = [0] 32 | 33 | for i in customer_nodes.keys(): 34 | if i not in result: 35 | remaining_customers[i] = customer_nodes[i] 36 | new_demand.append(demand[i]) 37 | 38 | new_demand = np.array(new_demand) 39 | 40 | while len(remaining_customers.keys()) != 1: 41 | colony = Ant_Colony.Ant_Colony(nodes=remaining_customers, distance_callback=distance, capacity=capacity, demand=new_demand, start=0, 42 | alpha=5, beta=5, gamma=0, ant_count=10, pheromone_evaporation_coefficient=.25, iterations=10) 43 | 44 | result = colony.solve()+[0] 45 | 46 | print('result: \n', result) 47 | 48 | remaining_customers_2 = {0: (0, 0)} 49 | new_demand = [0] 50 | 51 | for i in remaining_customers.keys(): 52 | if i not in result: 53 | remaining_customers_2[i] = remaining_customers[i] 54 | new_demand.append(demand[i]) 55 | new_demand = np.array(new_demand) 56 | remaining_customers = remaining_customers_2.copy() 57 | 58 | def demo_Tabu(): 59 | ''' 60 | This demo is ... cheating 61 | Assume the sum of all customers' demand is no bigger than the capacity of the vehicle 62 | ''' 63 | None 64 | 65 | def demo_SA(): 66 | None 67 | 68 | if __name__ == "__main__": 69 | demo_Ant() 70 | -------------------------------------------------------------------------------- /Variants/README.md: -------------------------------------------------------------------------------- 1 | # Variants Intro 2 | 3 | This is a short intro on difference types of variants of VRP. This may be different from the overview image. This repo only focus on CVRP so far (may added others). 4 | 5 | ## Capacitated VRP(CVRP) 6 | 7 | The vehicle has a limited capacity. There are some other versions of CVRP, which also set up a limit on the maximum distance for each route. 8 | 9 | ## VRP with Time Window(VRPTW) 10 | 11 | Each customer is only available in a specific duration. Meanwhile there is also a restriction on the time intervel of depot which is called the scheduling horizon and the delivery must start and end within this time interval. 12 | 13 | ## VRP with Pick-Up and Delivering(VRPPD) 14 | There is a chance that some customers return the commodities. 15 | 16 | It’s needed to take into account that the goods that customers return to the deliver vehicle must fit into it. This restriction make the planning problem more difficult and can lead to bad utilization of the vehicles capacities, increased travel distances or a need for more vehicles. 17 | 18 | Another usual simplification is to consider that every vehicle must deliver all the commodities before picking up any goods. 19 | 20 | ## VRP with Backhauls(VRPB) 21 | 22 | Customers can demand or return some commodities. 23 | 24 | VRPB is similar to VRPPD with the restriction that in the case of VRPB all deliveries for each route must be completed before any pickups are made. 25 | 26 | The critical assumption in that all deliveries must be made on each route before any pickups can be made. This arises from the fact that the vehicles are rear-loaded, and rearrangement of the loads on the tracks at the delivery points is not deemed economical or feasible. The quantities to be delivered and picked-up are fixed and known in advance. 27 | 28 | ## Stochastic VRP(SVRP) 29 | 30 | one or several components of the problem are random. There three major cases: random customer(customer is present or not), random time window(random service time) and random demand(demands of customers are random). 31 | 32 | ## Multiple Depot VRP(MDVRP) 33 | 34 | A MDVRP requires the assignment of customers to depots. A fleet of vehicles is based at each depot. Each vehicle originate from one depot, service the customers assigned to that depot, and return to the same depot. Each cluster is solved independently. 35 | 36 | ## Split Delivery VRP(SDVRP) 37 | 38 | This same customer can be delivered by multiple times This relaxation is very important if the sizes of the customer orders are as big as the capacity of a vehicle. Note that currently the codings are all based on Non-split delivery. 39 | -------------------------------------------------------------------------------- /Exact/class_info.py: -------------------------------------------------------------------------------- 1 | # Class info 2 | 3 | ''' 4 | Implementation of some exact algorithms. 5 | No optimisation yet. 6 | CVRP 7 | ''' 8 | import time 9 | from scipy.spatial import distance_matrix 10 | import itertools 11 | import numpy as np 12 | 13 | class CVRP_exact: 14 | # depot and customers should be a 2d array 15 | depot, customers, demand = 0, 0, 0 16 | split_delivery = False 17 | vehicle = None 18 | 19 | def __init__(self, depot, customers, demand): 20 | self.depot = depot 21 | self.customers = customers 22 | self.demand = demand 23 | # self.split_delivery = split 24 | 25 | def fit(self, v): 26 | ''' 27 | v: The vehicle used to solve the current delivery. It contains info on numbers and capacities. 28 | ''' 29 | self.vehicle = v 30 | 31 | if self.vehicle.capacity < self.demand.max() and not self.split_delivery: 32 | raise Exception("The capacity of vehicle is smaller than the maximum demand") 33 | self.vehicle = None 34 | 35 | 36 | 37 | def solve(self): 38 | ''' 39 | solver = branch and bound(bnb) or branch and cut(bnc) 40 | ''' 41 | if self.vehicle == None: 42 | raise Exception("No vehicle info found") 43 | 44 | print('Start solving') 45 | 46 | start_time = time.time() 47 | 48 | depot, customers = self.depot, self.customers 49 | 50 | data = np.concatenate((depot, customers), axis=0) 51 | dm = distance_matrix(data, data) 52 | solution = [] 53 | best_distance = np.inf 54 | 55 | choices = list(itertools.permutations(np.arange(1, customers.shape[0]+1, 1))) 56 | choices = choices[:len(choices)//2] 57 | for choice in choices: 58 | 59 | total_distance = dm[0, choice[0]] # from depot to first customer 60 | 61 | for idx in range(1, len(choice)): 62 | total_distance += dm[choice[idx-1], choice[idx]] 63 | total_distance += dm[choice[-1], 0] # from last customer to depot 64 | if total_distance < best_distance: 65 | best_distance = total_distance 66 | solution = choice 67 | 68 | end_time = time.time() 69 | print('\nFinished solving, with total time %s mins ' % ((end_time - start_time)/60)) 70 | 71 | solution = np.array(solution) 72 | print('solution: ', solution) 73 | return customers[solution-1], solution, best_distance 74 | 75 | class Vehicle: 76 | ''' 77 | current number of vehicles is limited to 1 78 | ''' 79 | capacity, num = 0, 0 80 | 81 | def __init__(self, capacity, num=1): 82 | assert capacity.min() > 0 and num > 0 and num%1 == 0, "invalid vehicle info" 83 | self.capacity = capacity 84 | self.num = num 85 | -------------------------------------------------------------------------------- /Heuristic/2-Phase/README.md: -------------------------------------------------------------------------------- 1 | # Cluster-First, Route-Second Algorithms 2 | 3 | ## Fisher and Jakimar 4 | 5 | This algorithm refers to solve a *GA(generalized assignment)* problem. 6 | 7 | *Step 1*: Seed selection. Select a seed ![](https://latex.codecogs.com/gif.latex?j_k) in V for each cluster k=1,..,K. 8 | 9 | *Step 2*: Allocation of customers to seed. Compute the cost ![](https://latex.codecogs.com/gif.latex?c_{ik}) of allocating customer *i* to *k* as the cost of inserting *i* in the route ![](https://latex.codecogs.com/gif.latex?0-j_k-0). ![](https://latex.codecogs.com/gif.latex?c_{ik}=min(c_{0i}+c_{ij_k}+c_{j_k0},&space;c_{0j_k}+c_{j_ki}+c_{i0})-&space;(c_{0j_k}+c_{j_k0})) 10 | 11 | *Step 3*: Generailzed assignment. Sovle a GA problem with costs ![](https://latex.codecogs.com/gif.latex?c_{ik}), weights for customer ![](https://latex.codecogs.com/gif.latex?d_i), and vehicle capacity *Q*. 12 | 13 | *Step 4*: TSP solution. Solve a TSP for each cluster found. 14 | 15 | The following is taken from page 69 of the [slides](http://www.discovery.dist.unige.it/didattica/LS/VRP.pdf) by **Massimo Paolucci**: 16 | 17 | ![sweep_0](https://github.com/4342315yc/VRP-Algorithms/blob/master/Images/fisher_demo.png) 18 | 19 | The authors propose a geometric method based on the partition of the plane intomconesaccording to the customer weights. The seed points are dummy customers located along therays bisecting the cones. 20 | 21 | ## The Sweep Algorithm 22 | 23 | Imagine a ray centered at the depot. By rotating the ray, the customers can be divided into multiple clusters. By performing a TSP algorithm on each cluster we can form a solution in the end. 24 | 25 | The following is two pictures taken from page 68 of the [slides](http://www.discovery.dist.unige.it/didattica/LS/VRP.pdf) by **Massimo Paolucci**. 26 | 27 | ![sweep_0](https://github.com/4342315yc/VRP-Algorithms/blob/master/Images/sweep_demo_0.png) 28 | 29 | ![sweep_1](https://github.com/4342315yc/VRP-Algorithms/blob/master/Images/sweep_demo_1.png) 30 | 31 | ## The Petal Algorithm 32 | 33 | **Massimo Paolucci**'s slides really did a good job on explaining the algorithms. So I'm going to refer to his slides here again. 34 | 35 | ![petal_0](https://github.com/4342315yc/VRP-Algorithms/blob/master/Images/petal_demo_0.png) 36 | 37 | ![petal_1](https://github.com/4342315yc/VRP-Algorithms/blob/master/Images/petal_demo_1.png) 38 | 39 | ## Tailard 40 | 41 | ## Location based heuristic 42 | 43 | # Route-First, Cluster-Second Algorithms 44 | 45 | ## Beasley's Algorithm 46 | 47 | Phase 1: Routing 48 | 49 | By relaxing the constraint on capacity(infinite capacity), the problem is converted to a TSP. 50 | 51 | Phase 2: Clustering 52 | 53 | Cut the TSP solutions into sub-routes that satisfy the capacity constraints 54 | 55 | Here, again, an example taken from the slides. 56 | 57 | ![sweep_0](https://github.com/4342315yc/VRP-Algorithms/blob/master/Images/beasley_demo_0.png) 58 | 59 | ![sweep_1](https://github.com/4342315yc/VRP-Algorithms/blob/master/Images/beasley_demo_1.png) 60 | 61 | ![sweep_2](https://github.com/4342315yc/VRP-Algorithms/blob/master/Images/beasley_demo_2.png) 62 | 63 | 64 | -------------------------------------------------------------------------------- /Heuristic/2-Phase/Petal.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import time 3 | from scipy.optimize import linprog 4 | 5 | class Vehicle: 6 | ''' 7 | current number of vehicles is limited to 1 8 | ''' 9 | capacity, num = 0, 0 10 | 11 | def __init__(self, capacity, num=1): 12 | assert capacity.min() > 0 and num > 0 and num%1 == 0, "invalid vehicle info" 13 | self.capacity = capacity 14 | self.num = num 15 | 16 | class Petal: 17 | 18 | depot, customers, demand = 0, 0, 0 19 | split_delivery = False 20 | vehicle = None 21 | ASSIGN_MATRIX = None 22 | COST_MATRIX = None 23 | 24 | def __init__(self, depot, customers, demand): 25 | self.depot = depot 26 | self.customers = customers 27 | self.demand = demand 28 | 29 | def fit(self, v): 30 | self.vehicle = v 31 | 32 | if self.vehicle.capacity < self.demand.max() and not self.split_delivery: 33 | raise Exception("The capacity of vehicle is smaller than the maximum demand") 34 | self.vehicle = None 35 | 36 | def _assign_matrix(self, starting_plan): 37 | init_start = starting_plan 38 | curr_demand = self.demand[starting_plan] 39 | 40 | while curr_demand <= self.vehicle.capacity and starting_plan < self.demand.shape[0]: 41 | if starting_plan < self.demand.shape[0] - 1: 42 | starting_plan += 1 43 | curr_demand += self.demand[starting_plan] 44 | else: 45 | break 46 | if curr_demand > self.vehicle.capacity: # The reason for the loop to stop is overcapacity 47 | curr_demand -= self.demand[starting_plan] 48 | starting_plan -= 1 49 | 50 | work_plans = np.zeros((self.customers.shape[0], starting_plan-init_start+1)) 51 | work_plan_cost = np.zeros((starting_plan-init_start+1, )) 52 | 53 | for i in range(work_plans.shape[1]): 54 | work_plans[:,i][init_start:init_start+i+1] = 1 55 | work_plan_cost[i] = self.demand[init_start:init_start+i+1].sum() 56 | 57 | return work_plans, work_plan_cost 58 | 59 | def _create_AM(self): # Assignment matrix creation 60 | assign_init, cost_init = self._assign_matrix(0) 61 | for i in range(1, self.demand.shape[0]): 62 | plan, cost = self._assign_matrix(i) 63 | assign_init = np.concatenate((assign_init, plan), axis=1) 64 | cost_init = np.concatenate((cost_init, cost)) 65 | 66 | print('\nAssignment matrix:\n ', assign_init) 67 | print('\nCost Array:\n ', cost_init) 68 | self.ASSIGN_MATRIX = assign_init 69 | self.COST_MATRIX = cost_init 70 | 71 | def _SSP_solver(self): 72 | ''' 73 | This function is simply based on scipy 74 | https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.linprog.html 75 | ''' 76 | c = self.COST_MATRIX.reshape(-1, ) 77 | A_eq = self.ASSIGN_MATRIX 78 | b_eq = np.ones((A_eq.shape[0], )) 79 | 80 | bounds = (0, 1) 81 | 82 | res = linprog(c, A_eq=A_eq, b_eq=b_eq, bounds=bounds, method='revised simplex') 83 | print('\nresult: ', res) 84 | return res 85 | 86 | def solve(self): 87 | 88 | start_time = time.time() 89 | self._create_AM() 90 | _ = self._SSP_solver() 91 | 92 | end_time = time.time() 93 | print('\nFinished solving, with total time %s mins \n' % ((end_time - start_time)/60)) 94 | 95 | # return res.x -------------------------------------------------------------------------------- /Meta-Heuristic/Tabu.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import time 3 | import random 4 | 5 | class Tabu: 6 | def __init__(self, customer, demand, tabu_tenure, tabu_size): 7 | ''' 8 | customer is a dictionary 9 | ''' 10 | self.customer = customer 11 | self.demand = demand 12 | self.TABU_TENURE = tabu_tenure 13 | self.TABU_SIZE = tabu_size 14 | self.s0 = self._generate_init_solution() 15 | 16 | def _euclidean_dist(self, a, b): 17 | if a == b: 18 | return np.inf 19 | node_a = self.customer[a] 20 | node_b = self.customer[b] 21 | return np.sqrt(np.abs(node_a[0]-node_b[0])**2 + np.abs(node_a[1]-node_b[1])**2) 22 | 23 | def _generate_init_solution(self): 24 | ''' 25 | Generating the initial solution randomly 26 | ''' 27 | customers = self.customer.keys() 28 | init_solution = random.shuffle(customers) 29 | if init_solution[0] != 0: 30 | for i in range(len(init_solution)): 31 | if init_solution[i] == 0: 32 | init_solution[i] = init_solution[0] 33 | init_solution[0] = 0 34 | init_solution += [0] 35 | 36 | return init_solution 37 | 38 | def _two_opt_swap(self, candidate_solution, i, k): 39 | 40 | before = candidate_solution[:i] 41 | after = candidate_solution[k+1:] 42 | new_solution = before + candidate_solution[k:i-1:-1] + after 43 | 44 | return new_solution 45 | 46 | def two_opt(self, curr_solution): 47 | ''' 48 | Here the current solution is fixed and used to find a good solution 49 | There might be implementation like change the current one to the best one found 50 | and use the best one to continue the search 51 | ''' 52 | best_solution = curr_solution 53 | best_distance = self._evaluate(curr_solution) 54 | eligible_nodes_num = len(curr_solution) 55 | for i in range(1, eligible_nodes_num-1): 56 | for k in range(i+2, eligible_nodes_num): 57 | swapped_solution = self._two_opt_swap(curr_solution, i, k) 58 | swapped_distance = self._evaluate(swapped_solution) 59 | if swapped_distance < best_distance: 60 | best_distance = swapped_distance 61 | best_solution = swapped_solution 62 | return best_solution, best_distance 63 | 64 | def _evaluate(self, solution): 65 | sum_distance = 0 66 | for idx in range(1, len(solution)): 67 | sum_distance += self._euclidean_dist(solution[idx], solution[idx-1]) 68 | return sum_distance 69 | 70 | def tabu_search(self): 71 | start_time = time.time() 72 | 73 | stop = False 74 | best_solution_kept = 0 75 | best_solution = self.s0 76 | best_candidate = self.s0 77 | tabu_list = [] 78 | while not stop: 79 | candidate_solution, candidate_distance = self.two_opt(best_candidate) 80 | 81 | if candidate_distance < self._evaluate(best_candidate) and candidate_solution not in tabu_list: 82 | best_candidate = candidate_solution 83 | 84 | if candidate_distance < self._evaluate(best_solution): 85 | best_solution = candidate_solution 86 | best_solution_kept = 0 87 | 88 | best_solution_kept += 1 89 | 90 | tabu_list.append(candidate_solution) 91 | 92 | if len(tabu_list) > self.TABU_SIZE: 93 | tabu_list.pop(0) 94 | 95 | if best_solution_kept == self.TABU_TENURE: 96 | stop = True 97 | 98 | end_time = time.time() 99 | print('\nFinished solving, with total time %s mins \n' % ((end_time - start_time)/60)) 100 | return best_solution -------------------------------------------------------------------------------- /Common.md: -------------------------------------------------------------------------------- 1 | # Common Terminologies and Algorithms 2 | 3 | Here is a list of collection of terminologies and algorithms which are common seen in those VRP paper for quick check. 4 | 5 | ## Intensification/Diversification 6 | 7 | **diversification** = **exploration** (found a new best solutions in the population). 8 | 9 | **intensification** = **exploitation** (use the latest best solution to find a new oneslike metaheuristics who are guided by the best solution). 10 | 11 | ## Generalized Insertion (GENI) 12 | 13 | Paper: [A GENERALIZED INSERTION HEURISTIC FOR THE TRAVELING SALESMAN PROBLEM WITH TIME WINDOWS](https://pubsonline.informs.org/doi/pdf/10.1287/opre.46.3.330). MICHEL GENDREAU, ALAIN HERTZ, GILBERT LAPORTE and MIHNEA STAN. 14 | 15 | Paper: [New Insertion and Postoptimization Procedures for the Traveling Salesman Problem (https://www.researchgate.net/publication/221704722_New_Insertion_and_Postoptimization_Procedures_for_the_Traveling_Salesman_Problem) 16 | 17 | GENI is a technique used for for inter-route local search. It includes two types of insertion. The demos of Type I insertion and Type II insertion are as follows(taken from the two paper above). 18 | 19 | ![](https://github.com/4342315yc/VRP-Algorithms/blob/master/Images/GENI_types.png) 20 | 21 | ![](https://github.com/4342315yc/VRP-Algorithms/blob/master/Images/Type_I_GENI.png) 22 | ![](https://github.com/4342315yc/VRP-Algorithms/blob/master/Images/Type_II_GENI.png) 23 | 24 | Refer to the second paper for the details of the GENI algorithm. 25 | 26 | ## Unstringing and Stringing (US) 27 | 28 | US is post-optimization algorithm which consists of removing a vertex from a feasible solution and reinserting it back. Stringing procedure is similar to GENI and Unstringing is just reverse. The demos of Type I Unstringing and Type II Unstringing are as follow (taken from the second paper): 29 | 30 | Refer to the second paper for the detail of the US algorithm. 31 | 32 | ## Nearest Insertion 33 | 34 | Wikipedia provides the algorithm for NN insertion. The basic idea is always pick up the nearest uninserted point to the current route(tp any node the current route) and insert into the route. Repeat until all points are inserted. 35 | 36 | Also refer to this [post](https://cs.stackexchange.com/a/88935). 37 | 38 | ## Large Neighbourhood Search (LNS) 39 | 40 | Paper: [Using Constraint Programming and Local Search Methods to Solve Vehicle Routing Problems](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.67.8526&rep=rep1&type=pdf). Paul Shaw. April 1998. 41 | 42 | Taken from the paper: 43 | 44 | *LNS makes moves like local search, but uses a tree-based search with constraint propagation to evaluate the cost and legality of the move. As a “heavyweight” technique such as constraint programming is used to evaluate the move, many less moves per second can be evaluated than is normally the case for local search. However, these moves can be more powerful, moving the search further at each step. When such far reaching moves are possible there are normally many more of them available than would be the case if the moves were very localized: hence the name Large Neighbourhood Search.* 45 | 46 | ## Limited Discrepancy Search (LDS) 47 | 48 | Paper: [Limited Discrepancy Search](https://pdfs.semanticscholar.org/efa5/6b710ff3c6d8b2666971d07c311eeb6c5b40.pdf?_ga=2.176917669.1045710749.1577795756-1357849254.1576571462). Willia m D. Harvey and Matthew L. Ginsberg. 49 | 50 | ## λ-interchange 51 | 52 | Inter-route. It's defined by Osman (Metastrategy simulated annealing and tabu search for combinatorial optimization problems). The basic idea is swap the two subsets of two different route, whose size is bouded by λ. 53 | 54 | ## k-opt 55 | 56 | Intra-route. Swap k non-adjacent edges. 2-opt and 3-opt are the mostly common one. Possible changes: O(n^k) 57 | 58 | ## Or opt 59 | 60 | Inter-route. Relocate 1, 2, or 3 consecutive vertices. Possisble changes for 2 consecutive: O(n^2). 61 | 62 | ## Farthest Inesrtion 63 | 64 | Refer to this [website](https://users.cs.cf.ac.uk/C.L.Mumford/howard/FarthestInsertion.html) 65 | 66 | # First Improving (FI) 67 | 68 | Accept the first improved solution. 69 | 70 | # Best Improving (BI) 71 | 72 | Accept the best improved solution. 73 | -------------------------------------------------------------------------------- /Meta-Heuristic/Simulated_Annealing.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import time 3 | import random 4 | 5 | class SA: 6 | 7 | def __init__(self, customer, demand, init_temperature=5000, reduction_multiplier=0.99, iteration_multiplier=1.05, update_gap=5, max_time): 8 | self.CUSTOMER = customer 9 | self.DEMAND = demand 10 | self.T = init_temperature 11 | self.alpha = reduction_multiplier 12 | self.beta = iteration_multiplier 13 | self.M0 = update_gap 14 | self.S0 = self._generate_init_solution() 15 | self.MAX_TIME = max_time 16 | 17 | def _generate_init_solution(self): 18 | ''' 19 | Generating the initial solution randomly 20 | ''' 21 | customers = self.CUSTOMER.keys() 22 | init_solution = random.shuffle(customers) 23 | if init_solution[0] != 0: 24 | for i in range(len(init_solution)): 25 | if init_solution[i] == 0: 26 | init_solution[i] = init_solution[0] 27 | init_solution[0] = 0 28 | init_solution += [0] 29 | 30 | return init_solution 31 | 32 | def _euclidean_dist(self, a, b): 33 | if a == b: 34 | return np.inf 35 | node_a = self.customer[a] 36 | node_b = self.customer[b] 37 | return np.sqrt(np.abs(node_a[0]-node_b[0])**2 + np.abs(node_a[1]-node_b[1])**2) 38 | 39 | def _evaluate(self, solution): 40 | sum_distance = 0 41 | for idx in range(1, len(solution)): 42 | sum_distance += self._euclidean_dist(solution[idx], solution[idx-1]) 43 | return sum_distance 44 | 45 | def _two_opt_swap(self, candidate_solution, i, k): 46 | 47 | before = candidate_solution[:i] 48 | after = candidate_solution[k+1:] 49 | new_solution = before + candidate_solution[k:i-1:-1] + after 50 | 51 | return new_solution 52 | 53 | def _two_opt(self, curr_solution): 54 | ''' 55 | Here the current solution is fixed and used to find a good solution 56 | There might be implementation like change the current one to the best one found 57 | and use the best one to continue the search 58 | ''' 59 | best_solution = curr_solution 60 | best_distance = self._evaluate(curr_solution) 61 | eligible_nodes_num = len(curr_solution) 62 | for i in range(1, eligible_nodes_num-1): 63 | for k in range(i+2, eligible_nodes_num): 64 | swapped_solution = self._two_opt_swap(curr_solution, i, k) 65 | swapped_distance = self._evaluate(swapped_solution) 66 | if swapped_distance < best_distance: 67 | best_distance = swapped_distance 68 | best_solution = swapped_solution 69 | return best_solution, best_distance 70 | 71 | def SA(self): 72 | alpha = self.alpha 73 | beta = self.beta 74 | M0 = self.M0 75 | T = self.T 76 | current_solution = self.S0 77 | current_cost = self._evaluate(current_solution) 78 | best_solution = self.S0 79 | best_cost = current_cost 80 | time = 0 81 | max_time = self.MAX_TIME # Haven't figured out what this variable is 82 | 83 | while time > max_time and T > 0.001: 84 | M = M0 85 | while M >= 0: 86 | new_solution = self._two_opt(current_solution) 87 | new_cost = self._evaluate(new_solution) 88 | delta_cost = new_cost - current_cost 89 | if delta_cost < 0: 90 | current_solution = new_solution 91 | current_cost = new_cost 92 | if new_cost < best_cost: 93 | best_solution = current_solution 94 | best_cost = current_cost 95 | else: 96 | random = np.random.random_sample() # Boltzmann 97 | if random < (np.e)**(-delta_cost/T): 98 | current_solution = new_solution 99 | current_cost = new_cost 100 | M -= 1 101 | 102 | time += M0 103 | T *= alpha 104 | M0 *= beta 105 | 106 | return best_solution 107 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | A collection of algotihms, links and paper and (naive) implementation 2 | 3 | # VRP Algorithms 4 | 5 | A collection of VRP algorithms. Refer to [this page](https://github.com/4342315yc/VRP-Algorithms/blob/master/References.md) for (all?) references used in this repo. 6 | 7 | The following is the overview algorithms based on [NEO](http://neo.lcc.uma.es/vrp/solution-methods/). Note that the follwing image is just a simplified version of the overview of VRP family. The actual variants and algorithms are actually more than the following. 8 | 9 | ![Overview](https://github.com/4342315yc/VRP-Algorithms/blob/master/Images/VRP_Overview.png) 10 | 11 | ## Variants 12 | * [CVRP](https://github.com/4342315yc/VRP-Algorithms/blob/master/Variants/README.md#capacitated-vrpcvrp) 13 | * [VRPTW](https://github.com/4342315yc/VRP-Algorithms/blob/master/Variants/README.md#vrp-with-time-windowvrptw) 14 | * [SVRP](https://github.com/4342315yc/VRP-Algorithms/blob/master/Variants/README.md#stochastic-vrpsvrp) 15 | * [VRPPPD](https://github.com/4342315yc/VRP-Algorithms/blob/master/Variants/README.md#vrp-with-pick-up-and-deliveringvrppd) 16 | * [VRPB](https://github.com/4342315yc/VRP-Algorithms/blob/master/Variants/README.md#vrp-with-backhaulsvrpb) 17 | * [SDVRP](https://github.com/4342315yc/VRP-Algorithms/blob/master/Variants/README.md#stochastic-vrpsvrp) 18 | * [MDVRP](https://github.com/4342315yc/VRP-Algorithms/blob/master/Variants/README.md#multiple-depot-vrpmdvrp) 19 | 20 | ## Exact Algorithms 21 | * [Branch and bound](https://github.com/4342315yc/VRP-Algorithms/tree/master/Exact) 22 | * [Branch and cut](https://github.com/4342315yc/VRP-Algorithms/tree/master/Exact) 23 | 24 | ## Heuristic Algorithms 25 | 26 | Note that the following hyperlinks in sub-sections may not work 27 | 28 | * **[Constructive heuristic](https://github.com/4342315yc/VRP-Algorithms/tree/master/Heuristic/Constructive)** 29 | * [Clarke and Wright savings algorithm](https://github.com/4342315yc/VRP-Algorithms/tree/master/Heuristic/Constructive/#clarke-and-wright-savings-algorithm) 30 | * [Matching Based](https://github.com/4342315yc/VRP-Algorithms/tree/master/Heuristic/Constructive/#matching-based) 31 | * [Multi-route Improvement Heuristic](https://github.com/4342315yc/VRP-Algorithms/tree/master/Heuristic/Constructive/#multi-route-improvement-heuristic) 32 | * [Thompson and Psaraftis](https://github.com/4342315yc/VRP-Algorithms/tree/master/Heuristic/Constructive/#thompson-and-psaraftic) 33 | * [Van Breedam](https://github.com/4342315yc/VRP-Algorithms/tree/master/Heuristic/Constructive/#van-breedam) 34 | * [Kinderwater and Savelsbergh](https://github.com/4342315yc/VRP-Algorithms/tree/master/Heuristic/Constructive/kinderwater-and-savesbergh) 35 | 36 | * **[2-Phase heuristic](https://github.com/4342315yc/VRP-Algorithms/tree/master/Heuristic/2-Phase)** 37 | * [Cluster-First, Route-Second Algorithms](https://github.com/4342315yc/VRP-Algorithms/tree/master/Heuristic/2-Phase/#cluster-first-route-second-algorithms) 38 | * [Fisher and Jakimar](https://github.com/4342315yc/VRP-Algorithms/tree/master/Heuristic/2-Phase/#fisher-and-jakimar) 39 | * [The Petal Algorithm](https://github.com/4342315yc/VRP-Algorithms/tree/master/Heuristic/2-Phase/#the-petal-algorithm) 40 | * [The Sweep Algorithm](https://github.com/4342315yc/VRP-Algorithms/tree/master/Heuristic/2-Phase/#the-sweep-algorithm) 41 | * [Route-First, Cluster-Second Algorithms](https://github.com/4342315yc/VRP-Algorithms/tree/master/Heuristic/2-Phase/#route-first-cluster-second-algorithms) 42 | * [Beasley's Algorithm](https://github.com/4342315yc/VRP-Algorithms/tree/master/Heuristic/2-Phase/#beasleys-algorithm) 43 | 44 | ## Meta Heuristic Algorithms 45 | * [Ant Algorithms](https://github.com/4342315yc/VRP-Algorithms/tree/master/Meta-Heuristic#ant-algorithms) 46 | * [Constraint Programming](https://github.com/4342315yc/VRP-Algorithms/tree/master/Meta-Heuristic#constraint-programming) 47 | * [Deterministic Annealing](https://github.com/4342315yc/VRP-Algorithms/tree/master/Meta-Heuristic#deterministic-annealing) 48 | * [Genetic Algorithms](https://github.com/4342315yc/VRP-Algorithms/tree/master/Meta-Heuristic#genetic-algorithms) 49 | * [Simulated Annealing](https://github.com/4342315yc/VRP-Algorithms/tree/master/Meta-Heuristic#simulated-annealing) 50 | * [Tabu Search](https://github.com/4342315yc/VRP-Algorithms/tree/master/Meta-Heuristic#tabu-search) 51 | * [Granular Tabu](https://github.com/4342315yc/VRP-Algorithms/tree/master/Meta-Heuristic#granular-search) 52 | * [The Adaptive Memory Procedure](https://github.com/4342315yc/VRP-Algorithms/tree/master/Meta-Heuristic#the-adaptive-memory-procedure) 53 | * [Kelly and Xu](https://github.com/4342315yc/VRP-Algorithms/tree/master/Meta-Heuristic#kelly-and-xu) 54 | * [Taburoute](https://github.com/4342315yc/VRP-Algorithms/tree/master/Meta-Heuristic#taburoute) 55 | 56 | -------------------------------------------------------------------------------- /Heuristic/2-Phase/Sweep.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import time 3 | import itertools 4 | from scipy.spatial import distance_matrix 5 | 6 | class Customer: 7 | coordinate = [] 8 | angle = 0 9 | cluster_num = 0 10 | label = 0 11 | 12 | def _get_angle(self, coordinate): 13 | 14 | x = coordinate[0] 15 | y = coordinate[1] 16 | 17 | assert (x != 0 and y != 0) 18 | 19 | if x == 0: 20 | return 90 if y > 0 else 270 21 | elif y == 0: 22 | return 0 if x > 0 else 180 23 | 24 | angle = np.degrees(np.arctan(np.abs(y/x))) 25 | 26 | if x > 0 and y > 0: 27 | return angle 28 | elif x < 0 and y < 0: 29 | return angle + np.pi 30 | elif x < 0 and y > 0: 31 | return angle + np.pi/2 32 | 33 | return angle + np.pi*3/2 34 | 35 | 36 | def __init__(self, coordinate, label): 37 | self.label = label 38 | self.coordinate = coordinate 39 | self.angle = self._get_angle(coordinate) 40 | 41 | class Vehicle: 42 | ''' 43 | current number of vehicles is limited to 1 44 | ''' 45 | capacity, num = 0, 0 46 | 47 | def __init__(self, capacity, num=1): 48 | assert capacity.min() > 0 and num > 0 and num%1 == 0, "invalid vehicle info" 49 | self.capacity = capacity 50 | self.num = num 51 | 52 | class Sweep: 53 | 54 | depot, customers, demand, customers_location = 0, 0, 0, 0 55 | split_delivery = False 56 | vehicle = None 57 | 58 | def __init__(self, depot, customers, demand): 59 | self.depot = depot 60 | self.customers = customers 61 | self.demand = demand 62 | 63 | self.customers_location = np.array([c.coordinate for c in customers]) 64 | 65 | def fit(self, v): 66 | self.vehicle = v 67 | 68 | if self.vehicle.capacity < self.demand.max() and not self.split_delivery: 69 | raise Exception("The capacity of vehicle is smaller than the maximum demand") 70 | self.vehicle = None 71 | 72 | def _sort_by_angle(self): 73 | angles = np.array([c.angle for c in self.customers]) 74 | angles_arg = angles.argsort() 75 | return angles_arg 76 | 77 | def cluster(self): 78 | angles_arg = self._sort_by_angle() 79 | # sorted_customers = self.customers[angles_arg] 80 | sorted_customers = [self.customers[i] for i in angles_arg] 81 | demand = self.demand[angles_arg] 82 | cluster_num, curr_demand, i = 0, 0, 0 83 | 84 | while i < len(sorted_customers) and curr_demand <= self.vehicle.capacity: 85 | 86 | if curr_demand + demand[i] > self.vehicle.capacity: 87 | cluster_num += 1 88 | curr_demand = 0 89 | curr_demand += demand[i] 90 | sorted_customers[i].cluster_num = cluster_num 91 | i += 1 92 | 93 | # The following code will be extremely slow 94 | clusters = [[] for i in range(cluster_num+1)] 95 | label_clusters = [[] for i in range(cluster_num+1)] 96 | 97 | for c in sorted_customers: 98 | clusters[c.cluster_num].append(c.coordinate) 99 | label_clusters[c.cluster_num].append(c.label) 100 | 101 | return clusters, label_clusters 102 | 103 | def _TSP_Exact(self, cluster, label_cluster): 104 | cluster = np.array(cluster) 105 | data = np.concatenate((self.depot, cluster), axis=0) 106 | cluster_dm = distance_matrix(data, data) 107 | solution = [] 108 | best_distance = np.inf 109 | 110 | choices = list(itertools.permutations(np.arange(1, cluster.shape[0]+1, 1))) 111 | choices = choices[:len(choices)//2] 112 | for choice in choices: 113 | 114 | total_distance = cluster_dm[0, choice[0]] # from depot to first customer 115 | 116 | for idx in range(1, len(choice)): 117 | total_distance += cluster_dm[choice[idx-1], choice[idx]] 118 | total_distance += cluster_dm[choice[-1], 0] # from last customer to depot 119 | if total_distance < best_distance: 120 | best_distance = total_distance 121 | solution = choice 122 | label_cluster = [label_cluster[i-1]+1 for i in solution] 123 | return label_cluster, best_distance # solution is based on index of each customer in cluster 124 | 125 | def solve(self): 126 | start_time = time.time() 127 | clusters, label_clusters = self.cluster() 128 | solutions = [] 129 | distances = [] 130 | 131 | for cls_idx in range(len(clusters)): 132 | cls, label_cls = clusters[cls_idx], label_clusters[cls_idx] 133 | tsp_solution, best_distance = self._TSP_Exact(cls, label_cls) 134 | solutions.append(tsp_solution) 135 | distances.append(best_distance) 136 | 137 | end_time = time.time() 138 | print('\nFinished solving, with total time %s mins \n' % ((end_time - start_time)/60)) 139 | 140 | return solutions, distances -------------------------------------------------------------------------------- /Heuristic/Constructive/CW_parallel.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import time 3 | from scipy.spatial import distance_matrix 4 | 5 | class Customer: 6 | coordinate = [] 7 | belong_to_route = False 8 | is_endpoint = True 9 | 10 | def __init__(self, coordinate): 11 | self.coordinate = coordinate 12 | 13 | class Vehicle: 14 | ''' 15 | current number of vehicles is limited to 1 16 | ''' 17 | capacity, num = 0, 0 18 | 19 | def __init__(self, capacity, num=1): 20 | assert capacity.min() > 0 and num > 0 and num%1 == 0, "invalid vehicle info" 21 | self.capacity = capacity 22 | self.num = num 23 | 24 | class Clark_Wright_Parallel: 25 | # depot and customers should be a 2d array 26 | depot, customers, demand, customer_location = 0, 0, 0, 0 27 | split_delivery = False 28 | vehicle = None 29 | CWSM = None # This is a lower left triangular 30 | DM = None # distance matrix 31 | 32 | def __init__(self, depot, customers, demand, customer_location): 33 | self.depot = depot 34 | self.customers = customers 35 | self.demand = demand 36 | self.customer_location = customer_location 37 | 38 | def fit(self, v): 39 | self.vehicle = v 40 | 41 | if self.vehicle.capacity < self.demand.max() and not self.split_delivery: 42 | raise Exception("The capacity of vehicle is smaller than the maximum demand") 43 | self.vehicle = None 44 | 45 | def _init_saving_matrix(self): 46 | # The first row is depot 47 | mat_size = self.customer_location.shape[0] #+self.depot.shape[0] 48 | CWSM = np.zeros((mat_size, mat_size)) 49 | DM = self.DM 50 | from_node_idx = 0 51 | while from_node_idx != mat_size: 52 | for i in range(from_node_idx+1, mat_size): 53 | CWSM[i,from_node_idx] = DM[0, from_node_idx+1] + DM[i+1, 0] - DM[i+1, from_node_idx+1] 54 | from_node_idx += 1 55 | 56 | self.CWSM = CWSM 57 | 58 | def _parallel_solve(self): 59 | 60 | SM = self.CWSM.copy() 61 | customers_info = self.customers.copy() 62 | 63 | solution_set = [[]]*self.customer_location.shape[0] 64 | for i in range(len(solution_set)): 65 | solution_set[i] = [i] 66 | 67 | routes_capacity = self.demand.copy() 68 | 69 | while np.amax(SM) > 0: 70 | 71 | print('\nBefore: ', solution_set) 72 | 73 | max_savings = np.where(SM == np.amax(SM)) 74 | max_savings_idx = list(zip(max_savings[0], max_savings[1]))[0] # Pick up the first index 75 | print('max_savings_idx: ', max_savings_idx) 76 | is_endpoint = customers_info[max_savings_idx[0]].is_endpoint and customers_info[max_savings_idx[1]].is_endpoint 77 | 78 | max_savings_idx = list(max_savings_idx) 79 | max_savings_idx.sort() 80 | 81 | if (sum(routes_capacity[max_savings_idx]) <= self.vehicle.capacity) and is_endpoint: 82 | 83 | smaller = max_savings_idx[0] 84 | larger = max_savings_idx[1] 85 | 86 | routes_capacity[smaller] += routes_capacity[larger] 87 | # routes_capacity.remove(routes_capacity[larger]) 88 | 89 | solution_set[smaller] += solution_set[larger] 90 | 91 | for i in range(1, len(solution_set[smaller])-1): 92 | customers_info[solution_set[smaller][i]].is_endpoint = False 93 | 94 | solution_set.remove(solution_set[larger]) 95 | 96 | for idx in range(1, len(max_savings_idx)): 97 | SM[max_savings_idx[idx], max_savings_idx[idx-1]] = -np.inf 98 | 99 | if len(solution_set[smaller]) > 2: 100 | # print('if clause: ', curr_solution) 101 | # print('middle point: ', curr_solution[1:curr_solution.size-1]) 102 | middle_points = [int(i) for i in solution_set[smaller][1:len(solution_set[smaller])-1]] 103 | 104 | SM[:, middle_points] = -np.inf 105 | 106 | print('After: ', solution_set) 107 | 108 | self.customer = customers_info 109 | 110 | return solution_set 111 | 112 | def solve(self): 113 | ''' 114 | solver: parallel or sequential 115 | ''' 116 | 117 | start_time = time.time() 118 | 119 | depot, customers = self.depot, self.customer_location 120 | 121 | data = np.concatenate((depot, customers), axis=0) 122 | self.DM = distance_matrix(data, data) 123 | print('Distance Matrix: \n', self.DM) 124 | self._init_saving_matrix() 125 | print('\nInitial Savings Matrix: \n', self.CWSM) 126 | 127 | solutions = self._parallel_solve() 128 | 129 | end_time = time.time() 130 | print('\nFinished solving, with total time %s mins \n' % ((end_time - start_time)/60)) 131 | 132 | return solutions 133 | 134 | -------------------------------------------------------------------------------- /Heuristic/Constructive/CW_sequential.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import time 3 | from scipy.spatial import distance_matrix 4 | 5 | 6 | class Vehicle: 7 | ''' 8 | current number of vehicles is limited to 1 9 | ''' 10 | capacity, num = 0, 0 11 | 12 | def __init__(self, capacity, num=1): 13 | assert capacity.min() > 0 and num > 0 and num%1 == 0, "invalid vehicle info" 14 | self.capacity = capacity 15 | self.num = num 16 | 17 | class Clark_Wright_Sequential: 18 | # depot and customers should be a 2d array 19 | depot, customers, demand = 0, 0, 0 20 | split_delivery = False 21 | vehicle = None 22 | CWSM = None # This is a lower left triangular 23 | DM = None # distance matrix 24 | 25 | def __init__(self, depot, customers, demand): 26 | self.depot = depot 27 | self.customers = customers 28 | self.demand = demand 29 | 30 | def fit(self, v): 31 | self.vehicle = v 32 | 33 | if self.vehicle.capacity < self.demand.max() and not self.split_delivery: 34 | raise Exception("The capacity of vehicle is smaller than the maximum demand") 35 | self.vehicle = None 36 | 37 | 38 | def _init_saving_matrix(self): 39 | # The first row is depot 40 | mat_size = self.customers.shape[0] #+self.depot.shape[0] 41 | CWSM = np.zeros((mat_size, mat_size)) 42 | DM = self.DM 43 | from_node_idx = 0 44 | while from_node_idx != mat_size: 45 | for i in range(from_node_idx+1, mat_size): 46 | CWSM[i,from_node_idx] = DM[0, from_node_idx+1] + DM[i+1, 0] - DM[i+1, from_node_idx+1] 47 | from_node_idx += 1 48 | 49 | self.CWSM = CWSM 50 | 51 | def _sequential_solve(self): 52 | SM = self.CWSM.copy() 53 | curr_demand = 0 54 | solution_set = [] 55 | curr_solution = np.array([]) 56 | while np.amax(SM) > 0: 57 | 58 | overcapacity = False 59 | 60 | if curr_solution.size > 0: # There are endpoints in the array 61 | sub_SM = SM[:,[int(curr_solution[0]), int(curr_solution[-1])]] 62 | max_savings = np.amax(sub_SM) 63 | _temp_is_holder = np.where(sub_SM == np.amax(sub_SM)) 64 | savings_idx = (np.array([_temp_is_holder[0][0]]), np.array([_temp_is_holder[1][0]])) 65 | 66 | else: 67 | max_savings = np.amax(SM) 68 | _temp_is_holder = np.where(SM == np.amax(SM)) 69 | savings_idx = (np.array([_temp_is_holder[0][0]]), np.array([_temp_is_holder[1][0]])) 70 | 71 | # max_savings_idx is a tuple 72 | max_savings_idx = list(zip(savings_idx[0], savings_idx[1]))[0] # Pick up the first index 73 | 74 | new_demand = 0 75 | for idx in max_savings_idx: 76 | if idx not in curr_solution: 77 | new_demand += self.demand[idx] 78 | 79 | # print('current demand: ', curr_demand) 80 | # print('new demand: ', new_demand) 81 | # print('max_saving_idx: ', max_savings_idx) 82 | 83 | max_savings_idx = list(max_savings_idx) 84 | max_savings_idx.sort() 85 | 86 | if (curr_demand + new_demand <= self.vehicle.capacity) and (max_savings > 0): 87 | curr_demand += new_demand 88 | 89 | curr_solution = np.unique(np.append(curr_solution, np.array(max_savings_idx))) 90 | 91 | else: 92 | overcapacity = True 93 | if curr_solution.size > 0: 94 | solution_set.append(curr_solution) 95 | curr_solution = np.array([]) 96 | curr_demand = 0 97 | 98 | for idx in range(1, len(max_savings_idx)): 99 | SM[max_savings_idx[idx], max_savings_idx[idx-1]] = -np.inf 100 | 101 | if curr_solution.size > 2: 102 | # print('if clause: ', curr_solution) 103 | # print('middle point: ', curr_solution[1:curr_solution.size-1]) 104 | SM[:, [curr_solution[1:curr_solution.size-1].astype(int)]] = -np.inf 105 | 106 | if not overcapacity: 107 | solution_set.append(curr_solution) 108 | 109 | for c in range(len(self.customers)): 110 | is_in = False 111 | for s in solution_set: 112 | if c in s: 113 | is_in = True 114 | break 115 | if not is_in: 116 | solution_set.append(np.array([int(c)])) 117 | 118 | solution_set = [i.astype(int) for i in solution_set] 119 | return np.array(solution_set) 120 | 121 | 122 | def solve(self): 123 | ''' 124 | solver: parallel or sequential 125 | ''' 126 | 127 | start_time = time.time() 128 | 129 | depot, customers = self.depot, self.customers 130 | 131 | data = np.concatenate((depot, customers), axis=0) 132 | self.DM = distance_matrix(data, data) 133 | print('Distance Matrix: \n', self.DM) 134 | self._init_saving_matrix() 135 | print('\nInitial Savings Matrix: \n', self.CWSM) 136 | 137 | solutions = self._sequential_solve() 138 | 139 | end_time = time.time() 140 | print('\nFinished solving, with total time %s mins \n' % ((end_time - start_time)/60)) 141 | 142 | return solutions 143 | 144 | -------------------------------------------------------------------------------- /Heuristic/Constructive/README.md: -------------------------------------------------------------------------------- 1 | ## Clarke and Wright savings algorithm 2 | 3 | **Paper** : G. Clarke and J.W.Wright [Scheduling of vehicles from a central depot to a number of delivery points](https://www.jstor.org/stable/167703?seq=1#metadata_info_tab_contents) 1964 4 | 5 | In this algorithm, *saving* is defined to be the distance saved by merging two seperate routes, i.e. ![](https://latex.codecogs.com/gif.latex?{s_{ij}=c_{i0}+c_{0j}-c_{ij}}), where ![](https://latex.codecogs.com/gif.latex?c_{i0},&space;c_{0j},&space;c_{ij}) is defined as the distance between the depot with customer *i*, as the distance between the depot with customer *j*, and as the distance between customer *i* and customer *j*. 6 | 7 | By ordering the savings in a non-inceasing order and merging those routes with largest savings first, this algorithm could manage to find a near-optimal solution to the problem. 8 | 9 | There two versions of this algorithm, sequential and parallel respectively and both of them require calculating the savings first. After that, the sequential algorithm picks up one route and repeat merging other feasible routes to the current one until there is no more feasible routes and then consider the next route and reapply the same procedure. While the parallel algorithm starts from the largest savings and determine if there are two routes that can be feasibly merged. Here a feasible merge refers to the total demand of customers of two merged routes does not exceed the maximum capacity of the vehicle. 10 | 11 | More, taken from [NEO](http://neo.lcc.uma.es/vrp/solution-methods/heuristics/savings-algorithms/) website: 12 | 13 | Step 1. Savings computation 14 | * Compute the savings ![](https://latex.codecogs.com/gif.latex?{s_{ij}=c_{i0}+c_{0j}-c_{ij}}) for ![](https://latex.codecogs.com/gif.latex?i,j&space;=&space;1,...,n) and ![](https://latex.codecogs.com/gif.latex?i&space;\neq&space;j) 15 | * Create *n* vehicle routes ![](https://latex.codecogs.com/gif.latex?(0,i,0)) for ![](https://latex.codecogs.com/gif.latex?i&space;=&space;1,...,n) 16 | * Order the savings in a non increasing fashion. 17 | 18 | Step 2. Best feasible merge (Parallel version) 19 | Starting from the top of the savings list, execute the following: 20 | * Given a saving ![](https://latex.codecogs.com/gif.latex?s_{ij}), determine whether there exist two routes that can feasibility be merged: 21 | * One starting with ![](https://latex.codecogs.com/gif.latex?(0,j)) 22 | * One ending with ![](https://latex.codecogs.com/gif.latex?(i,0)) 23 | * Combine these two routes by deleting ![](https://latex.codecogs.com/gif.latex?(0,j)) and ![](https://latex.codecogs.com/gif.latex?(i,0)) and introducing ![](https://latex.codecogs.com/gif.latex?(i,j)). 24 | 25 | Step 2. Route Extension (Sequential version) 26 | * Consider in turn each route ![](https://latex.codecogs.com/gif.latex?(0,i,..,j,0)) 27 | * Determine the first saving ![](https://latex.codecogs.com/gif.latex?s_{ki}) or ![](https://latex.codecogs.com/gif.latex?s_{jl}) that can feasibly be used to merge the current route with another route ending with ![](https://latex.codecogs.com/gif.latex?(k,0)) or starting with ![](https://latex.codecogs.com/gif.latex?(0,l)). 28 | * Implement the merge and repeat this operation to the current route. 29 | * If not feasible merge exists, consider the next route and reapply the same operations. 30 | * Stop when not route merge is feasible. 31 | 32 | Example taken from [Berkeley website](http://courses.ieor.berkeley.edu/ieor151/lecture_notes/ieor151_lec18.pdf). 33 | 34 | ## Matching Based 35 | 36 | **Paper**: 37 | Maybe a relevant paper on SBPP. A.Shafahi, Z. Wang, A. Haghani [A matching-based heuristic algorithm for school bus routing problems](https://arxiv.org/ftp/arxiv/papers/1807/1807.05311.pdf). 38 | 39 | K. Altinkemer, and B.Gavish. “Parallel Savings Based Heuristic for the Delivery Problem”. 1991. 40 | 41 | M. Desrochers, and T. W. Verhoog. “A Matching Based Savings Algorithm for the Vehicle Routing Problem”. 1989. 42 | 43 | An modification to standard savings algorithm, where savings is calculated by ![](https://latex.codecogs.com/gif.latex?{s_{ij}=t(S_{i})+t(S_{j})-t(S_{i}&space;\bigcup&space;S_{j})}). Here, ![](https://latex.codecogs.com/gif.latex?S_{k}) is the set containing all vertices from route *k* and ![](https://latex.codecogs.com/gif.latex?tS_{k}) is the TSP solution to the set 44 | 45 | One variant involves approximating the TSP solution of each route instead of calculating the exact solution. 46 | 47 | ## Multi-route Improvement Heuristic 48 | 49 | ### Thompson and Psaraftis 50 | 51 | The author describes a general “b-cyclic, k-transfer” scheme in which a circular permutation of b routes is considered and k customers from each route are shifted to the next route of the cyclic permutation. The authors show that applying specific sequences of b-cyclic, k-transfer exchanges (with b = 2 or b variable, and k = 1 or 2) yields interesting results. Due to the complexity of the cyclic transfer neighborhood search, it is performed heuristically. 52 | 53 | Example of 3-cyclic-2-transfer: 54 | 55 | ![cyclic_demo](https://github.com/4342315yc/VRP-Algorithms/blob/master/Images/cyclic_transfer_demo.png) 56 | 57 | Another example of 3-cyclic 2-transfer(taken from paper): 58 | 59 | ![cyclic demo 2](https://github.com/4342315yc/VRP-Algorithms/blob/master/Images/cyclic_2.png) 60 | 61 | The cyclic transfer operator. The basic idea is to transfer simultaneously the customers denoted by white circles in cyclical manner between the routes. More precisely here customers a and c in route 1, f and j in route 2 and o and p in route 4 are simultaneously transferred to routes 2, 4, and 1 respectively and route 3 remains untouched. 62 | 63 | ### Van Breedam 64 | 65 | Van Breedam classifies the improvement operations as *string cross*, *string exchange*, *string relocation*, and *string mix*, which can all be viewed as special cases of 2-cyclic exchanges, and provides a computational analysis on a restricted number of test problems. 66 | 67 | String Cross (SC): Two chains of customers are exchanged by exchanging the 68 | endpoints of two edges in two different routes. 69 | 70 | ![SC_demo](https://github.com/4342315yc/VRP-Algorithms/blob/master/Images/van_0.png) 71 | 72 | String Relocation (SR): A string of at most k vertices is moved from one route to another, typically with k = 1 or 2. 73 | 74 | - A chain of at most k customers is moved from one route to another 75 | - Higher value of k may produce better results but the neighbourhood exploration time also increases 76 | 77 | ![SR_demo](https://github.com/4342315yc/VRP-Algorithms/blob/master/Images/van_2.png) 78 | 79 | String Exchange (SE): Two strings of at most k vertices are exchanged between two routes. k is usually small. 80 | 81 | SR and SC can viewed as a special case of SE. Assume there are two routes and *m* and *n* customers are taken from two routes respectively. SR is m > 0 and n = 0 while SC is m = n and the strings are at the end of routes. 82 | 83 | ![SE_demo](https://github.com/4342315yc/VRP-Algorithms/blob/master/Images/van_1.png) 84 | 85 | String Mix (SM): The best move between SE and SR is selected. 86 | 87 | 88 | ### Kinderwater and Savelsbergh 89 | 90 | Define similar operations and perform experiments mostly in the context of the VRP with time windows (VRPTW is not the main focus in this repo). 91 | 92 | The following are images depicting the algorithm which were taken from their paper. 93 | 94 | *crossover* 95 | 96 | ![corssover](https://github.com/4342315yc/VRP-Algorithms/blob/master/Images/crossover.png) 97 | 98 | *relocation* 99 | ![relocation](https://github.com/4342315yc/VRP-Algorithms/blob/master/Images/relocation.png) 100 | 101 | Note that in the relocation, the customer is inserted between two consecutive vertices. However, the customers can be inserted to the two closest customer in the other route. The following is an example. This is proposed by Gendreau, Hertz & Laporte [1994] 102 | 103 | ![relocation_ext](https://github.com/4342315yc/VRP-Algorithms/blob/master/Images/insertion_ext.png) 104 | 105 | *exchange* 106 | ![exchange](https://github.com/4342315yc/VRP-Algorithms/blob/master/Images/exchange.png) 107 | 108 | And some other images: 109 | 110 | ![more](https://github.com/4342315yc/VRP-Algorithms/blob/master/Images/more.png) 111 | 112 | 113 | **Paper: ** 114 | 115 | G. A. P. Kinderwater and M. W. P. Savelsbergh. [Vehicle Routing: Handling Edge Exchanges](https://pdfs.semanticscholar.org/e0a5/55b01d71f5ebdc653cf68b3ab8fc136ba47b.pdf). 1997. 116 | 117 | P. M. Thompson and H. N. Psaraftis. [Cyclic Transfer Algorithms for the Multivehicle Routing and Scheduling Problems](https://www.jstor.org/stable/171656?seq=1#metadata_info_tab_contents). 1993. 118 | 119 | A. Van Breedam. An Analysis of the Behavior of Heuristics for the Vehicle Routing Problem for a Selection of Problems with Vehicle-Related. 1994. 120 | -------------------------------------------------------------------------------- /References.md: -------------------------------------------------------------------------------- 1 | # References & Useful Resources 2 | 3 | * A general overview on VRP algorithms: [Classical and modern heuristics for the vehicle routing problem](https://onlinelibrary.wiley.com/doi/epdf/10.1111/j.1475-3995.2000.tb00200.x) 4 | 5 | * [NEO](http://neo.lcc.uma.es/vrp/) website on VRP 6 | 7 | * VRP [Slides](http://www.discovery.dist.unige.it/didattica/LS/VRP.pdf) by **Massimo Paolucci**. 8 | 9 | * SP [Slides](http://www2.imm.dtu.dk/courses/02735/sppintro.pdf) by **Jesper Larsen**. 10 | 11 | * Intro to reinforcement learning: [Lil's log](https://lilianweng.github.io/lil-log/tag/reinforcement-learning) 12 | 13 | * David Silver's reinforcement learning [course](http://www0.cs.ucl.ac.uk/staff/d.silver/web/Teaching.html) in UCL 14 | 15 | * pgrouting [Github repo](https://github.com/pgRouting/pgrouting/wiki/VRP-Algorithms) 16 | 17 | * VRP [Slides](https://imada.sdu.dk/~marco/Teaching/Fall2008/DM87/Slides/dm87-lec19-2x2.pdf). 18 | 19 | * There are 3 Github repos on TSP, which are taken from the appendix of the paper [Pointer Network](https://arxiv.org/pdf/1506.03134.pdf). 20 | [1](https://github.com/dmishin/tsp-solver), [2](https://github.com/samlbest/traveling-salesman), [3](https://github.com/beckysag/traveling-salesman). 21 | 22 | * [LKH3 Solver](http://akira.ruc.dk/~keld/research/LKH-3/LKH-3_REPORT.pdf), which is one of powerful solvers that can be used for many variants of TSP and VRP (and applied to large size). 23 | 24 | * TSP [Concorde](http://www.math.uwaterloo.ca/tsp/concorde.html) solver 25 | 26 | 27 | ## Heuristic 28 | 29 | * [Scheduling of Vehicles from A Central Depot to A Number of Delivery Points](https://www.jstor.org/stable/pdf/167703.pdf?refreqid=excelsior%3A213dde1f036b370eb9a25af6dc8cd763) by G. Clarke and J. W. Wright. 30 | 31 | * [A Generalized Insertion Heuristic for the Traveling Salesman Problem with Time Windows](https://pubsonline.informs.org/doi/pdf/10.1287/opre.46.3.330) by Michel Gendreau, Alain Hertz, Gilbert Laporte and Mihnea Stan. 32 | 33 | * [New Insertion and Postoptimization Procedures for the Traveling Salesman Problem](https://www.researchgate.net/profile/Alain_Hertz/publication/221704722_New_Insertion_and_Postoptimization_Procedures_for_the_Traveling_Salesman_Problem/links/53f746120cf2fceacc7513b0/New-Insertion-and-Postoptimization-Procedures-for-the-Traveling-Salesman-Problem.pdf) by Michel Gendreau and Alain Hertz. 34 | 35 | * [A Heuristic Algorithm for the Vehicle-Dispatch Problem](https://www.jstor.org/stable/pdf/169591.pdf?refreqid=excelsior%3A29e679f04bd42699700b8a9e88a0677e) by Billy E. Gillett and Leland R. Miller. 36 | 37 | * [Extensions of the Petal Method for Vehicle Routing](http://www2.imm.dtu.dk/courses/02735/hjorring.pdf) by David M. Ryan, Curt Hjorring and Fred Glover. 38 | 39 | * [Parallel Iterative Search Methods for Vehicle Routing Problems](https://onlinelibrary.wiley.com/doi/pdf/10.1002/net.3230230804) by E. Taillard. 40 | 41 | * [Algorithms for the Vehicle Routing and Scheduling Problems with Time Window Constraints](https://www.jstor.org/stable/pdf/170697.pdf?refreqid=excelsior%3A837230bb409395c09071c4b1d98bdbcd) by Marius M. Solomon. 42 | 43 | * [An Improved Petal Heuristic for the Vehicle Routeing Problem](https://www.jstor.org/stable/pdf/2584352.pdf?refreqid=excelsior%3Ac3b7c4c3025fe82c9d4897134f4ea925) by Jacques Renaud, Fayez F. Boctor and Gilbert Laporte. 44 | 45 | 46 | ## Meta-Heuristic 47 | 48 | * [Applying the Ant System to the Vehicle Routing Problem](https://www.researchgate.net/publication/259687430_Applying_the_Ant_System_to_the_Vehicle_Routing_Problem) by Brend Bullnheimer, Richard F. Hartl and Christine Strauss. 49 | 50 | * [An improved Ant System algorithm for the Vehicle Routing Problem](https://link.springer.com/content/pdf/10.1023/A:1018940026670.pdf) by Bernd Bullnheimer, Richard F. Hartl and Christine Strauss. 51 | 52 | * [Ant colony optimization techniques for the vehicle routing problem](https://www.sciencedirect.com/science/article/pii/S1474034604000060) by John E. Bell and Patrick R. McMullen. 53 | 54 | * [Applying Simulated Annealing Approach for Capacitated Vehicle Routing Problems](https://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=4273904) by S.W. Lin, K.C. Ying, Z.J. Lee and F.H. Hsi. 55 | 56 | * [A Simulated Annealing Algorithm for The Capacitated Vehicle Routing Problem](https://pdfs.semanticscholar.org/d80e/a21777ffd9ff41a96303fdf672bac8fe5753.pdf?_ga=2.150665974.1997366065.1577435548-1357849254.1576571462) by H. Harmanani, D. Azar, N. Helal and W. Keirouz. 57 | 58 | * [Parallel simulated annealing for the vehicle routing problem with time windows](http://neo.lcc.uma.es/vrp/wp-content/data/articles/parallel-simulated-annealing-vrp.pdf) by Zbigniew J. Czech and Piotr Czarna. 59 | 60 | * [Using Constraint Programming and Local Search Methods to Solve Vehicle Routing Problems](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.67.8526&rep=rep1&type=pdf) by Paul Shaw. 61 | 62 | * [A Parallel Hybrid Genetic Algorithm for the Vehicle Routing Problem with Time Windows](http://neo.lcc.uma.es/radi-aeb/WebVRP/data/articles/hybrid2.pdf) by Jean Berger, Mohamed Barkaoui and Olli Bräysy. 63 | 64 | * [A Hybrid Genetic Algorithm for the Capacitated Vehicle Routing Problem](https://www.cs.york.ac.uk/rts/docs/GECCO_2003/papers/2723/27230646.pdf) by Jean Berger and Mohamed Barkaoui. 65 | 66 | * [Genetic Algorithms for the Vehicle Routing Problem with Time Windows](http://neo.lcc.uma.es/vrp/wp-content/data/articles/GA4VRPTW-Sols.pdf) by Olli Bräysy. 67 | 68 | * [A route-neighborhood-based metaheuristic for vehicle routing problem with time windows](https://www.sciencedirect.com/science/article/pii/S0377221798003154) by Fuh-Hwa Franklin Liu, Sheng-Yuan Shen. 69 | 70 | * [Tabu Search Part I](http://leeds-faculty.colorado.edu/glover/TS%20-%20Part%20I-ORSA-aw.pdf) 71 | 72 | * [Tabu Search Part II](http://leeds-faculty.colorado.edu/glover/TS%20-%20Part%20II-ORSA-aw.pdf) 73 | 74 | * [A User's Guide to Tabu Search](https://link.springer.com/content/pdf/10.1007%2FBF02078647.pdf) 75 | 76 | * **The granular Tabu Search and Its Applicatiop to the Vehicle Routing Problem** by Paolo Toth and Daniele Vigo 77 | 78 | * [Tabu Search Implementation on Traveling Salesman Problem and Its Variations: A Literature Survey](https://www.scirp.org/pdf/AJOR20120200002_63598589.pdf) by Sumanta Basu. 79 | 80 | * [A Tabu Search Heuristic for the Vehicle Routing Problem](https://www.jstor.org/stable/pdf/2661622.pdf?refreqid=excelsior%3A00ee7932bb4ba6da610c30d55d5dcdc8) by Michel Gendreau, Alain Hertz and Gilbert Laporte. 81 | 82 | * [Metastrategy simulated annealing and tabu search algorithms for the vehicle routing problem ](https://link.springer.com/content/pdf/10.1007/BF02023004.pdf) by Ibrahim Hassan Osman. 83 | 84 | * [Problistic Diversification and Intensification in Local Search for Vehicle Routing](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.3.9291&rep=rep1&type=pdf) by Yves Rochat and Éric D. Taillard. 85 | 86 | 87 | ## Deep Learning / Reinforcement Learning Approaches 88 | 89 | Here is a survey on the combinatorial optimization, which is a very general topic: [Machine Learning for Combinatorial Optimization: 90 | a Methodological Tour d’Horizon](https://arxiv.org/pdf/1811.06128.pdf) by Yoshua Bengio, Andrea Lodi, and Antoine Prouvost. 91 | 92 | The following is a collection of some modern approaches on combinatorial problems mainly focusing on TSP/VRP. It turns out these DL/RL models are rather limited to solve VRP of small to medium size while solutions to larger size of VRP still depend on meta-heuristic. 93 | 94 | * [Pointer Network](https://arxiv.org/pdf/1506.03134.pdf) by Oriol Vinyals, Meire Fortunato and Navdeep Jaitly. PN is specillay designed for the combinatorial optimization. In this paper, the autohers proposed a new neural network called pointer network which outputs a permutation of the input. However, authors trained their model in a supervised way, which makes use of heuristic to get the label. The disadvantage of this is that the labels(optimal solutinos) are hard to acquire when the size of the problem is too large. Meanwhile. the quality of the model is tied to the supervised label. If the labels are not good enough, the model is not able to get better solution. 95 | 96 | * [Neural Combinatorial Optimization with Reinforcement Learning](https://arxiv.org/pdf/1611.09940.pdf) by Irwan Bello, Hieu Pham, Quoc V. Le, Mohammad Norouzi, Samy Bengio. The authors made use of reinfoecement learning based on actor-critic training along with different search strategies: sampling and active search. 97 | 98 | * [Reinforcement Learning for Solving the Vehicle Routing Problem](https://arxiv.org/pdf/1802.04240.pdf) by Mohammadreza Nazari, Afshin Oroojlooy, Martin Takac and Lawrence V. Snyder. The authors think the RNN used in original PN is not necessary since the order of the input sequence does not matter. Hence Therefore, the authors simply leave out the encoder RNN and directly use the embedded inputs instead of the RNN hidden states. On capacitated VRP, the approach outperforms classical heuristics and Google’s OR-Tools on medium-sized instances in solution quality with comparable computation time (after training). The authors also showed their approach can be applied to stochastic VRP and even more general problem of combinatorial problem. 99 | 100 | * [Attention, Learn to Sovle Routing Problem](https://openreview.net/pdf?id=ByxBFsRqYm) by Wouter Kool, Herke van Hoof and Max Welling. The authors propose a model based on attention layers with benefits over the PN and they show how to train the model using REINFORCE with a simple baseline based on a deterministic greedy rollout. This model has outperformed a wide range of baselines and get highly near-optimal results. 101 | 102 | * [Neural Large Neighborhood Search for the Capacitated Vehicle Routing Problem](https://arxiv.org/pdf/1911.09539.pdf) by Andre Hottung and Kevin Tierney. The authors apply DL to LNS (NLNS) and train the model based on different destroy operators and let the model learn the complex repair operator. The authors have shoen their method outperformed the two models mentioned above in batch search. 103 | 104 | * [Learning-Based Iterative Method for Solving Vehicle Routing Problems](https://openreview.net/pdf?id=BJe1334YDH), which is still under review now. 105 | 106 | * [Learning to Perform Local Rewriting for Combinatorial Optimization](https://arxiv.org/pdf/1810.00337.pdf) by Xinyun Chen and Yuandong Tian. 107 | 108 | * [Learning Combinatorial Optimization Algorithms over Graphs](https://arxiv.org/pdf/1704.01665.pdf) by Hanjun Dai, Elias B. Khalil, Yuyu Zhang, Bistra Dilkina, Le Song. 109 | 110 | * [Learning to Solve NP-Complete Problems: A Graph Neural Network for Decision TSP](https://arxiv.org/pdf/1809.02721.pdf) by Marcelo Prates, Pedro H. C. Avelar, Henrique Lemos, Luis C. Lamb and Moshe Y. Vardi. 111 | 112 | * [An Efficient Graph Convolutional Network Technique for the Travelling Salesman Problem](https://arxiv.org/pdf/1906.01227.pdf) by Chaitanya K. Joshi, Thomas Laurent and Xavier Bresson. 113 | -------------------------------------------------------------------------------- /Meta-Heuristic/Ant_Colony.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from threading import Thread 3 | 4 | class Ant_Colony: 5 | 6 | class Ant(Thread): 7 | 8 | def __init__(self, pheromone_map, start_node, possible_locations, demand, 9 | capacity, distance_callback, decay=0.75, alpha=5, beta=5, gamma=5, first_pass=False): 10 | ''' 11 | pheromone_map: map of pheromone from current location to the next location (pheromone of arc) 12 | start_node: starting location 13 | possible locations: array of indices showing available locations of next move 14 | demand: 1d array showing the demand of each customer 15 | capacity: represents the capacity of the vehicle 16 | decay: evaporation rate 17 | alpha: relative influence of pheromone trails 18 | beta: relative influence of visibility 19 | gamma: relative influence of capacity ratio. This parameter is problem-specific 20 | ''' 21 | Thread.__init__(self) 22 | self.pheromone_map = pheromone_map 23 | self.start_node= start_node 24 | self.possible_locations = possible_locations 25 | self.demand = demand 26 | self.decay = decay 27 | self.alpha = alpha 28 | self.beta = beta 29 | self.gamma = gamma 30 | self.distance_travelled = 0 31 | self.route = [] 32 | self.tour_complete = False 33 | 34 | 35 | self.remaining_capacity = capacity 36 | self.capacity = capacity 37 | self.distance_callback = distance_callback 38 | self.first_pass = first_pass 39 | 40 | self._update_route(start_node) 41 | self._update_possible_locations() 42 | 43 | 44 | def run(self): 45 | while self.possible_locations: 46 | next_location = self._pick_move() 47 | self._update_distance(next_location) 48 | self._update_route(next_location) 49 | self._update_possible_locations() 50 | 51 | self.tour_complete = True 52 | 53 | def _pick_move(self, Q=1): 54 | # Ensure visited places not not visited again 55 | attractiveness = [] 56 | total = 0 57 | for possbile_next_location in self.possible_locations: 58 | 59 | tau = float(self.pheromone_map[self.location][possbile_next_location]) 60 | eta = Q/self.distance_callback(self.location, possbile_next_location) 61 | 62 | kappa = (self.remaining_capacity + self.demand[possbile_next_location])/self.capacity 63 | # kappa = 0 if kappa > 1 else kappa 64 | 65 | v = (tau**self.alpha)*(eta**self.beta)*(kappa**self.gamma) 66 | attractiveness.append(v) 67 | total += v 68 | 69 | if np.abs(total-0.0)<1e-7: 70 | choice = np.random.choice(a=np.arange(len(self.possible_locations)), size=1)[0] 71 | else: 72 | attractiveness = np.array(attractiveness)/total 73 | choice = np.random.choice(a=np.arange(len(self.possible_locations)), size=1, p=attractiveness)[0] 74 | 75 | return self.possible_locations[choice] 76 | 77 | def get_tour(self): 78 | if self.tour_complete: 79 | return self.route 80 | return None 81 | 82 | def get_total_distance(self): 83 | if self.tour_complete: 84 | return self.distance_travelled 85 | return None 86 | 87 | def _update_route(self, new): 88 | self.route.append(new) 89 | self.location = new 90 | self.remaining_capacity -= self.demand[new] 91 | 92 | def _update_distance(self, new): 93 | self.distance_travelled += float(self.distance_callback(self.location, new)) 94 | 95 | def _update_possible_locations(self): 96 | curr_capacity = self.remaining_capacity 97 | possible_locations = [] 98 | for location in self.possible_locations: 99 | if (curr_capacity - self.demand[location] >= 0) and (location != self.location): 100 | possible_locations.append(location) 101 | self.possible_locations = possible_locations 102 | 103 | 104 | def __init__(self, nodes, distance_callback, capacity, demand, start=None, elitist_num = 5, ant_count=50, alpha=5, beta=5, gamma=5, 105 | pheromone_evaporation_coefficient=.40, pheromone_constant=1, iterations=80): 106 | """ 107 | initializes an ant colony (houses a number of worker ants that will traverse a map to find an optimal route as per ACO [Ant Colony Optimization]) 108 | source: https://en.wikipedia.org/wiki/Ant_colony_optimization_algorithms 109 | 110 | nodes -> is assumed to be a dict() mapping node ids to values 111 | that are understandable by distance_callback 112 | 113 | distance_callback -> is assumed to take a pair of coordinates and return the distance between them 114 | populated into distance_matrix on each call to get_distance() 115 | 116 | start -> if set, then is assumed to be the node where all ants start their traversal 117 | if unset, then assumed to be the first key of nodes when sorted() 118 | 119 | distance_matrix -> holds values of distances calculated between nodes 120 | populated on demand by _get_distance() 121 | 122 | pheromone_map -> holds final values of pheromones 123 | used by ants to determine traversals 124 | pheromone dissipation happens to these values first, before adding pheromone values from the ants during their traversal 125 | (in ant_updated_pheromone_map) 126 | 127 | ant_updated_pheromone_map -> a matrix to hold the pheromone values that the ants lay down 128 | not used to dissipate, values from here are added to pheromone_map after dissipation step 129 | (reset for each traversal) 130 | 131 | alpha -> a parameter from the ACO algorithm to control the influence of the amount of pheromone when an ant makes a choice 132 | 133 | beta -> a parameters from ACO that controls the influence of the distance to the next node in ant choice making 134 | 135 | pheromone_constant -> a parameter used in depositing pheromones on the map (Q in ACO algorithm) 136 | used by _update_pheromone_map() 137 | 138 | pheromone_evaporation_coefficient -> a parameter used in removing pheromone values from the pheromone_map (rho in ACO algorithm) 139 | used by _update_pheromone_map() 140 | 141 | ants -> holds worker ants 142 | they traverse the map as per ACO 143 | notable properties: 144 | total distance traveled 145 | route 146 | 147 | first_pass -> flags a first pass for the ants, which triggers unique behavior 148 | 149 | iterations -> how many iterations to let the ants traverse the map 150 | 151 | shortest_distance -> the shortest distance seen from an ant traversal 152 | 153 | shortets_path_seen -> the shortest path seen from a traversal (shortest_distance is the distance along this path) 154 | """ 155 | #nodes 156 | if type(nodes) is not dict: 157 | raise TypeError("nodes must be dict") 158 | 159 | if len(nodes) < 1: 160 | raise ValueError("there must be at least one node in dict nodes") 161 | 162 | #create internal mapping and mapping for return to caller 163 | self.id_to_key, self.nodes = self._init_nodes(nodes) 164 | 165 | #create matrix to hold distance calculations between nodes 166 | self.distance_matrix = np.zeros((len(nodes), len(nodes))) #self._init_matrix(len(nodes)) 167 | 168 | #create matrix for master pheromone map, that records pheromone amounts along routes 169 | self.pheromone_map = np.zeros((len(nodes), len(nodes))) #self._init_matrix(len(nodes)) 170 | 171 | #create a matrix for ants to add their pheromones to, before adding those to pheromone_map during the update_pheromone_map step 172 | self.ant_updated_pheromone_map = np.zeros((len(nodes), len(nodes))) #self._init_matrix(len(nodes)) 173 | 174 | #distance_callback 175 | if not callable(distance_callback): 176 | raise TypeError("distance_callback is not callable, should be method") 177 | 178 | self.distance_callback = distance_callback 179 | 180 | # capacity 181 | if type(capacity) is not int: 182 | raise TypeError("capacity must be int") 183 | 184 | if beta < 0: 185 | raise ValueError("capacity must be >= 1") 186 | 187 | self.capacity = capacity 188 | 189 | # demand 190 | if type(demand) is not np.ndarray: 191 | raise TypeError("demand must be numpy array") 192 | 193 | if demand.min() < 0: 194 | raise ValueError("min value of demand must be >= 0") 195 | 196 | self.deamnd = demand 197 | 198 | #start 199 | if start is None: 200 | self.start = 0 201 | else: 202 | self.start = None 203 | #init start to internal id of node id passed 204 | for key, value in self.id_to_key.items(): 205 | if value == start: 206 | self.start = key 207 | 208 | #if we didn't find a key in the nodes passed in, then raise 209 | if self.start is None: 210 | raise KeyError("Key: " + str(start) + " not found in the nodes dict passed.") 211 | 212 | #ant_count 213 | if type(ant_count) is not int: 214 | raise TypeError("ant_count must be int") 215 | 216 | if ant_count < 1: 217 | raise ValueError("ant_count must be >= 1") 218 | 219 | self.ant_count = ant_count 220 | 221 | #elitist number 222 | if type(elitist_num) is not int: 223 | raise TypeError("elitist_num must be int") 224 | 225 | if ant_count < 0: 226 | raise ValueError("elitist_num must be >= 0") 227 | 228 | if elitist_num == 0: 229 | elitist_num = ant_count 230 | 231 | self.elitist_num = elitist_num 232 | 233 | 234 | #alpha 235 | if (type(alpha) is not int) and type(alpha) is not float: 236 | raise TypeError("alpha must be int or float") 237 | 238 | if alpha < 0: 239 | raise ValueError("alpha must be >= 0") 240 | 241 | self.alpha = float(alpha) 242 | 243 | #beta 244 | if (type(beta) is not int) and type(beta) is not float: 245 | raise TypeError("beta must be int or float") 246 | 247 | if beta < 1: 248 | raise ValueError("beta must be >= 1") 249 | 250 | self.beta = float(beta) 251 | 252 | #gamma 253 | if (type(gamma) is not int) and type(gamma) is not float: 254 | raise TypeError("alpha must be int or float") 255 | 256 | if gamma < 0: 257 | raise ValueError("alpha must be >= 0") 258 | 259 | self.gamma = float(gamma) 260 | 261 | #pheromone_evaporation_coefficient 262 | if (type(pheromone_evaporation_coefficient) is not int) and type(pheromone_evaporation_coefficient) is not float: 263 | raise TypeError("pheromone_evaporation_coefficient must be int or float") 264 | 265 | self.pheromone_evaporation_coefficient = float(pheromone_evaporation_coefficient) 266 | 267 | #pheromone_constant 268 | if (type(pheromone_constant) is not int) and type(pheromone_constant) is not float: 269 | raise TypeError("pheromone_constant must be int or float") 270 | 271 | self.pheromone_constant = float(pheromone_constant) 272 | 273 | #iterations 274 | if (type(iterations) is not int): 275 | raise TypeError("iterations must be int") 276 | 277 | if iterations < 0: 278 | raise ValueError("iterations must be >= 0") 279 | 280 | self.iterations = iterations 281 | 282 | #other internal variable init 283 | self.first_pass = True 284 | self.ants = self._init_ants(self.start) 285 | self.shortest_distance = None 286 | self.shortest_path_seen = None 287 | 288 | def _init_nodes(self, nodes): 289 | id_to_key = dict() 290 | id_to_values = dict() # think this as a map from customer id to its demand 291 | 292 | id = 0 293 | for key in sorted(nodes.keys()): 294 | id_to_key[id] = key 295 | id_to_values[id] = nodes[key] 296 | id += 1 297 | 298 | return id_to_key, id_to_values 299 | 300 | def _init_matrix(self, size): 301 | return np.zeros((size, size)) 302 | 303 | def _init_ants(self, start_location): 304 | if self.first_pass: 305 | return [self.Ant(self.pheromone_map, start_location, list(self.nodes.keys()), self.deamnd, 306 | self.capacity, self.distance_callback, self.pheromone_evaporation_coefficient, 307 | self.alpha, self.beta, self.gamma, first_pass=True) for _ in range(self.ant_count)] 308 | 309 | else: 310 | for ant in self.ants: 311 | ant.__init__(self.pheromone_map, start_location, list(self.nodes.keys()), self.deamnd, 312 | self.capacity, self.distance_callback, self.pheromone_evaporation_coefficient, 313 | self.alpha, self.beta, self.gamma) 314 | 315 | 316 | def _update_pheromone(self): 317 | 318 | shortest_length = self.shortest_distance 319 | shortest_route = self.shortest_path_seen 320 | 321 | for from_node in range(self.pheromone_map.shape[0]): 322 | for to_node in range(self.pheromone_map.shape[1]): 323 | self.pheromone_map[from_node, to_node] *= (1-self.pheromone_evaporation_coefficient) 324 | self.pheromone_map[from_node, to_node] += self.ant_updated_pheromone_map[from_node, to_node] 325 | 326 | if self.elitist_num != 0 and [from_node, to_node] in shortest_route: 327 | self.pheromone_map[from_node, to_node] += self.elitist_num*(self.pheromone_constant/shortest_length) 328 | 329 | def _populate_ant_updated_pheromone_map(self, ant): 330 | route = ant.get_tour() 331 | for i in range(len(route)-1): 332 | curr_pher = float(self.ant_updated_pheromone_map[route[i], route[i+1]]) 333 | new_pher = self.pheromone_constant/ant.get_total_distance() 334 | self.ant_updated_pheromone_map[route[i], route[i+1]] = curr_pher + new_pher 335 | self.ant_updated_pheromone_map[route[i+1], route[i]] = curr_pher + new_pher 336 | 337 | def solve(self): 338 | for i in range(self.iterations): 339 | for ant in self.ants: 340 | ant.start() 341 | 342 | for ant in self.ants: 343 | ant.join() 344 | 345 | for ant in self.ants: 346 | self._populate_ant_updated_pheromone_map(ant) 347 | 348 | if not self.shortest_distance: 349 | self.shortest_distance = ant.get_total_distance() 350 | 351 | if not self.shortest_path_seen: 352 | self.shortest_path_seen = ant.get_tour() 353 | 354 | if self.shortest_distance < ant.get_total_distance(): 355 | self.shortest_distance = ant.get_total_distance() 356 | self.shortest_path_seen = ant.get_tour() 357 | 358 | 359 | self._update_pheromone() 360 | 361 | if self.first_pass: 362 | self.first_pass = False 363 | 364 | self._init_ants(self.start)#+i%4) 365 | self.ant_updated_pheromone_map = np.zeros((len(self.nodes), len(self.nodes))) 366 | 367 | result = [] 368 | 369 | for id in self.shortest_path_seen: 370 | result.append(self.id_to_key[id]) 371 | 372 | return result -------------------------------------------------------------------------------- /Meta-Heuristic/README.md: -------------------------------------------------------------------------------- 1 | A lot of the following are taken from [NEO](http://neo.lcc.uma.es/vrp/solution-methods/) and Wikipedia. 2 | 3 | # Ant Algorithms 4 | 5 | Accoridng to [Bullnheimer et al. 1997](http://neo.lcc.uma.es/vrp/wp-content/data/articles/bullnheimer97AS.pdf), the ant system consists of two phases: construction of vehicle routes and trail update. 6 | 7 | [Wikipedia](https://en.wikipedia.org/wiki/Ant_colony_optimization_algorithms) provides a elegant formulation of AS algorithm in *Algorithm and formulae* section. 8 | 9 | * **Construction (Edge selection)** 10 | 11 | The probabiliy of selecting an edge is calculated as following: 12 | 13 | ![ant](https://github.com/4342315yc/VRP-Algorithms/blob/master/Images/AS_prob.png) 14 | 15 | Where 16 | 17 | ![e](https://latex.codecogs.com/png.latex?\dpi{120}&space;\fn_cm&space;\fn_cm&space;{\Omega&space;=&space;\left&space;\lbrace&space;v_{j}&space;\in&space;V&space;:&space;v_{j}&space;\;&space;\text{is&space;feasible&space;to&space;be&space;visited}&space;\right&space;\rbrace&space;\bigcup&space;\left&space;\lbrace&space;v_{0}&space;\right&space;\rbrace}) 18 | 19 | The *attractiveness* ![eta](https://latex.codecogs.com/png.latex?\inline&space;\dpi{150}&space;\fn_cm&space;\eta_{ij}) of the move, as computed by some heuristic indicating the a priori desirability of that move. 20 | 21 | The *trail level* ![tau](https://latex.codecogs.com/png.latex?\inline&space;\dpi{150}&space;\fn_cm&space;\tau_{ij}) of the move, indicating how proficient it has been in the past to make that particular move(typically ![tau](https://latex.codecogs.com/png.latex?\inline&space;\dpi{150}&space;\fn_cm&space;\frac{1}{d_{ij}}), where ![tau](https://latex.codecogs.com/png.latex?\inline&space;\dpi{150}&space;\fn_cm&space;d_{ij}) is the distance between two states). The trail level represents a posteriori indication of the desirability of that move. 22 | 23 | * **Trail Update (Pheromone update)** 24 | 25 | ![1](https://latex.codecogs.com/png.latex?\inline&space;\dpi{150}&space;\fn_cm&space;{&space;\tau_{ij}^{new}&space;=&space;\rho\tau_{ij}^{old}&space;+&space;\sum_{\mu&space;=&space;1}^{\sigma-1}\Delta\tau_{ij}^{\mu}&space;+&space;\sigma&space;\Delta&space;\tau_{ij}^{*}&space;}) 26 | 27 | Or according to Wikipedia: 28 | 29 | ![2](https://latex.codecogs.com/png.latex?\inline&space;\dpi{150}&space;\fn_cm&space;{&space;\tau_{ij}=&space;(1-\rho)\tau_{ij}&space;+&space;\sum_{k}\Delta&space;\tau_{ij}^k&space;}) 30 | 31 | where ![3](https://latex.codecogs.com/png.latex?\inline&space;\dpi{110}&space;\fn_cm&space;\rho) in the first equation is the *trail persistance* and ![4](https://latex.codecogs.com/png.latex?\inline&space;\dpi{110}&space;\fn_cm&space;\rho) in the second equation is the *pheromone evaporation coefficient*. 32 | 33 | And 34 | 35 | ![4](https://github.com/4342315yc/VRP-Algorithms/blob/master/Images/AS_delta.png), 36 | 37 | where **Q** is a constant. 38 | 39 | # Constraint Programming 40 | 41 | [Python library for CP](https://pypi.org/project/python-constraint/). And [Wikipedia](https://en.wikipedia.org/wiki/Constraint_programming#Constraint_programming_libraries_for_general-purpose_programming_languages) provides a list of languages that support CP. 42 | 43 | Problems are expressed in terms of variables, domains for those variables and constraints between the variables. The problems are then solved using complete search techniques such as depth-first search (for satisfaction) and branch and bound (for optimisation). 44 | 45 | The typical methods for CP are *chronological backtracking* and *constraint propagation*. 46 | 47 | *Backtracking* is a general algorithm for finding all (or some) solutions to some computational problems, notably constraint satisfaction problems, that incrementally builds candidates to the solutions, and abandons a candidate ("backtracks") as soon as it determines that the candidate cannot possibly be completed to a valid solution. 48 | 49 | *Local consistency* conditions are properties of constraint satisfaction problems related to the consistency of subsets of variables or constraints. They can be used to reduce the search space and make the problem easier to solve. Various kinds of local consistency conditions are leveraged, including *node consistency*, *arc consistency*, and *path consistency*. Every *local consistency* condition can be enforced by a transformation that changes the problem without changing its solutions. Such a transformation is called *constraint propagation*. 50 | 51 | * Core Constraints 52 | 53 | 1. Time 54 | 55 | 2. Capacity 56 | 57 | * Side constraints 58 | 59 | Solutions to CP problems are usually found using complete methods such as depth-first search and branch and bound. However, for routing problems of practical size, complete search methods cannot produce solutions in a short and reliable time period. By contrast, iterative improvement methods have proved very successful in this regard. Iterative improvement methods operate by changing small parts of the solution, for instance moving a visit from one route to another. This type of operation involves retracting previous decisions and making new ones. In contrast to depth-first search, retraction can be done in any order, not simply in the opposite order the decisions were made. In general the overhead of implementing this type of non-chronological retraction mechanism in CP frameworks is very high. 60 | 61 | To overcome this problem, the CP system is only used to check the validity of solutions and determine the values of constrained variables, not to search for solutions. The search is performed by an iterative improvement procedure. When the procedure needs to check the validity of a potential solution, it is handed to the CP system. As a part of checking, constraint propagation using all constraints still takes place. This adds value to the constraint check, as the iterative improvement method can then take advantage of the reduced domains to speed up search by performing fast legality checks. 62 | 63 | If the propagation mechanism removes all values from any variable, then there cannot be a solution in this subtree and the search backtracks making a different decision at the backtrack point. 64 | 65 | # Deterministic Annealing 66 | 67 | Deterministic Annealing operates in a way that is similar to SA, except that a deterministic rule is used for the acceptance of a move. Two standard implementations of this technique are threshold accepting [Dueck and Scheurer 1990] and record to record travel [Dueck 1993]. 68 | 69 | At iteration t of a threshold accepting algorithm, solution ![10](https://latex.codecogs.com/png.latex?\inline&space;\fn_cm&space;(x_{i+1})) is accepted if ![](https://latex.codecogs.com/png.latex?\inline&space;\fn_cm&space;f(x_{i+1})&space;\le&space;f(x_{i})+&space;\theta_{1}) here ![](https://latex.codecogs.com/png.latex?\inline&space;\fn_cm&space;\theta_1) is a user controlled parameter. In record-to-record travel a record is the best solution ![](https://latex.codecogs.com/png.latex?\inline&space;\fn_cm&space;x^{*}) encountered during the search. At iteration t, solution ![](https://latex.codecogs.com/png.latex?\inline&space;\fn_cm&space;x_{i+1}) is accepted if ![](https://latex.codecogs.com/png.latex?\inline&space;\fn_cm&space;f(x_{i+1})&space;\le&space;\theta_{2}f(x_{i})), where ![](https://latex.codecogs.com/png.latex?\inline&space;\fn_cm&space;\theta_2) is a user controlled parameter slightly larger than ![](https://latex.codecogs.com/png.latex?\inline&space;\fn_cm&space;\theta_1). 70 | 71 | # Genetic Algorithms 72 | 73 | Inspired by the natural selection, genetic algorithm is used a meta-heeuritic algorithm being used a lot to generate high-quality solutions based on bio-inspired operations such as mutation, crossover and selection. 74 | 75 | The evolution usually starts from a population of randomly generated individuals, and is an iterative process, with the population in each iteration called a generation. In each generation, the *fitness* of every individual in the population is evaluated; the *fitness* is usually the value of the objective function in the optimization problem being solved. 76 | 77 | A picture illustrating of GA. (solutions are aften represented as a string of bits, which are problem specific): 78 | 79 | ![](https://miro.medium.com/max/1600/1*BYDJpa6M2rzWNSurvspf8Q.png) 80 | 81 | In general, there are some common phases for GA: 82 | 83 | 1. **Initialization**. A *papulation* in GA refers to a set of candidate solutions(called *individual*, encoded as *chromosomes*) and the size of it is depending on the problem. The initial population is generated randomly, allowing the entire range of possible solutions (the search space). Occasionally, the solutions may be "seeded" in areas where optimal solutions are likely to be found. 84 | 85 | 2. **Selection**. The process consist of randomly choosing two parent individuals from the population for mating purposes. The probability of selecting a population member is generally proportional to its fitness in order to emphasize genetic quality while maintaining genetic diversity. (Fitness can also be considered as a measure of profit, utility or goodness to be maximized while exploring the solution space.). Diversity is ensured by selecting parents with less fitness value. 86 | 87 | 3. **Genetic operators** This process refers to generate a new generation through genetic operators: crossover(AKA recombination) and mutation. 88 | 89 | * **Crossover**. The process makes use of genes of selected parents to produce offspring that will form the next generation. Although reproduction methods that are based on the use of two parents are more "biology inspired", some research suggests that more than two "parents" generate higher quality chromosomes. 90 | 91 | * **Mutation**. It consists of randomly modifying some gene(s) of a single individual at a time to further explore the solution space and ensure, or preserve, genetic diversity. The occurrence of mutation is generally associated with a low probability. 92 | 93 | **Heuristic**. In addition to the main operators above, other heuristics may be employed to make the calculation faster or more robust. The speciation heuristic penalizes crossover between candidate solutions that are too similar; this encourages population diversity and helps prevent premature convergence to a less optimal solution. 94 | 95 | There are usually multiple stopping ctriteria such as the number of iterations is reached, the whole population converges to a homogeneous system with similar individuals or an optimal solution is found. 96 | 97 | For solving VRP with GAs, it is usual to represent each individual by just one chromosome, which is a chain of integers, each of them representing a customer or a vehicle. So that each vehicle identifier represents in the chromosome a separator between two different routes, and a string of customer identifiers represents the sequence of deliveries that must cover a vehicle during its route. In the figure below we can see a representation of a possible solution for VRP with 10 customers and 4 vehicles. Each route begins and end at the depot (it will be assigned the number 0). If we find in a solution two vehicle identifiers not separated by any customer identifier, we will understand that the route is empty and, therefore, it will not be necessary to use all the vehicles available. 98 | 99 | ![](https://latex.codecogs.com/png.latex?\inline&space;\dpi{120}&space;\fn_cm&space;\large&space;\underbrace{4-5-2}_{route1}-11-\underbrace{10-3-1}_{route2}-13-\underbrace{7-8-9}_{route3}-12-\underbrace{6}_{route4}) 100 | 101 | A typical fitness function used for solving VRP with GA is ![](https://latex.codecogs.com/png.latex?\inline&space;\fn_cm&space;f_{eval}(x)&space;=&space;f_{max}-f(x)) where ![](https://latex.codecogs.com/png.latex?\inline&space;\fn_cm&space;{f(x)&space;=&space;totaldistance(x)&space;+&space;\lambda&space;overcapacity(x)&space;+&space;\mu&space;overtime(x)}) is the *unfitness* value. 102 | 103 | The **overcapacity** and **overtime** functions are acting as a penaly term and they measured the amount of capacity and time that is over the maximum. If no violations on the capacity and time, the function will simply return the total distance and hence the fitness value is better. 104 | 105 | There are still other functions can be used as the fitness value function such as the reciprocal of the total travelled distance 106 | 107 | # Simulated Annealing 108 | 109 | Simulated Annealing (SA) is a stochastic relaxation technique, which has its origin in statistical mechanics. It is based on an analogy from the annealing process of solids, where a solid is heated to a high temperature and gradually cooled in order for it to crystallize in a low energy configuration. SA can be seen as one way of trying to allow the basic dynamics of hill-climbing to also be able to escape local optima of poor solution quality. SA guides the original local search method in the following way. The solution **S’** is accepted as the new current solution if ![5](https://latex.codecogs.com/png.latex?\inline&space;\fn_cm&space;\Delta&space;\leq&space;0), where ![7](https://latex.codecogs.com/png.latex?\inline&space;\fn_cm&space;\Delta&space;=&space;f(x)-f(x_i)). To allow the search to escape a local optimum, moves that increase the objective function value are accepted with a probability ![8](https://latex.codecogs.com/png.latex?\inline&space;\fn_cm&space;e^{-\Delta/T}) if $![9](https://latex.codecogs.com/png.latex?\inline&space;\fn_cm&space;\Delta&space;>&space;0), where *T* is a parameter called the “temperature”. The value of *T* varies from a relatively large value to a small value close to zero. These values are controlled by a cooling schedule, which specifies the initial, and temperature values at each stage of the algorithm. 110 | 111 | At iteration **t** of Simulated Annealing, a solution **x** is drawn randomly in ![10](https://latex.codecogs.com/png.latex?\inline&space;\fn_cm&space;N(x_{i})). If ![11](https://latex.codecogs.com/png.latex?\inline&space;\fn_cm&space;f(x)&space;\le&space;f(x_{i})), then ![12](https://latex.codecogs.com/png.latex?\inline&space;\fn_cm&space;x_{i+1}) is set equal to **x**; otherwise 112 | 113 | ![13](https://latex.codecogs.com/png.latex?\inline&space;\fn_cm&space;{x_{i+1}=\begin{cases}&space;x&space;&&space;\text{with&space;probability&space;}p_{i}\\\\&space;x_{i}&space;&&space;\text{with&space;probability&space;}1-p_{i}&space;\end{cases}}) 114 | 115 | where ![12](https://latex.codecogs.com/png.latex?\inline&space;\fn_cm&space;p_{i}) is usually a decreasing function of **t** and of ![12](https://latex.codecogs.com/png.latex?\inline&space;\fn_cm&space;f(x)-f(x_i)). It's usually set to be ![8](https://latex.codecogs.com/png.latex?\inline&space;\fn_cm&space;e^{-\Delta/T}) 116 | 117 | The stopping criteria is usually divided into three cases: 118 | 119 | 1. The value ![13](https://latex.codecogs.com/png.latex?\inline&space;\fn_cm&space;f^{*}) of the incumbent ![14](https://latex.codecogs.com/png.latex?\inline&space;\fn_cm&space;x^{*}) has not decreased by at least ![15](https://latex.codecogs.com/png.latex?\inline&space;\fn_cm&space;\pi_1) % for at least ![16](https://latex.codecogs.com/png.latex?\inline&space;\fn_cm&space;k_1) consecutive cycle of **T** iterations; 120 | 121 | 2. The number of accepted moves has been less than ![16](https://latex.codecogs.com/png.latex?\inline&space;\fn_cm&space;\pi_{2}\%) % of **T** for ![17](https://latex.codecogs.com/png.latex?\inline&space;\fn_cm&space;k_2) consecutive cycles of **T** iterations; 122 | 123 | 3. ![19](https://latex.codecogs.com/png.latex?\inline&space;\fn_cm&space;k_3) of **T** iterations have been executed. 124 | 125 | Also an example of annealing on CVRP (taken from [here](https://pdfs.semanticscholar.org/d80e/a21777ffd9ff41a96303fdf672bac8fe5753.pdf?_ga=2.150665974.1997366065.1577435548-1357849254.1576571462) ): 126 | 127 | ![6](https://github.com/4342315yc/VRP-Algorithms/blob/master/Images/cvrp_annealing.png) 128 | 129 | # Tabu Search 130 | 131 | Reference to Tabu search: 132 | 133 | [Tabu Search Part I](http://leeds-faculty.colorado.edu/glover/TS%20-%20Part%20I-ORSA-aw.pdf) 134 | 135 | [Tabu Search Part II](http://leeds-faculty.colorado.edu/glover/TS%20-%20Part%20II-ORSA-aw.pdf) 136 | 137 | [A User's Guide to Tabu Search](https://link.springer.com/content/pdf/10.1007%2FBF02078647.pdf) 138 | 139 | 140 | Tabu search is a metaheuristic search method employing local search methods used for mathematical optimization. 141 | 142 | Local (neighborhood) searches take a potential solution to a problem and check its immediate neighbors (that is, solutions that are similar except for very few minor details) in the hope of finding an improved solution. Local search methods have a tendency to become stuck in suboptimal regions or on plateaus where many solutions are equally fit. 143 | 144 | Tabu search enhances the performance of local search by relaxing its basic rule. First, at each step worsening moves can be accepted if no improving move is available (like when the search is stuck at a strict local minimum). In addition, prohibitions (henceforth the term tabu) are introduced to discourage the search from coming back to previously-visited solutions. 145 | 146 | The implementation of tabu search uses memory structures that describe the visited solutions or user-provided sets of rules. If a potential solution has been previously visited within a certain short-term period or if it has violated a rule, it is marked as "tabu" (forbidden) so that the algorithm does not consider that possibility repeatedly. 147 | 148 | The initial solution is typically created with some cheapest insertion heuristic. After creating an initial solution, an attempt is made to improve it using local search with one or more neighborhood structures and a best-accept strategy. Most of the neighborhoods used are well known and were previously introduced in the context of various construction and improvement heuristics. **Aspiration criteria** is often utilised in tabu search. It refers to the overrule of tabu status if the tabu solution found has a better objective value than the current solution. 149 | 150 | There three different memory structures used for tabu search: short term, intermediate term and long term. 151 | 152 | Based on this [literature review](https://www.scirp.org/pdf/AJOR20120200002_63598589.pdf): 153 | 154 | **Short term memory structure** are used in tabu search to prevent the search from re-visiting solutions that it has visited in the immediate past. They are normally stored as a collection of forbidden moves in a list called the tabu list. Each move in the tabu list remains in the list for a pre-specified number of tabu search iterations. This number is called its tabu tenure. The tabu tenure are divided into static and random ones. Static tenure refers to a fixed value or deterministiclly changed value based on solution/problem specific parameter while random tenure is about generation from a pre-defined range of value. However a general generation of tabu tenure value has not been found yet. Some authors used functional value but only linear and logarithmtic functions are tried. 155 | 156 | **Intermediate term momery structure** are used in tabu search to **intensify** the search by restricting it to promising regions of the solution space. It has four basic categories: 157 | 158 | * **IT1**: Edges that occur frequently in low cost tours are forced into candidate tours for next few iterations. 159 | 160 | * **IT2**: The search is re-started with a tour that was one of the lower cost tours found in previous iterations. 161 | 162 | * **IT3**: Higher probabilities are assigned to include the edges common to previously encountered low cost tours in a probabilistic tabu search. 163 | 164 | * **IT4**: Changing tabu tenure (in comparison to existing tenure value) whenever a local optimum is reached. 165 | 166 | **Long term memory structure** are used to diversify the search to new regions in the solution space. It has the following strategies (not exhaustive): 167 | 168 | * **LT1** is a frequency based diversification scheme implemented by adding a penalty value to the cost of each edge. The penalty is proportional to the number of times the edge appeared in previously visited tours. The objective here is to create a disincentive for including edges that were often encountered previously. 169 | 170 | * **LT2** is a modification of strategy LT1, in which the penalty value also includes terms that are not dependent on frequency measures. 171 | 172 | * **LT3** attains diversification by changing the way in which tours are evaluated in order to move the search to new parts of the search space. It may also involve changing the move being used in the search. 173 | 174 | * **LT4** creates a diversification mechanism by changing the stopping criterion to allow more non-improving moves. 175 | 176 | * **LT5** is a diversification scheme which restarts tabu search iteration from different initial solutions. It helps to change the initial search space and creates a chance to reach to a different solution region. 177 | 178 | * **LT6** is used in some papers for diversification if no improvement is seen in the best tour cost for certain number of iterations, diversification is attained by adding a parameter called influence measure to the tour costs for neighboring solutions. The influence measure measures the degree of similarity between two consecutive solutions. 179 | 180 | ## Granular Tabu 181 | 182 | It turns out the longer the distance is, the less likely it is to belong to the optimal solution. Hence by defining *granularity threshold*, several unpromising solutions will never be considered by the search process. The authors proposed ![](https://latex.codecogs.com/png.latex?\inline&space;\fn_cm&space;\large&space;\nu&space;=&space;\beta&space;\bar{c}), where ![](https://latex.codecogs.com/png.latex?\inline&space;\fn_cm&space;\large&space;\beta) is a sparsification parametetr typically chosen from interval [1.0, 2.0] (the percentage of remaining edges in the graph tends to be in the 10% – 20%) and ![](https://latex.codecogs.com/png.latex?\inline&space;\fn_cm&space;\large&space;\bar{c}) is the averaged edge length of a solution obtained by a fast heuristic. The value of ![](https://latex.codecogs.com/png.latex?\inline&space;\fn_cm&space;\large&space;\beta) is dynamically adjusted whenever the incumbent has not improved for a set number of iterations, and periodically decreased to its initial value. Neighbor solutions are obtained by performing a limited number of edge exchanges within the same route or between two routes. 183 | 184 | The authors proposed a procedure capable of examining all potential exchanges in ![](https://latex.codecogs.com/png.latex?\inline&space;\fn_cm&space;\large&space;O(|E(\nu)|)), where ![](https://latex.codecogs.com/png.latex?\inline&space;\fn_cm&space;\large&space;E(\nu)&space;=&space;\{(i,j)&space;\in&space;E&space;:&space;c_{ij}&space;\le&space;\nu\}&space;\bigcup&space;I) and ![](https://latex.codecogs.com/png.latex?\inline&space;\fn_cm&space;\large&space;I) is the set od important edges such as the inevitable edges between customers and deopts or the edged that are very likely to show up in the best edges. 185 | 186 | ## The Adaptive Memory Procedure 187 | 188 | An adaptive memory is a pool of good solutions that is dynamically updated throughout the search process. Periodically, some elements of these solutions are extracted from the pool and combined differently to produce new good solutions. When selecting these routes, care must be taken to avoid including the same customer twice in a solution. This restriction means that the selection process will often terminate with a partial solution that will have to be completed using a construction heuristic. In the example depicted in the figure below, extracting routes A, D and H from a memory of two solutions results in a partial solution. Rochat and Taillard have shown that the application of an adaptative memory procedure can enhance a search strategy. This has enabled them to obtain two new best solutions on the 14 standard VRP benchmark instances. 189 | 190 | ![](https://github.com/4342315yc/VRP-Algorithms/blob/master/Images/Adaptative_memory_demo.png) 191 | 192 | ## Kelly and Xu 193 | 194 | In this case, [Kelly and Xu 1996] considered swaps of vertices between two routes, a global repositioning of some vertices into other routes, and local routes improvements. The global repositioning strategy solves a network flow model to optimally relocate given numbers of vertices into different routes. Approximations are developed to compute the ejection and insertion costs, taking vehicle capacity into account. Route optimizations are performed by means of 3-opt exchanges. The algorithm is governed by several parameters which are dynamically adjusted through the search. A pool of best solutions is memorized and periodically used to reinitiate the search with new parameter values. Overall, this algorithm produced several best known solutions on benchmark instances, but it is fair to say that it is not as effective as some other TS implementations. It tends to require a meaty computational effort and properly tuning its many parameters can be problematic. 195 | 196 | ## TABUROUTE 197 | 198 | Original paper: [A Tabu Search Heuristic for the Vehicle Routing Problem](https://www.jstor.org/stable/pdf/2661622.pdf?refreqid=excelsior%3A00ee7932bb4ba6da610c30d55d5dcdc8) by Michel Gendreau, Alain Hertz and Gilbert Laporte. 199 | 200 | Refer to this [slides](https://imada.sdu.dk/~marco/Teaching/Fall2008/DM87/Slides/dm87-lec19-2x2.pdf) in the home page for a quick overview. The details are skipped here due to the limit of latex of Github. 201 | --------------------------------------------------------------------------------