Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow exiting the anneal early via .min_energy #49

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
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
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,8 @@ tsp.set_schedule(auto_schedule)
itinerary, miles = tsp.anneal()
```

Sometimes you have a problem for which an optimal solution exists and you can calculate its energy. One such example is the n-queens problem, where 0 queens attacking each-other is optimal. In such cases you may specify that you want the algorithm to terminate after finding the solution by setting `min_energy` member to the energy of the optimal solution.

## Extra data dependencies

You might have noticed that the `energy` function above requires a `cities` dict
Expand Down
36 changes: 36 additions & 0 deletions examples/nqueens.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import random
from simanneal import Annealer


class NQueensProblem(Annealer):
def move(self):
"""Move a queen on a random column to some different row."""
column = random.randrange(len(self.state))
new_row = random.randrange(len(self.state))
self.state[column] = new_row

def energy(self):
"""Calculates the number of attacks among the queens."""
e = 0
for i in range(len(self.state)):
for j in range(i + 1, len(self.state)):
e += self.state[i] == self.state[j]
e += abs(i - j) == abs(self.state[i] - self.state[j])
return e


if __name__ == '__main__':
init_state = list(range(10))
random.shuffle(init_state)
print(init_state)

nqueens = NQueensProblem(init_state)
nqueens.set_schedule(nqueens.auto(minutes=0.2))
# stop as soon as we hit 0 attacks
nqueens.min_energy = 0
nqueens.copy_strategy = "slice"
state, e = nqueens.anneal()

print()
print("number of attacks: %i" % e)
print(state)
5 changes: 5 additions & 0 deletions simanneal/anneal.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class Annealer(object):
Tmin = 2.5
steps = 50000
updates = 100
min_energy = None
copy_strategy = 'deepcopy'
user_exit = False
save_state_on_exit = False
Expand Down Expand Up @@ -192,6 +193,8 @@ def anneal(self):
prevEnergy = E
self.best_state = self.copy_state(self.state)
self.best_energy = E
if self.min_energy is not None and self.best_energy <= self.min_energy:
return self.best_state, self.best_energy
trials = accepts = improves = 0
if self.updates > 0:
updateWavelength = self.steps / self.updates
Expand Down Expand Up @@ -222,6 +225,8 @@ def anneal(self):
if E < self.best_energy:
self.best_state = self.copy_state(self.state)
self.best_energy = E
if self.min_energy is not None and self.best_energy <= self.min_energy:
return self.best_state, self.best_energy
if self.updates > 1:
if (step // updateWavelength) > ((step - 1) // updateWavelength):
self.update(
Expand Down