simanneal
simanneal copied to clipboard
Avoid copies if it is possible to undo a move
For states with complex objects, copies can take a significant bulk of the time spent by the algorithm. This pull requests add optional support for defining def undo_move(self)
method on the class, which the user can implement to undo the last move, almost avoiding the copying in the body of the annealing.
For demonstration purposes consider the following (admittedly convoluted) example:
import random
from simanneal import Annealer
class Queen:
def __init__(self, row):
self.row = row
self.dummy1 = set(range(12))
self.dummy2 = {i:i for i in range(15)}
self.dummy3 = {i:i for i in range(20)}
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.last_move = (column, self.state[column].row)
self.state[column].row = new_row
def undo_move(self):
self.state[self.last_move[0]].row = self.last_move[1]
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].row == self.state[j].row
e += abs(i - j) == abs(self.state[i].row - self.state[j].row)
return e
if __name__ == '__main__':
nqueens = NQueensProblem([Queen(row) for row in range(20)])
nqueens.Tmax = 100
nqueens.Tmin = 0.1
nqueens.steps = 500000
state, e = nqueens.anneal()
print()
print(f"number of attacks: {e}")
print([s.row for s in state])
On my pc, this code runs approximately 20x slower when I comment out undo_move
(3 minutes vs 10 seconds).