I need to figure out how to modify this code so that I can read this data on a text file named "TSP_6.txt". It's a TSP problem using Ant Colony Optimization This is the format I'm using:TSP_6.txt.
nbr_destinations 6
coordinates:
430 105
488 93
186 124
151 226
367 403
179 460
And this is my current code, the code three classes, the main: "main.py"
import math
from aco import ACO, Graph
from plot import plot
def distance(city1: dict, city2: dict):
return math.sqrt((city1['x'] - city2['x']) ** 2 + (city1['y'] - city2['y']) ** 2)
def main():
cities = []
points = []
with open('./data/chn31.txt') as f:
for line in f.readlines():
city = line.split(' ')
cities.append(dict(index=int(city[0]), x=int(city[1]), y=int(city[2])))
points.append((int(city[1]), int(city[2])))
cost_matrix = []
rank = len(cities)
for i in range(rank):
row = []
for j in range(rank):
row.append(distance(cities[i], cities[j]))
cost_matrix.append(row)
aco = ACO(10, 100, 1.0, 10.0, 0.5, 10, 2)
graph = Graph(cost_matrix, rank)
path, cost = aco.solve(graph)
print('cost: {}, path: {}'.format(cost, path))
plot(points, path)
if __name__ == '__main__':
main()
And the Ant Colony Optimization class: "aco.py"
import random
class Graph(object):
def __init__(self, cost_matrix: list, rank: int):
"""
:param cost_matrix:
:param rank: rank of the cost matrix
"""
self.matrix = cost_matrix
self.rank = rank
# noinspection PyUnusedLocal
self.pheromone = [[1 / (rank * rank) for j in range(rank)] for i in range(rank)]
class ACO(object):
def __init__(self, ant_count: int, generations: int, alpha: float, beta: float, rho: float, q: int,
strategy: int):
"""
:param ant_count:
:param generations:
:param alpha: relative importance of pheromone
:param beta: relative importance of heuristic information
:param rho: pheromone residual coefficient
:param q: pheromone intensity
:param strategy: pheromone update strategy. 0 - ant-cycle, 1 - ant-quality, 2 - ant-density
"""
self.Q = q
self.rho = rho
self.beta = beta
self.alpha = alpha
self.ant_count = ant_count
self.generations = generations
self.update_strategy = strategy
def _update_pheromone(self, graph: Graph, ants: list):
for i, row in enumerate(graph.pheromone):
for j, col in enumerate(row):
graph.pheromone[i][j] *= self.rho
for ant in ants:
graph.pheromone[i][j] += ant.pheromone_delta[i][j]
# noinspection PyProtectedMember
def solve(self, graph: Graph):
"""
:param graph:
"""
best_cost = float('inf')
best_solution = []
for gen in range(self.generations):
# noinspection PyUnusedLocal
ants = [_Ant(self, graph) for i in range(self.ant_count)]
for ant in ants:
for i in range(graph.rank - 1):
ant._select_next()
ant.total_cost += graph.matrix[ant.tabu[-1]][ant.tabu[0]]
if ant.total_cost < best_cost:
best_cost = ant.total_cost
best_solution = [] + ant.tabu
# update pheromone
ant._update_pheromone_delta()
self._update_pheromone(graph, ants)
# print('generation #{}, best cost: {}, path: {}'.format(gen, best_cost, best_solution))
return best_solution, best_cost
class _Ant(object):
def __init__(self, aco: ACO, graph: Graph):
self.colony = aco
self.graph = graph
self.total_cost = 0.0
self.tabu = [] # tabu list
self.pheromone_delta = [] # the local increase of pheromone
self.allowed = [i for i in range(graph.rank)] # nodes which are allowed for the next selection
self.eta = [[0 if i == j else 1 / graph.matrix[i][j] for j in range(graph.rank)] for i in
range(graph.rank)] # heuristic information
start = random.randint(0, graph.rank - 1) # start from any node
self.tabu.append(start)
self.current = start
self.allowed.remove(start)
def _select_next(self):
denominator = 0
for i in self.allowed:
denominator += self.graph.pheromone[self.current][i] ** self.colony.alpha * self.eta[self.current][
i] ** self.colony.beta
# noinspection PyUnusedLocal
probabilities = [0 for i in range(self.graph.rank)] # probabilities for moving to a node in the next step
for i in range(self.graph.rank):
try:
self.allowed.index(i) # test if allowed list contains i
probabilities[i] = self.graph.pheromone[self.current][i] ** self.colony.alpha * \
self.eta[self.current][i] ** self.colony.beta / denominator
except ValueError:
pass # do nothing
# select next node by probability roulette
selected = 0
rand = random.random()
for i, probability in enumerate(probabilities):
rand -= probability
if rand <= 0:
selected = i
break
self.allowed.remove(selected)
self.tabu.append(selected)
self.total_cost += self.graph.matrix[self.current][selected]
self.current = selected
# noinspection PyUnusedLocal
def _update_pheromone_delta(self):
self.pheromone_delta = [[0 for j in range(self.graph.rank)] for i in range(self.graph.rank)]
for _ in range(1, len(self.tabu)):
i = self.tabu[_ - 1]
j = self.tabu[_]
if self.colony.update_strategy == 1: # ant-quality system
self.pheromone_delta[i][j] = self.colony.Q
elif self.colony.update_strategy == 2: # ant-density system
# noinspection PyTypeChecker
self.pheromone_delta[i][j] = self.colony.Q / self.graph.matrix[i][j]
else: # ant-cycle system
self.pheromone_delta[i][j] = self.colony.Q / self.total_cost
And finally the plot class: "plot.py"
import operator
import matplotlib.pyplot as plt
def plot(points, path: list):
x = []
y = []
for point in points:
x.append(point[0])
y.append(point[1])
# noinspection PyUnusedLocal
y = list(map(operator.sub, [max(y) for i in range(len(points))], y))
plt.plot(x, y, 'co')
for _ in range(1, len(path)):
i = path[_ - 1]
j = path[_]
# noinspection PyUnresolvedReferences
plt.arrow(x[i], y[i], x[j] - x[i], y[j] - y[i], color='r', length_includes_head=True)
# noinspection PyTypeChecker
plt.xlim(0, max(x) * 1.1)
# noinspection PyTypeChecker
plt.ylim(0, max(y) * 1.1)
plt.show()
The input file can be slightly modified as it only needs the coordinates. There is no need to have the
nbr_destinations
orCoordiantes
header. If you still want them, you can easily extent the conditional inside the for loop to read/check for them. The code below does not assume this lines. For example:The code below stores the
nbr_destinations
in a variable that can be used to initialise the index of the city. Also your code can be slightly modified to read the coordinate values: