import random
class HopfieldUnit(object):
def __init__(self):
# Bullpoop default values for inputs and weights so I know what's in the
# object
self.inputs = []
self.weights = []
self.state = False
# set to True initially so that HopfieldNetwork won't falsely report
# convergence
self.has_changed = True
def update(self):
old_state = self.state
# Take weighted sum of inputs
weighted_sum = sum(u.state * w for u, w in zip(self.inputs, self.weights))
if weighted_sum >= 0:
new_state = True
else:
new_state = False
# Used to tell whether the Hopfield network has converged
if old_state == new_state:
self.has_changed = False
self.state = new_state
class HopfieldNetwork(object):
def __init__(self, n):
self.units = []
# Preserved for convenience
self.n = n
# Fill the network with n Hopfield units
for i in xrange(n):
self.units.append(HopfieldUnit())
# Weights are initially zeros--there are n, rather than n-1 weights
# for simplicity. For any unit j, its own weight is always zero and
# essentially ignored
self.units[-1].weights = [0] * n
# Make a complete graph of interconnects
for i in xrange(n):
# Tuple saves memory
# inputs = tuple([u for u, j in zip(self.units, range(n)) if j != i])
inputs = tuple(self.units)
self.units[i].inputs = inputs
def _get_state(self):
state_vector = []
for u in self.units:
state_vector.append(u.state)
return state_vector
def _set_state(self, state_vector):
for i in xrange(self.n):
self.units[i].state = state_vector[i]
state = property(_get_state, _set_state)
def _has_converged(self):
for u in self.units:
if u.has_changed == True:
return False
# Fall-through
return True
has_converged = property(_has_converged)
def train(self, pattern):
for j in xrange(self.n):
# j has j as its own input, but the weight is zero and plays no role
for i in [i for i in xrange(self.n) if i != j]:
if pattern[j] == pattern[i]:
self.units[j].weights[i] += 1
else:
self.units[j].weights[i] -= 1
def recall(self):
# run update loop until convergence
while not self.has_converged:
# update_schedule is the list that tells this method which order to
# update the Hopfield units in
update_schedule = range(self.n)
# it is randomized to simulate biological behavior
random.shuffle(update_schedule)
for i in update_schedule:
self.units[i].update()