from network import * class HopfieldNetwork(Network): '''A simple Hopfield network. The single real layer is treated as an output layer.''' def __init__(self, name, size, lr=.1): '''Create an input layer and one output layer of the same size.''' Network.__init__(self, name, [Layer('input', size=size), HopfieldLayer('output', size=size)], supervised=False) self.competitive = False self.settling = True self.has_bias = False self.lr = lr def step(self, pattern, train=True, show_act=False, lr=.1): '''Run the network on one pattern, returning the error and [] (winner for CompetNetwork).''' # Error is really energy error = 0.0 # Clamp the input layer and output layer to the pattern. self.layers[0].clamp(pattern) self.layers[1].clamp(pattern) # If not training, update all of the other units in the network if not train: self.update() # Figure the error after settling error = self.figure_error() else: # Update weights self.update_weights(lr=self.lr) if show_act: self.show() return error, [] def update(self): '''Update all of the units in the network other than the input layer.''' any_changes = True iteration = 0 while any_changes: iteration += 1 for layer in self.layers[1:]: any_changes = layer.update() def figure_error(self): return 0.0 class HopfieldLayer(Layer): '''Output Layer for a HopfieldNetwork.''' def __init__(self, name, size=10, lr=.1): Layer.__init__(self, name, size=size, has_bias=False, labels=[], lr=lr, linear=True) def initialize_weights(self): '''Create the weights list of lists: a weight connecting each pair of units. Indices: [dest-index][input-unit-index] ''' self.weights = [ [0.0 for i in range(self.size)] for j in range(self.size) ] def gen_random_acts(self): '''Generate random activations.''' return [(1 if random.random() > .5 else -1) for u in range(self.size)] def update(self): '''Update all the units once in random order.''' self.temp_acts = self.activations[:] indices = range(self.size) random.shuffle(indices) any_changes = False for i in indices: act_i = self.activations[i] input_i = self.get_input(i) if input_i >= 0: self.activations[i] = 1 else: self.activations[i] = -1 if self.activations[i] != act_i: any_changes = True return any_changes def get_input(self, dest_i): '''Get input into unit dest_i (no bias).''' return dot_product(self.activations, self.weights[dest_i]) def learn(self, lr=1.0): '''Add lr * the product of source and destination activations to the weight.''' for i in range(self.size-1): act_i = self.activations[i] for j in range(i, self.size): weight_change = lr * act_i * self.activations[j] self.weights[i][j] += weight_change # Enforce symmetry self.weights[j][i] += weight_change