Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 0 additions & 22 deletions README

This file was deleted.

25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Python Ant Colony TSP Solver

Uses Ant Colony Optimization to solve the TSP.
See http://en.wikipedia.org/wiki/Ant_colony_optimization_algorithms

**anttsp.py** is the file to run. It reads from citiesAndDistances.pickled, which is a pickled 2D array with this format:

| CityName1 | CityName2 | ... | CitNameN |
|-----------|-----------|-----|----------|
| 0 | 23 | ... | 34 |
| 10 | 0 | ... | 22 |
| . | . | . | . |
| . | . | . | . |

It is not necessary for the matrix to be symmetric i.e. the distance traveling from A to B need not be the distance from B to A (if you have ever been to Italy and dealt with the mess of one-way streets you will understand how this applies).

Run without additional arguments, it solves the 10-city TSP

You can also try this:
```
$ python anttsp.py 14
```

Other values <= 14 tested as well

26 changes: 19 additions & 7 deletions ant.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@
from threading import *

class Ant(Thread):
def __init__(self, ID, start_node, colony):
def __init__(self, ID, start_node, colony, logs=True):
Thread.__init__(self)
self.ID = ID
self.start_node = start_node
self.colony = colony
self.logs = logs

self.curr_node = self.start_node
self.graph = self.colony.graph
Expand Down Expand Up @@ -47,7 +48,10 @@ def run(self):
self.path_vec.append(new_node)
self.path_mat[self.curr_node][new_node] = 1 #adjacency matrix representing path

print "Ant %s : %s, %s" % (self.ID, self.path_vec, self.path_cost,)
if self.logs:
print ("Ant %s : %s, %s" % (self.ID, self.path_vec, self.path_cost))
else:
print("") # this is necessary

self.local_updating_rule(self.curr_node, new_node)
graph.lock.release()
Expand All @@ -59,7 +63,9 @@ def run(self):

# send our results to the colony
self.colony.update(self)
print "Ant thread %s terminating." % (self.ID,)

if self.logs:
print ("Ant thread %s terminating." % (self.ID,))

# allows thread to be restarted (calls Thread.__init__)
self.__init__(self.ID, self.start_node, self.colony)
Expand All @@ -74,7 +80,8 @@ def state_transition_rule(self, curr_node):
max_node = -1

if q < self.Q0:
print "Exploitation"
if self.logs:
print ("Exploitation")
max_val = -1
val = None

Expand All @@ -87,7 +94,8 @@ def state_transition_rule(self, curr_node):
max_val = val
max_node = node
else:
print "Exploration"
if self.logs:
print ("Exploration")
sum = 0
node = -1

Expand All @@ -100,12 +108,14 @@ def state_transition_rule(self, curr_node):

avg = sum / len(self.nodes_to_visit)

print "avg = %s" % (avg,)
if self.logs:
print ("avg = %s" % (avg,))

for node in self.nodes_to_visit.values():
p = graph.tau(curr_node, node) * math.pow(graph.etha(curr_node, node), self.Beta)
if p > avg:
print "p = %s" % (p,)
if self.logs:
print ("p = %s" % (p,))
max_node = node

if max_node == -1:
Expand All @@ -124,3 +134,5 @@ def local_updating_rule(self, curr_node, next_node):
val = (1 - self.Rho) * graph.tau(curr_node, next_node) + (self.Rho * graph.tau0)
graph.update_tau(curr_node, next_node, val)

def set_logs(self, logs):
self.logs = logs
25 changes: 18 additions & 7 deletions antcolony.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
import sys

class AntColony:
def __init__(self, graph, num_ants, num_iterations):
def __init__(self, graph, num_ants, num_iterations, logs=True):
self.logs = logs
self.graph = graph
self.num_ants = num_ants
self.num_iterations = num_iterations
Expand All @@ -17,7 +18,7 @@ def __init__(self, graph, num_ants, num_iterations):
self.reset()

def reset(self):
self.best_path_cost = sys.maxint
self.best_path_cost = sys.maxsize
self.best_path_vec = None
self.best_path_mat = None
self.last_best_path_iteration = 0
Expand Down Expand Up @@ -45,9 +46,15 @@ def iteration(self):
self.avg_path_cost = 0
self.ant_counter = 0
self.iter_counter += 1
print "iter_counter = %s" % (self.iter_counter,)

