Skip to content

Commit

Permalink
Allow exiting the anneal early via .min_energy
Browse files Browse the repository at this point in the history
  • Loading branch information
adolenc committed Apr 7, 2024
1 parent 951e7d8 commit 11ab86f
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 0 deletions.
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
39 changes: 39 additions & 0 deletions examples/nqueens.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import random
from simanneal import Annealer


class NQueensProblem(Annealer):
def __init__(self, state):
super(NQueensProblem, self).__init__(state) # important!

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

0 comments on commit 11ab86f

Please sign in to comment.