File Coverage

blib/lib/AI/ANN/Neuron.pm
Criterion Covered Total %
statement 8 10 80.0
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 12 14 85.7


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2             package AI::ANN::Neuron;
3             BEGIN {
4 4     4   27645 $AI::ANN::Neuron::VERSION = '0.008';
5             }
6             # ABSTRACT: a neuron for an artificial neural network simulator
7              
8 4     4   38 use strict;
  4         9  
  4         275  
9 4     4   22 use warnings;
  4         10  
  4         127  
10              
11 4     4   2013 use Moose;
  0            
  0            
12             use Inline C => <<'END_C';
13              
14             double _execute_internals ( AV* inputs, AV* neurons, AV* inputweights, AV* neuronweights ) {
15             double output = 0.0;
16             int i;
17             int v1 = av_len(inputweights);
18             int v2 = av_len(inputs);
19             if (v2 < v1) {
20             v1 = v2;
21             }
22             if (v1 >= 0) {
23             for (i=0; i<=v1; i++) {
24             SV** val = av_fetch(inputs, i, 0);
25             SV** weight = av_fetch(inputweights, i, 0);
26             output += SvNV(*val) * SvNV(*weight);
27             }
28             }
29             v1 = av_len(neuronweights);
30             v2 = av_len(neurons);
31             if (v2 < v1) {
32             v1 = v2;
33             }
34             if (v1 >= 0) {
35             for (i=0; i<=v1; i++) {
36             SV** val = av_fetch(neurons, i, 0);
37             SV** weight = av_fetch(neuronweights, i, 0);
38             output += SvNV(*val) * SvNV(*weight);
39             }
40             }
41             return output;
42             }
43              
44             END_C
45              
46              
47             has 'id' => (is => 'rw', isa => 'Int');
48             has 'inputs' => (is => 'rw', isa => 'ArrayRef', required => 1);
49             has 'neurons' => (is => 'rw', isa => 'ArrayRef', required => 1);
50             has 'eta_inputs' => (is => 'rw', isa => 'ArrayRef');
51             has 'eta_neurons' => (is => 'rw', isa => 'ArrayRef');
52             has 'inline_c' => (is => 'ro', isa => 'Int', required => 1, default => 1);
53              
54             around BUILDARGS => sub {
55             my $orig = shift;
56             my $class = shift;
57             my %data;
58             if ( @_ >= 2 && ref $_[0] && ref $_[1]) {
59             %data = ('inputs' => $_[0], 'neurons' => $_[1]);
60             $data{'eta_inputs'} = $_[2] if defined $_[2];
61             $data{'eta_neurons'} = $_[3] if defined $_[3];
62             } elsif ( @_ >= 3 && ref $_[1] && ref $_[2]) {
63             %data = ('id' => $_[0], 'inputs' => $_[1], 'neurons' => $_[2]);
64             $data{'eta_inputs'} = $_[3] if defined $_[3];
65             $data{'eta_neurons'} = $_[4] if defined $_[4];
66             } elsif ( @_ == 1 && ref $_[0] eq 'HASH' ) {
67             %data = %{$_[0]};
68             } else {
69             %data = @_;
70             }
71             if (ref $data{'inputs'} eq 'HASH') {
72             my @temparray;
73             foreach my $i (keys %{$data{'inputs'}}) {
74             if (defined $data{'inputs'}->{$i} && $data{'inputs'}->{$i} != 0) {
75             $temparray[$i]=$data{'inputs'}->{$i};
76             }
77             }
78             $data{'inputs'}=\@temparray;
79             }
80             if (ref $data{'neurons'} eq 'HASH') {
81             my @temparray;
82             foreach my $i (keys %{$data{'neurons'}}) {
83             if (defined $data{'neurons'}->{$i} && $data{'neurons'}->{$i} != 0) {
84             $temparray[$i]=$data{'neurons'}->{$i};
85             }
86             }
87             $data{'neurons'}=\@temparray;
88             }
89             if (defined $data{'eta_inputs'} && ref $data{'eta_inputs'} eq 'HASH') {
90             my @temparray;
91             foreach my $i (keys %{$data{'eta_inputs'}}) {
92             if (defined $data{'eta_inputs'}->{$i} && $data{'eta_inputs'}->{$i} != 0) {
93             $temparray[$i]=$data{'eta_inputs'}->{$i};
94             }
95             }
96             $data{'eta_inputs'}=\@temparray;
97             }
98             if (defined $data{'eta_neurons'} && ref $data{'eta_neurons'} eq 'HASH') {
99             my @temparray;
100             foreach my $i (keys %{$data{'eta_neurons'}}) {
101             if (defined $data{'eta_neurons'}->{$i} && $data{'eta_neurons'}->{$i} != 0) {
102             $temparray[$i]=$data{'eta_neurons'}->{$i};
103             }
104             }
105             $data{'eta_neurons'}=\@temparray;
106             }
107             foreach my $i (0..$#{$data{'inputs'}}) {
108             $data{'inputs'}->[$i] ||= 0;
109             }
110             foreach my $i (0..$#{$data{'neurons'}}) {
111             $data{'neurons'}->[$i] ||= 0;
112             }
113             foreach my $i (0..$#{$data{'eta_inputs'}}) {
114             $data{'eta_inputs'}->[$i] ||= 0;
115             }
116             foreach my $i (0..$#{$data{'eta_neurons'}}) {
117             $data{'eta_neurons'}->[$i] ||= 0;
118             }
119             return $class->$orig(%data);
120             };
121              
122              
123             sub ready {
124             my $self = shift;
125             my $inputs = shift;
126             my $neurons = shift;
127             if (ref $neurons eq 'HASH') {
128             my @temparray;
129             foreach my $i (keys %$neurons) {
130             if (defined $neurons->{$i} && $neurons->{$i} != 0) {
131             $temparray[$i]=$neurons->{$i};
132             }
133             }
134             $neurons=\@temparray;
135             }
136             my @inputs = @$inputs;
137             my @neurons = @$neurons;
138              
139             foreach my $id (0..$#{$self->{'inputs'}}) {
140             unless ((not defined $self->{'inputs'}->[$id]) ||
141             $self->{'inputs'}->[$id] == 0 || defined $inputs[$id])
142             {return 0}
143             # This probably shouldn't ever happen, as it would be weird if our
144             # inputs weren't available yet.
145             }
146             foreach my $id (0..$#{$self->{'neurons'}}) {
147             unless ((not defined $self->{'neurons'}->[$id]) ||
148             $self->{'neurons'}->[$id] == 0 || defined $neurons[$id])
149             {return 0}
150             }
151             return 1;
152             }
153              
154              
155             sub execute {
156             my $self = shift;
157             my $inputs = shift;
158             my $neurons = shift;
159             if (ref $neurons eq 'HASH') {
160             my @temparray;
161             foreach my $i (keys %$neurons) {
162             $temparray[$i]=$neurons->{$i} || 0;
163             }
164             $neurons=\@temparray;
165             }
166             my @inputs = @$inputs;
167             my @neurons = @$neurons;
168             my @inputweights = @{$self->{'inputs'}};
169             my @neuronweights = @{$self->{'neurons'}};
170             # foreach my $i (0..$#inputs) {
171             # $inputs[$i] ||= 0;
172             # }
173             # foreach my $i (0..$#neurons) {
174             # $neurons[$i] ||= 0;
175             # }
176             # if ($#inputs < $#inputweights) {
177             # foreach my $i ($#inputs+1..$#inputweights) {
178             # $inputs[$i]=0;
179             # }
180             # }
181             # if ($#neurons < $#neuronweights) {
182             # foreach my $i ($#neurons+1..$#neuronweights) {
183             # $neurons[$i]=0;
184             # }
185             # }
186             #print STDERR $self->{'id'}."\n";
187             #print STDERR join(',', @inputs)."\n";
188             #print STDERR join(',', @neurons)."\n";
189             #print STDERR join(',', @inputweights)."\n";
190             #print STDERR join(',', @neuronweights)."\n";
191             my $output = 0;
192             if ($self->{'inline_c'}) {
193             $output = _execute_internals( \@inputs, \@neurons, \@inputweights, \@neuronweights );
194             } else {
195             foreach my $id (0..$#inputweights) {
196             $output += ($inputweights[$id] || 0 ) * ($inputs[$id] || 0);
197             }
198             foreach my $id (0..$#neuronweights) {
199             $output += ($neuronweights[$id] || 0) * ($neurons[$id] || 0);
200             }
201             }
202             return $output;
203             }
204              
205             __PACKAGE__->meta->make_immutable;
206              
207             1;
208              
209              
210             __END__
211             =pod
212              
213             =head1 NAME
214              
215             AI::ANN::Neuron - a neuron for an artificial neural network simulator
216              
217             =head1 VERSION
218              
219             version 0.008
220              
221             =head1 METHODS
222              
223             =head2 new
224              
225             AI::ANN::Neuron->new( $neuronid, {$inputid => $weight, ...}, {$neuronid => $weight} )
226              
227             Weights may be whatever the user chooses. Note that packages that use this
228             one may place their own restructions. Neurons and inputs are assumed to be
229             zero-indexed.
230              
231             eta_inputs and eta_neurons are optional, required only if you wish to use the
232             Gaussian mutation in AI::ANN::Evolver.
233              
234             =head2 ready
235              
236             $neuron->ready( [$input0, $input1, ...], [$neuronvalue0, ...] )
237              
238             All inputs must be provided or you're insane.
239             If a neuron is not yet available, make it undef, not zero.
240             Returns 1 if ready, 0 otherwise.
241              
242             =head2 execute
243              
244             $neuron->execute( [$input0, $input1, ...], {$neuronid => $neuronvalue, ...} )
245              
246             You /must/ pass the correct number of inputs and neurons, and undefined values
247             /must/ be zeros, not undef.
248             Returns raw value (linear potential)
249              
250             =head1 AUTHOR
251              
252             Dan Collins <DCOLLINS@cpan.org>
253              
254             =head1 COPYRIGHT AND LICENSE
255              
256             This software is Copyright (c) 2011 by Dan Collins.
257              
258             This is free software, licensed under:
259              
260             The GNU General Public License, Version 3, June 2007
261              
262             =cut
263