if self.logs:
print ("iter_counter = %s" % (self.iter_counter,))

for ant in self.ants:
print "starting ant = %s" % (ant.ID)
if self.logs:
print ("starting ant = %s" % (ant.ID))

ant.set_logs(self.logs)
ant.start()

def num_ants(self):
Expand All @@ -66,7 +73,9 @@ def update(self, ant):

#outfile = open("results.dat", "a")

print "Update called by %s" % (ant.ID,)
if self.logs:
print ("Update called by asd" % (ant.ID))

self.ant_counter += 1

self.avg_path_cost += ant.path_cost
Expand All @@ -80,7 +89,8 @@ def update(self, ant):

if self.ant_counter == len(self.ants):
self.avg_path_cost /= len(self.ants)
print "Best: %s, %s, %s, %s" % (self.best_path_vec, self.best_path_cost, self.iter_counter, self.avg_path_cost,)
if self.logs:
print ("Best: %s, %s, %s, %s" % (self.best_path_vec, self.best_path_cost, self.iter_counter, self.avg_path_cost,))
#outfile.write("\n%s\t%s\t%s" % (self.iter_counter, self.avg_path_cost, self.best_path_cost,))
self.cv.acquire()
self.cv.notify()
Expand All @@ -96,7 +106,8 @@ def create_ants(self):
self.reset()
ants = []
for i in range(0, self.num_ants):
ant = Ant(i, random.randint(0, self.graph.num_nodes - 1), self)
ant = Ant(i, random.randint(0, self.graph.num_nodes - 1), self, logs=self.logs)
ant.set_logs(self.logs)
ants.append(ant)

return ants
Expand Down
13 changes: 9 additions & 4 deletions antgraph.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
from threading import Lock

class AntGraph:
def __init__(self, num_nodes, delta_mat, tau_mat=None):
print len(delta_mat)
def __init__(self, num_nodes, delta_mat, tau_mat=None, logs=True):
self.logs = logs

if self.logs:
print (len(delta_mat))

if len(delta_mat) != num_nodes:
raise Exception("len(delta) != num_nodes")

Expand Down Expand Up @@ -41,8 +45,9 @@ def reset_tau(self):
# initial tau
self.tau0 = 1.0 / (self.num_nodes * 0.5 * avg)

print "Average = %s" % (avg,)
print "Tau0 = %s" % (self.tau0)
if self.logs:
print ("Average = %s" % (avg,))
print ("Tau0 = %s" % (self.tau0))

for r in range(0, self.num_nodes):
for s in range(0, self.num_nodes):
Expand Down
27 changes: 16 additions & 11 deletions anttsp.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,12 @@
num_iterations = 20
num_repetitions = 1

stuff = pickle.load(open("citiesAndDistances.pickled", "r"))
f = open("citiesAndDistances.pickled", "rb")
u = pickle._Unpickler(f)
u.encoding = 'latin1'
stuff = u.load()
f.close()

cities = stuff[0]
cost_mat = stuff[1]

Expand All @@ -30,12 +35,12 @@
for i in range(0, num_nodes):
cost_mat[i] = cost_mat[i][0:num_nodes]

print cost_mat
print (cost_mat)

try:
graph = AntGraph(num_nodes, cost_mat)
best_path_vec = None
best_path_cost = sys.maxint
best_path_cost = sys.maxsize
for i in range(0, num_repetitions):
graph.reset_tau()
ant_colony = AntColony(graph, num_ants, num_iterations)
Expand All @@ -44,14 +49,14 @@
best_path_vec = ant_colony.best_path_vec
best_path_cost = ant_colony.best_path_cost

print "\n------------------------------------------------------------"
print " Results "
print "------------------------------------------------------------"
print "\nBest path = %s" % (best_path_vec,)
print ("\n------------------------------------------------------------")
print (" Results ")
print ("------------------------------------------------------------")
print ("\nBest path = %s" % (best_path_vec,))
for node in best_path_vec:
print cities[node] + " ",
print "\nBest path cost = %s\n" % (best_path_cost,)
print (cities[node] + " ",)
print ("\nBest path cost = %s\n" % (best_path_cost,))

except Exception, e:
print "exception: " + str(e)
except Exception as e:
print ("exception: " + str(e))
traceback.print_exc()