File Coverage

blib/lib/AI/NNFlex/Hopfield.pm
Criterion Covered Total %
statement 108 109 99.0
branch 6 8 75.0
condition n/a
subroutine 9 9 100.0
pod 2 4 50.0
total 125 130 96.1


line stmt bran cond sub pod time code
1             ####################################################
2             # AI::NNFlex::Hopfield
3             ####################################################
4             # Hopfield network simulator
5             ####################################################
6             #
7             # Version history
8             # ===============
9             #
10             # 1.0 20050330 CColbourn New module
11             #
12             ####################################################
13             package AI::NNFlex::Hopfield;
14              
15 1     1   672 use strict;
  1         2  
  1         35  
16 1     1   617 use AI::NNFlex;
  1         2  
  1         35  
17 1     1   9 use AI::NNFlex::Mathlib;
  1         1  
  1         32  
18 1     1   1073 use Math::Matrix;
  1         13058  
  1         42  
19 1     1   12 use base qw(AI::NNFlex AI::NNFlex::Mathlib);
  1         31  
  1         1080  
20              
21             ####################################################
22             # AI::NNFlex::Hopfield::init
23             ####################################################
24             #
25             # The hopfield network has connections from every
26             # node to every other node, rather than being
27             # arranged in distinct layers like a feedforward
28             # network. We can retain the layer architecture to
29             # give us blocks of nodes, but need to overload init
30             # to perform full connections
31             #
32             #####################################################
33             sub init
34             {
35              
36 1     1 1 8 my $network = shift;
37 1         3 my @nodes;
38              
39             # Get a list of all the nodes in the network
40 1         8 foreach my $layer (@{$network->{'layers'}})
  1         3  
41             {
42 2         3 foreach my $node (@{$layer->{'nodes'}})
  2         4  
43             {
44             # cover the assumption that some inherited code
45             # will require an activation function
46 4 50       11 if (!$node->{'activationfunction'})
47             {
48 4         7 $node->{'activationfunction'}= 'hopfield_threshold';
49 4         7 $node->{'activation'} =0;
50 4         7 $node->{'lastactivation'} = 0;
51             }
52 4         9 push @nodes,$node;
53             }
54             }
55              
56             # we'll probably need this later
57 1         2 $network->{'nodes'} = \@nodes;
58              
59 1         3 foreach my $node (@nodes)
60             {
61 4         6 my @connectedNodes;
62 4         6 foreach my $connectedNode (@nodes)
63             {
64 16         23 push @connectedNodes,$connectedNode;
65             }
66 4         6 my @weights;
67 4         16 $node->{'connectednodes'}->{'nodes'} = \@connectedNodes;
68 4         9 for (0..(scalar @nodes)-1)
69             {
70 16         44 push @weights,$network->calcweight();
71             }
72 4         13 $node->{'connectednodes'}->{'weights'} = \@weights
73             }
74              
75 1         3 return 1;
76              
77             }
78              
79             ##########################################################
80             # AI::NNFlex::Hopfield::run
81             ##########################################################
82             # apply activation patterns & calculate activation
83             # through the network
84             ##########################################################
85             sub run
86             {
87 1     1 1 7 my $network = shift;
88              
89 1         2 my $inputPatternRef = shift;
90              
91 1         4 my @inputpattern = @$inputPatternRef;
92              
93 1 50       2 if (scalar @inputpattern != scalar @{$network->{'nodes'}})
  1         5  
94             {
95 0         0 return "Error: input pattern does not match number of nodes"
96             }
97              
98             # apply the pattern to the network
99 1         2 my $counter=0;
100 1         2 foreach my $node (@{$network->{'nodes'}})
  1         3  
101             {
102 4         8 $node->{'activation'} = $inputpattern[$counter];
103 4         6 $counter++;
104             }
105              
106             # Now update the network with activation flow
107 1         49 foreach my $node (@{$network->{'nodes'}})
  1         5  
108             {
109 4         8 $node->{'activation'}=0;
110 4         6 my $counter=0;
111 4         7 foreach my $connectedNode (@{$node->{'connectednodes'}->{'nodes'}})
  4         9  
112             {
113             # hopfield nodes don't have recursive connections
114 16 100       40 unless ($node == $connectedNode)
115             {
116 12         27 $node->{'activation'} += $connectedNode->{'activation'} * $node->{'connectednodes'}->{'weights'}->[$counter];
117              
118             }
119 16         30 $counter++;
120             }
121              
122              
123             # bias
124 4         11 $node->{'activation'} += 1 * $node->{'connectednodes'}->{'weights'}->[-1];
125              
126 4         7 my $activationfunction = $node->{'activationfunction'};
127 4         21 $node->{'activation'} = $network->$activationfunction($node->{'activation'});
128              
129             }
130              
131 1         5 return $network->output;
132             }
133              
134             #######################################################
135             # AI::NNFlex::Hopfield::output
136             #######################################################
137             # This needs to be overloaded, because the default
138             # nnflex output method returns only the rightmost layer
139             #######################################################
140             sub output
141             {
142 1     1 0 2 my $network = shift;
143              
144 1         2 my @array;
145 1         1 foreach my $node (@{$network->{'nodes'}})
  1         4  
146             {
147 4         9 unshift @array,$node->{'activation'};
148             }
149              
150 1         5 return \@array;
151             }
152              
153             ########################################################
154             # AI::NNFlex::Hopfield::learn
155             ########################################################
156             sub learn
157             {
158 1     1 0 238 my $network = shift;
159              
160 1         2 my $dataset = shift;
161              
162             # calculate the weights
163             # turn the dataset into a matrix
164 1         2 my @matrix;
165 1         2 foreach (@{$dataset->{'data'}})
  1         4  
166             {
167 2         5 push @matrix,$_;
168             }
169 1         11 my $patternmatrix = Math::Matrix->new(@matrix);
170              
171 1         33 my $inversepattern = $patternmatrix->transpose;
172              
173 1         78 my @minusmatrix;
174              
175 1         3 for (my $rows=0;$rows <(scalar @{$network->{'nodes'}});$rows++)
  5         18  
176             {
177 4         5 my @temparray;
178 4         7 for (my $cols=0;$cols <(scalar @{$network->{'nodes'}});$cols++)
  20         53  
179             {
180 16 100       32 if ($rows == $cols)
181             {
182 4         6 my $numpats = scalar @{$dataset->{'data'}};
  4         8  
183 4         9 push @temparray,$numpats;
184             }
185             else
186             {
187 12         25 push @temparray,0;
188             }
189             }
190 4         10 push @minusmatrix,\@temparray;
191             }
192              
193 1         9 my $minus = Math::Matrix->new(@minusmatrix);
194              
195 1         37 my $product = $inversepattern->multiply($patternmatrix);
196              
197 1         244 my $weights = $product->subtract($minus);
198              
199 1         223 my @element = ('1');
200 1         2 my @truearray;
201 1         2 for (1..scalar @{$dataset->{'data'}}){push @truearray,"1"}
  1         4  
  2         6  
202            
203 1         6 my $truematrix = Math::Matrix->new(\@truearray);
204              
205 1         21 my $thresholds = $truematrix->multiply($patternmatrix);
206             #$thresholds = $thresholds->transpose();
207              
208 1         93 my $counter=0;
209 1         2 foreach (@{$network->{'nodes'}})
  1         3  
210             {
211 4         6 my @slice;
212 4         5 foreach (@{$weights->slice($counter)})
  4         14  
213             {
214 16         169 push @slice,$$_[0];
215             }
216              
217 4         13 push @slice,${$thresholds->slice($counter)}[0][0];
  4         12  
218              
219 4         121 $_->{'connectednodes'}->{'weights'} = \@slice;
220 4         12 $counter++;
221             }
222              
223 1         12 return 1;
224              
225             }
226              
227              
228              
229             1;
230              
231             =pod
232              
233             =head1 NAME
234              
235             AI::NNFlex::Hopfield - a fast, pure perl Hopfield network simulator
236              
237             =head1 SYNOPSIS
238              
239             use AI::NNFlex::Hopfield;
240              
241             my $network = AI::NNFlex::Hopfield->new(config parameter=>value);
242              
243             $network->add_layer(nodes=>x);
244              
245             $network->init();
246              
247              
248              
249             use AI::NNFlex::Dataset;
250              
251             my $dataset = AI::NNFlex::Dataset->new([
252             [INPUTARRAY],
253             [INPUTARRAY]]);
254              
255             $network->learn($dataset);
256              
257             my $outputsRef = $dataset->run($network);
258              
259             my $outputsRef = $network->output();
260              
261             =head1 DESCRIPTION
262              
263             AI::NNFlex::Hopfield is a Hopfield network simulator derived from the AI::NNFlex class. THIS IS THE FIRST ALPHA CUT OF THIS MODULE! Any problems, let me know and I'll fix them.
264              
265             Hopfield networks differ from feedforward networks in that they are effectively a single layer, with all nodes connected to all other nodes (except themselves), and are trained in a single operation. They are particularly useful for recognising corrupt bitmaps etc. I've left the multi layer architecture in this module (inherited from AI::NNFlex) for convenience of visualising 2d bitmaps, but effectively its a single layer.
266              
267             Full documentation for AI::NNFlex::Dataset can be found in the modules own perldoc. It's documented here for convenience only.
268              
269             =head1 CONSTRUCTOR
270              
271             =head2 AI::NNFlex::Hopfield->new();
272              
273             =head2 AI::NNFlex::Dataset
274              
275             new ( [[INPUT VALUES],[INPUT VALUES],
276             [INPUT VALUES],[INPUT VALUES],..])
277              
278             =head2 INPUT VALUES
279              
280             These should be comma separated values. They can be applied to the network with ::run or ::learn
281              
282             =head2 OUTPUT VALUES
283            
284             These are the intended or target output values. Comma separated. These will be used by ::learn
285              
286              
287             =head1 METHODS
288              
289             This is a short list of the main methods implemented in AI::NNFlex::Hopfield.
290              
291             =head2 AI::NNFlex::Hopfield
292              
293             =head2 add_layer
294              
295             Syntax:
296              
297             $network->add_layer( nodes=>NUMBER OF NODES IN LAYER );
298              
299             =head2 init
300              
301             Syntax:
302              
303             $network->init();
304              
305             Initialises connections between nodes.
306              
307             =head2 run
308              
309             $network->run($dataset)
310              
311             Runs the dataset through the network and returns a reference to an array of output patterns.
312              
313             =head1 EXAMPLES
314              
315             See the code in ./examples.
316              
317              
318             =head1 PREREQs
319              
320             Math::Matrix
321              
322             =head1 ACKNOWLEDGEMENTS
323              
324             =head1 SEE ALSO
325              
326             AI::NNFlex
327             AI::NNFlex::Backprop
328              
329              
330             =head1 TODO
331              
332             More detailed documentation. Better tests. More examples.
333              
334             =head1 CHANGES
335              
336             v0.1 - new module
337              
338             =head1 COPYRIGHT
339              
340             Copyright (c) 2004-2005 Charles Colbourn. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
341              
342             =head1 CONTACT
343              
344             charlesc@nnflex.g0n.net
345              
346              
347              
348             =cut