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()