Skip to content

Commit

Permalink
use new style class for Annealer to support dependency injection; fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
perrygeo committed Aug 16, 2014
1 parent 4ff2ea1 commit 09c0dbb
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 5 deletions.
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,20 @@ tsp.set_schedule(auto_schedule)
itinerary, miles = tsp.anneal()
```

## Extra data dependencies

You might have noticed that the `energy` function above requires a `cities` dict
that is presumably defined in the enclosing scope. This is not necessarily a good
design pattern. The dependency on additional data can be made explicit by passing
them into the constructor like so

# pass extra data (the distance matrix) into the constructor
def __init__(self, state, distance_matrix):
self.distance_matrix = distance_matrix
super(TravellingSalesmanProblem, self).__init__(state) # important!

The last line (calling init on the super class) is critical.

## Implementation Details

The simulated annealing algorithm requires that we track state (current, previous, best) and thus means we need to copy the `self.state` frequently.
Expand Down
23 changes: 20 additions & 3 deletions examples/salesman.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ class TravellingSalesmanProblem(Annealer):

"""Test annealer with a travelling salesman problem.
"""

def __init__(self, state, distance_matrix):
self.distance_matrix = distance_matrix
super(TravellingSalesmanProblem, self).__init__(state) # important!

def move(self):
"""Swaps two cities in the route."""
Expand All @@ -28,8 +32,7 @@ def energy(self):
"""Calculates the length of the route."""
e = 0
for i in range(len(self.state)):
e += distance(cities[self.state[i - 1]],
cities[self.state[i]])
e += self.distance_matrix[self.state[i-1]][self.state[i]]
return e


Expand Down Expand Up @@ -60,9 +63,23 @@ def energy(self):
'Baltimore': (39.28, 76.62)
}

# initial state, a randomly-ordered itinerary
init_state = list(cities.keys())
random.shuffle(init_state)

tsp = TravellingSalesmanProblem(init_state)
# create a distance matrix
distance_matrix = {}
for ka, va in cities.items():
distance_matrix[ka] = {}
for kb, vb in cities.items():
if kb == ka:
distance_matrix[ka][kb] = 0.0
else:
distance_matrix[ka][kb] = distance(va, vb)

tsp = TravellingSalesmanProblem(init_state, distance_matrix)
# since our state is just a list, slice is the fastest way to copy
tsp.copy_strategy = "slice"
state, e = tsp.anneal()

while state[0] != 'New York City':
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"""

setup(name='simanneal',
version='0.1',
version='0.1.1',
description='Simulated Annealing in Python',
license='BSD',
author='Matthew Perry',
Expand Down
2 changes: 1 addition & 1 deletion simanneal/anneal.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def time_string(seconds):
return '%4i:%02i:%02i' % (h, m, s)


class Annealer:
class Annealer(object):

"""Performs simulated annealing by calling functions to calculate
energy and make moves on a state. The temperature schedule for
Expand Down

0 comments on commit 09c0dbb

Please sign in to comment.