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:Δτi,k = 1/Lk1 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
Δτi,k = 1/Lk1.5What is the effect of this?
-
Modify the pheromone depositing formula to
Δτi,k = 5/LkWhat is the effect of this?
-
Investigate the effect of number of ants
n_ant. -
Investigate the effect of pheromone influence constant α
alpha. -
Investigate the effect of evaporation rate ρ
rho.