Lab 4: EC (ACO)¶
Ant colony optimisation¶
Objective¶
- to develop a Python function to perform ant colony optimisation on a problem
Problem to solve¶
We will use ant colony optimisation to solve the Nick's route-finding problem in Romania. The problem is a route finding problem to identify the best (cheapest) route to travel from Arad to Bucharest.
The road map of Romania is provided as follows:
Problem formulation¶
-
The coordinates of each town are provided as follows. This will be used later for the purpose of visualisation.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
location_list = [ # [x,y,name] [75, 125, 'Arad'], [100, 75, 'Zerind'], [125, 25, 'Oradea'], [265, 175, 'Sibiu'], [425, 175, 'Fagaras'], [320, 230, 'Rimnicu Vilcea'], [475, 310, 'Pitesti'], [350, 465, 'Craiova'], [185, 450, 'Drobeta'], [190, 390, 'Mehadia'], [185, 335, 'Lugoj'], [85, 280, 'Timisoara'], [640, 390, 'Bucharest'], [575, 485, 'Giurgiu'], [745, 340, 'Urziceni'], [875, 340, 'Hirsova'], [935, 440, 'Eforie'], [850, 225, 'Vaslui'], [760, 120, 'Iasi'], [625, 60, 'Neamt'] ] -
Then define the travel cost between connected cities.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
step_cost = [ ['Arad', 'Zerind', 75], ['Zerind', 'Oradea', 71], ['Oradea', 'Sibiu', 151], ['Sibiu', 'Arad', 140], ['Sibiu', 'Fagaras', 99], ['Sibiu', 'Rimnicu Vilcea', 80], ['Fagaras', 'Bucharest', 211], ['Bucharest', 'Giurgiu', 90], ['Bucharest', 'Pitesti', 101], ['Pitesti', 'Rimnicu Vilcea', 97], ['Rimnicu Vilcea', 'Craiova', 146], ['Craiova', 'Pitesti', 138], ['Craiova', 'Drobeta', 120], ['Drobeta', 'Mehadia', 75], ['Mehadia', 'Lugoj', 70], ['Lugoj', 'Timisoara', 111], ['Arad', 'Timisoara', 118], ['Bucharest', 'Urziceni', 85], ['Urziceni', 'Vaslui', 142], ['Vaslui', 'Iasi', 92], ['Iasi', 'Neamt', 87], ['Urziceni', 'Hirsova', 98], ['Hirsova', 'Eforie', 86] ] -
We will define two class,
CityandRoad. -
An object of class
Cityhas the attributes ofname(the name of the city),roads(an array of references to the roads connected to the current city), andcoordinates(coordinates of the cities).1 2 3 4 5 6 7 8 9 10 11 12
class City: def __init__(self, name): self.name = name self.roads = [] self.coordinates = [] def set_coordinates(self, coordinates): self.coordinates = coordinates def add_road(self, road): if road not in self.roads: self.roads.append(road) -
An object of class
Roadhas the attributes ofconnected_cities(an array of references to the cities connected through this road),cost(the step cost of this road), andpheromone(the pheromone on this road).1 2 3 4 5
class Road: def __init__(self, connected_cities, cost, pheromone=0): self.connected_cities = connected_cities self.cost = cost self.pheromone = pheromone -
We will construct the list of
Cityobjects andRoadobjects from the information provided by the question, i.e. information inlocation_listandstep_cost. The following code block should be in themaincode block.1 2 3 4 5 6 7 8 9 10
cities = {} for coord1, coord2, name in location_list: cities[name] = City(name) cities[name].set_coordinates([coord1, coord2]) roads = [] for city1, city2, cost in step_cost: road = Road([cities[city1], cities[city2]], cost) cities[city1].add_road(road) cities[city2].add_road(road) roads.append(road) -
In the
maincode block, define theoriginanddestinationcities.1 2
origin = cities['Arad'] destination = cities['Bucharest']
Initiating ACO algorithm¶
-
We then define the parameters for ACO, i.e. number of ants,
n_ant, pheromone influence constant,alpha, and evaporation rate,rho.1 2 3
n_ant = 10 alpha = 1 rho = 0.1 -
Add the method
set_pheromoneto the classRoad.1 2 3 4 5 6
class Road: def __init__(...): ... def set_pheromone(self, pheromone): self.pheromone = pheromone -
Set the initial pheromone of each road to 0.01.
1 2 3 4
# in main block initial_pheromone = 0.01 for road in roads: road.set_pheromone(initial_pheromone) -
Define the class
Ant.1 2 3 4
class Ant: def __init__(self): self.cities = [] # cities the ant passes through, in sequence self.path = [] # roads the ant uses, in sequence -
Initiate
n_antsants.1ants = [Ant() for _ in range(n_ant)]
Identify path of each ant¶
-
In the
Antclass, define a method to identify the path by taking the inputs of the available roads, the origin, the destination, and the pheromone influence constant α.1 2 3 4 5 6 7 8
class Ant: def __init__(...): ... def get_path(self, origin, destination, alpha): # 1. append origin to the self.cities # 2. if the last city is not destination, search for the next city to go # 3. after getting to the destination, remove the loop within the path, i.e. if there are repeated cities in self.cities, remove the cities and the roads in between the repetition -
Define a method to calculate the path length.
1 2 3 4 5 6 7 8 9 10
class Ant: def __init__(...): ... def get_path(...): ... def get_path_length(self): # calculate path length based on self.path return path_length -
As the path of each ant will be reset every iteration, define a method that reset the
pathandcities.1 2 3 4 5 6 7 8 9 10 11 12 13
class Ant: def __init__(...): ... def get_path(...): ... def get_path_length(...): ... def reset(self): self.path = [] self.cities = []
Evaporation¶
-
In the
Roadclass, define a method to evaporate the pheromone by taking the input of evaporation rate ρ.1 2 3 4 5 6 7 8 9
class Road: def __init__(...): ... def set_pheromone(...): ... def evaporate_pheromone(self, rho): # update the pheromone of the road
Deposition¶
-
In the
Roadclass, define a method to calculate the updated pheromone after pheromone deposition by taking the input of all the ants. We will use the following pheromone deposition formula for ant \(k\) on road \(i\):\[\Delta\tau_{i,k} = \frac{1}{L_k}\]1 2 3 4 5 6 7 8 9 10 11 12 13
class Road: def __init__(...): ... def set_pheromone(...): ... def evaporate_pheromone(...): ... def deposit_pheromone(self, ants): # 1. search for ants that uses the raod # 2. deposit pheromone using the inversely proportionate relationship between path length and deposited pheromone
Termination conditions¶
-
We will use the following conditions as the termination conditions:
- maximum iteration of 200
- if ≥90% of the ants use the same path
-
Define a function to calculate the percentage of the most dominant path.
1 2 3
def get_percentage_of_dominant_path(ants): ... return percentage
Loop until termination¶
-
Create a loop to iterate until termination.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
# termination threshold max_iteration = 200 percentage_of_dominant_path = 0.9 iteration = 0 while ...: # termination conditions # loop through all the ants to identify the path of each ant for ant in ants: # reset the path of the ant ant.reset() # identify the path of the ant ant.get_path(origin, destination, alpha) # loop through all roads for road in roads: # evaporate the pheromone on the road road.evaporate_pheromone(rho) # deposit the pheromone road.deposit_pheromone(ants) # increase iteration count iteration += 1 # after exiting the loop, return the most occurred path as the solution ...
Visualisation¶
-
Define the following functions:
1 2 3 4 5 6 7 8 9 10
import matplotlib.pyplot as plt ... def create_graph(cities): fig = plt.figure() ax = fig.add_subplot(1,1,1) cities_x = [city.coordinates[0] for key, city in cities.items()] cities_y = [city.coordinates[1] for key, city in cities.items()] ax.scatter(cities_x, cities_y) ax.set_aspect(aspect=1.0) return ax1 2 3 4 5 6 7 8 9
def draw_pheromone(ax, roads): lines = [] for road in roads: from_coord = road.connected_cities[0].coordinates to_coord = road.connected_cities[1].coordinates coord_x = [from_coord[0], to_coord[0]] coord_y = [from_coord[1], to_coord[1]] lines.append(ax.plot(coord_x, coord_y, c='k', linewidth=road.pheromone**2)) return lines -
Add the following lines to the
maincode block just before thewhileloop (loop until termination).1 2
ax = create_graph(cities) lines = draw_pheromone(ax, roads) -
Add the following lines to after
iteration += 1.1 2 3 4 5
# visualise for l in lines: del l lines = draw_pheromone(ax, roads) plt.pause(0.05)
Evaluate effect of parameters¶
-
Modify the pheromone depositing formula to
\[\Delta\tau_{i,k} = \frac{1}{L_k^{1.5}}\]What is the effect of this?
-
Modify the pheromone depositing formula to
\[\Delta\tau_{i,k} = \frac{5}{L_k}\]What is the effect of this?
-
Investigate the effect of number of ants
n_ant. -
Investigate the effect of pheromone influence constant \(\alpha\)
alpha. -
Investigate the effect of evaporation rate \(\rho\)
rho.