File Coverage

blib/lib/AI/NeuralNet/SOM.pm
Criterion Covered Total %
statement 279 582 47.9
branch 46 176 26.1
condition 15 144 10.4
subroutine 25 34 73.5
pod 17 17 100.0
total 382 953 40.0


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2              
3             # $Id: SOM.pm, v0.01 2000/09/28
4             #
5             # Copyright (c) 2000 Alexander Voischev, Russian Federation
6             #
7             # See AUTHOR section in pod text below for usage and distribution rights.
8             #
9              
10             BEGIN {
11 1     1   543 $AI::NeuralNet::SOM::VERSION = "0.02";
12             }
13              
14             package AI::NeuralNet::SOM;
15              
16             require 5.005_62;
17 1     1   4 use strict;
  1         1  
  1         22  
18 1     1   3 use warnings;
  1         1  
  1         23  
19 1     1   5 use Carp;
  1         1  
  1         59  
20 1     1   423 use POSIX;
  1         4898  
  1         4  
21 1     1   2396 use locale;
  1         169  
  1         5  
22              
23             require Exporter;
24 1     1   492 use AutoLoader qw(AUTOLOAD);
  1         1098  
  1         4  
25              
26             our @ISA = qw(Exporter);
27              
28             our $VERSION = '0.1';
29              
30             $AI::NeuralNet::SOM::INV_ALPHA_CONSTANT = 100.0;
31              
32             #####################################################
33             #
34             # "Public" methods.
35             #
36             #####################################################
37              
38             ##################################################
39             #
40             #
41             #
42             ##################################################
43             sub new {
44 1     1 1 348 my $class = shift;
45 1         3 my $self = {};
46 1         2 bless($self, $class);
47 1         6 $self->{XDIM} = 0;
48 1         2 $self->{YDIM} = 0;
49 1         2 $self->{IDIM} = 0;
50 1         2 $self->{MAP} = ();
51 1         3 $self->clear_all_labels;
52 1         2 return $self;
53             }
54              
55             ##################################################
56             #
57             #
58             #
59             ##################################################
60             sub initialize {
61 1     1 1 566 my $self = shift;
62 1         1 my $xdim = shift; # X Dimension of map
63 1         2 my $ydim = shift; # Y Dimension of map
64 1         1 my $idim = shift; # Dimension of input data
65 1         2 my $topology = shift; # Map topolgy
66 1         1 my $neighborhood = shift; # neighborhood function type
67 1         1 my $init_type = shift; # Initialization type
68 1         1 my $random_seed = shift; # Random seed
69 1         1 my $data = shift; # Data values for initialize
70              
71             # Checking error in arguments
72 1 50       3 croak "Invalid XDim parameter" if ($xdim < 1);
73 1 50       4 croak "Invalid YDim parameter" if ($ydim < 1);
74 1 50       2 croak "Invalid IDim parameter" if ($idim < 1);
75 1 50 33     4 croak "Invalid topology parameter" if ($topology ne 'hexa') and ($topology ne 'rect');
76 1 50 33     3 croak "Invalid neighborhood parameter" if ($neighborhood ne 'bubble') and ($neighborhood ne 'gaussian');
77 1 50 33     11 croak "Invalid initialization type parameter" if ($init_type ne 'random') and ($init_type ne 'linear');
78 1 50       3 croak "Invalid Random seed parameter" if ($random_seed < 0);
79 1 50 33     6 croak "Invalid Data parameter" if ((ref($data) eq 'ARRAY') and (@$data % $idim));
80              
81 1         2 $self->{XDIM} = $xdim;
82 1         1 $self->{YDIM} = $ydim;
83 1         2 $self->{IDIM} = $idim;
84 1         2 $self->{TOPOLOGY} = $topology;
85 1         1 $self->{NEIGHBORHOOD} = $neighborhood;
86 1         3 $self->{RANDOMSEED} = $random_seed;
87              
88 1         2 my $patterns_count = @$data / $idim;
89              
90 1         2 srand($random_seed);
91 1 50       3 if ($init_type eq 'random') {
92 0         0 my @max_vals = ();
93 0         0 my @min_vals = ();
94 0         0 for my $i (0..$idim-1) {
95 0         0 my $max = 0.0 - DBL_MAX;
96 0         0 my $min = DBL_MAX;
97 0         0 for my $p (0..$patterns_count-1) {
98 0 0       0 if ($data->[$i + $p * $idim] =~ /^-?\d+\.?\d*$/ ) { # Is a real number
99 0 0       0 $max = $data->[$i + $p * $idim] if ($max < $data->[$i + $p * $idim]);
100 0 0       0 $min = $data->[$i + $p * $idim] if ($min > $data->[$i + $p * $idim]);
101             }
102             }
103 0         0 $max_vals[$i] = $max;
104 0         0 $min_vals[$i] = $min;
105             }
106              
107 0         0 my $c = 0;
108 0         0 for my $z (0..$xdim * $ydim-1) {
109 0         0 for my $i (0..$idim-1) {
110 0         0 $self->{MAP}->[$c++] = $min_vals[$i] + rand($max_vals[$i]-$min_vals[$i]);
111             }
112             }
113             }
114 1 50       3 if ($init_type eq 'linear') {
115 1         2 my $ind = 0;
116 1         1 my $eigen1;
117             my $eigen2;
118 0         0 my $mean;
119 0         0 my $xf;
120 0         0 my $yf;
121 1         4 ($eigen1, $eigen2, $mean) = $self->_find_two_eigenvectors_and_mean($data);
122            
123 1         5 for my $z (0..$self->{XDIM}*$self->{YDIM}-1) {
124 25         28 $xf = 4.0 * int($ind % $self->{XDIM}) / ($self->{XDIM} - 1.0) - 2.0;
125 25         28 $yf = 4.0 * int($ind / $self->{XDIM}) / ($self->{YDIM} - 1.0) - 2.0;
126 25         28 for my $i (0..$self->{IDIM}-1) {
127 125         173 $self->{MAP}->[$z * $self->{IDIM} + $i] = $mean->[$i] + $xf * $eigen1->[$i] + $yf * $eigen2->[$i];
128             }
129 25         29 $ind++;
130             }
131             }
132             }
133              
134             ##################################################
135             #
136             #
137             #
138             ##################################################
139             sub train {
140 1     1 1 305 my $self = shift;
141 1         3 my ($train_length, $alpha, $radius, $alpha_type, $data) = @_;
142              
143 1 50 33     9 croak "Invalid Data parameter" if ((ref($data) eq 'ARRAY') and (@$data % $self->{IDIM}));
144              
145 1         1 my ($adapt_func, $alpha_func);
146 1 50       5 if ($self->{NEIGHBORHOOD} eq 'bubble') {
147 1         2 $adapt_func = '_adapt_bubble';
148             }
149             else {
150 0         0 $adapt_func = '_adapt_gaussian';
151             }
152              
153 1 50       2 if ($alpha_type eq 'linear') {
154 1         2 $alpha_func = '_alpha_linear';
155             }
156             else {
157 0         0 $alpha_func = '_alpha_inverse_t';
158             }
159              
160 1         1 my $p = 0;
161 1         3 my $patterns_count = @$data / $self->{IDIM};
162 1         2 my ($trad, $talp);
163 0         0 my ($xwin, $ywin, $min_diff);
164              
165 0         0 my @data_slice;
166 1         3 for my $len (0..$train_length-1) {
167              
168 2000 100       2962 if (++$p == $patterns_count) {$p = 0;}
  20         22  
169              
170 2000         2397 $trad = 1.0 + ($radius - 1.0) * ($train_length - $len) / $train_length;
171 2000         3156 $talp = $self->$alpha_func($len, $train_length, $alpha);
172              
173 2000         7823 @data_slice = @$data[$p*$self->{IDIM}..($p+1)*$self->{IDIM}-1];
174 2000         3751 ($xwin, $ywin, $min_diff) = $self->winner(\@data_slice);
175              
176 2000         5089 $self->$adapt_func($xwin, $ywin, $trad, $talp, \@data_slice);
177             }
178             }
179              
180             ##################################################
181             #
182             #
183             #
184             ##################################################
185             sub qerror {
186 4     4 1 46 my $self = shift;
187 4         5 my $data = shift;
188              
189 4 50 33     31 croak "Invalid Data parameter" if ((ref($data) eq 'ARRAY') and (@$data % $self->{IDIM}));
190              
191 4         10 my $patterns_count = @$data / $self->{IDIM};
192 4         9 my ($xwin, $ywin, $min_diff);
193 4         4 my $qerror=0;
194              
195 4         5 my @data_slice;
196 4         9 for my $p (0..$patterns_count-1) {
197 400         1351 @data_slice = @$data[$p*$self->{IDIM}..($p+1)*$self->{IDIM}-1];
198 400         802 ($xwin, $ywin, $min_diff) = $self->winner(\@data_slice);
199              
200 400         458 $qerror += $min_diff;
201             }
202 4         25 return ($qerror/$patterns_count);
203             }
204              
205             ##################################################
206             #
207             #
208             #
209             ##################################################
210             sub winner {
211 2838     2838 1 7019 my $self = shift;
212 2838         2133 my $data = shift;
213              
214 2838         2156 my ($xwin, $ywin);
215 0         0 my ($diff, $difference, $masked);
216 2838         2243 my $min_diff = DBL_MAX;
217              
218 2838         4029 for my $y (0..$self->{YDIM}-1) {
219 14190         15002 for my $x (0..$self->{XDIM}-1) {
220 70950         49578 $masked = 0;
221 70950         45910 $difference = 0.0;
222 70950         71942 for my $i (0..$self->{IDIM}-1) {
223 354750 50       704573 if ($data->[$i] =~ /^-?\d+\.?\d*$/) { # Is a real number
224 354750         433297 $diff = $self->{MAP}->[($y * $self->{XDIM} + $x) * $self->{IDIM} + $i] - $data->[$i];
225 354750         377102 $difference += $diff ** 2;
226             }
227             else {
228 0         0 $masked++;
229             }
230             }
231             # If data pattern is empty
232 70950 50       109848 croak "Empty pattern" if ($masked == $self->{IDIM});
233              
234             # If distance is smaller than previous distances
235 70950 100       106858 if ($difference < $min_diff) {
236 15481         10987 $xwin = $x;
237 15481         10158 $ywin = $y;
238 15481         14834 $min_diff = $difference;
239             }
240             }
241             }
242 2838         6491 return ($xwin, $ywin, sqrt($min_diff));
243             }
244              
245             ##################################################
246             #
247             #
248             #
249             ##################################################
250             sub set_label {
251 246     246 1 195 my $self = shift;
252 246         270 my ($x, $y, $label) = @_;
253 246 50 33     2201 croak "Invalid argument" if (($x<0) or ($x>=$self->{XDIM}) or ($y<0) or ($y>=$self->{YDIM}) or not defined($label));
      33        
      33        
      33        
254 246         811 $self->{LABELS}->[$y * $self->{XDIM} + $x] =$label;
255             }
256              
257             ##################################################
258             #
259             #
260             #
261             ##################################################
262             sub clear_all_labels {
263 1     1 1 2 my $self = shift;
264 1         3 $self->{LABELS} = ();
265             }
266              
267             ##################################################
268             #
269             #
270             #
271             ##################################################
272             sub save {
273 1     1 1 404 my $self = shift;
274 1         4 my $file = shift;
275              
276 1         3 my $xdim = $self->{XDIM};
277 1         2 my $ydim = $self->{YDIM};
278 1         2 my $idim = $self->{IDIM};
279 1         2 my $topol = $self->{TOPOLOGY};
280 1         2 my $neigh = $self->{NEIGHBORHOOD};
281 1         5 print $file "# Created by SOM.pm v0.01 by Voischev Alexander e-mail: voischev\@mail.ru\n";
282 1         17 print $file "$idim $topol $xdim $ydim $neigh\n";
283              
284 1         6 for my $z (0..$self->{XDIM}*$self->{YDIM}-1) {
285 25         49 for my $i (0..$self->{IDIM}-1) {
286 125         163 my $value = $self->{MAP}->[$z * $self->{IDIM} + $i];
287 125         377 print $file "$value ";
288             }
289 25         36 my $label = $self->{LABELS}->[$z];
290 25 100       38 print $file "$label" if (defined($label));
291 25         35 print $file "\n";
292             }
293             }
294              
295             ##################################################
296             #
297             #
298             #
299             ##################################################
300             sub load {
301 1     1 1 83 my $self = shift;
302 1         3 my $file = shift;
303 1         1 my $header;
304              
305 1         9 while (<$file>) {
306 2 100       11 if (!/^ *#/) {
307 1         4 s/^ *//;
308 1         2 $header = $_;
309 1         3 last;
310             }
311             }
312 1         2 chomp($header);
313 1         4 my ($idim, $topology, $xdim, $ydim, $neighborhood) = split(/ /, $header);
314              
315 1 50       4 croak "Invalid XDim parameter" if ($xdim < 1);
316 1 50       8 croak "Invalid YDim parameter" if ($ydim < 1);
317 1 50       2 croak "Invalid IDim parameter" if ($idim < 1);
318 1 50 33     4 croak "Invalid topology parameter" if ($topology ne 'hexa') and ($topology ne 'rect');
319 1 50 33     5 croak "Invalid neighborhood parameter" if ($neighborhood ne 'bubble') and ($neighborhood ne 'gaussian');
320              
321 1         2 $self->{XDIM} = $xdim;
322 1         1 $self->{YDIM} = $ydim;
323 1         2 $self->{IDIM} = $idim;
324 1         1 $self->{TOPOLOGY} = $topology;
325 1         2 $self->{NEIGHBORHOOD} = $neighborhood;
326              
327 1         2 my @pattern;
328             my @line;
329 0         0 my @data;
330 1         2 my $z = 0;
331 1         10 while (<$file>) {
332 25 50       39 if (!/^ *#/) {
333 25         20 chomp();
334 25 50       28 if (/#/) {
335 0         0 @line = split(/ /,$`);
336             }
337             else {
338 25         64 @line = split(/ /);
339             }
340 25         48 @pattern = splice(@line, 0, $self->{IDIM});
341 25         35 push (@data, @pattern);
342 25 100       33 if (defined($line[0])) {
343 15         15 $self->{LABELS}->[$z] = $line[0];
344             }
345 25         43 $z++;
346             }
347             }
348 1         6 $self->{MAP} = \@data;
349             }
350              
351             ##################################################
352             #
353             #
354             #
355             ##################################################
356             sub umatrix {
357 0     0 1 0 my $self = shift;
358 0         0 my ($i,$j,$k,$count,$bx,$by,$bz);
359 0         0 my ($dx,$dy,$dz1,$dz2,$dz,$temp,$max,$min,$bw);
360 0         0 my @medtable;
361 0         0 my $tmp;
362              
363 0         0 my @umat = ();
364              
365 0 0 0     0 if ($self->{XDIM}<=0 or $self->{YDIM}<=0 or $self->{IDIM}<=0) {
      0        
366 0         0 return undef;
367             }
368 0         0 $max = 0;
369 0         0 $min = 0;
370              
371 0 0       0 if ($self->{TOPOLOGY} eq 'rect') {
372             # Rectangular topology
373 0         0 for $j (0..$self->{YDIM}-1) {
374 0         0 for $i (0..$self->{XDIM}-1) {
375 0         0 $dx=0; $dy=0; $dz1=0; $dz2=0; $count=0;
  0         0  
  0         0  
  0         0  
  0         0  
376 0         0 $bx=0; $by=0; $bz=0;
  0         0  
  0         0  
377 0         0 for $k (0..$self->{IDIM}-1) {
378 0 0       0 if ($i < $self->{XDIM}-1) {
379 0         0 $temp = $self->map($i,$j,$k) - $self->map($i+1,$j,$k);
380 0         0 $dx += $temp ** 2;
381 0         0 $bx = 1;
382             }
383 0 0       0 if ($j < $self->{YDIM}-1) {
384 0         0 $temp = $self->map($i,$j,$k) - $self->map($i,$j+1,$k);
385 0         0 $dy += $temp ** 2;
386 0         0 $by = 1;
387             }
388 0 0 0     0 if ($j < $self->{YDIM}-1 and $i < $self->{XDIM}-1) {
389 0         0 $temp = $self->map($i,$j,$k) - $self->map($i+1,$j+1,$k);
390 0         0 $dz1 += $temp ** 2;
391 0         0 $temp = $self->map($i,$j+1,$k) - $self->map($i+1,$j,$k);
392 0         0 $dz2 += $temp ** 2;
393 0         0 $bz=1;
394             }
395             }
396 0         0 $dz = (sqrt($dz1)/sqrt(2.0)+sqrt($dz2)/sqrt(2.0))/2;
397            
398 0 0       0 if ($bx) {
399 0         0 $umat[2*$i+1+(2*$j)*($self->{XDIM}*2-1)] = sqrt($dx);
400             }
401 0 0       0 if ($by) {
402 0         0 $umat[2*$i+(2*$j+1)*($self->{XDIM}*2-1)] = sqrt($dy);
403             }
404 0 0       0 if ($bz) {
405 0         0 $umat[2*$i+1+(2*$j+1)*($self->{XDIM}*2-1)] = $dz;
406             }
407             }
408             }
409             }
410             else {
411             # Hexagonal topology
412 0         0 for $j (0..$self->{YDIM}-1) {
413 0         0 for $i (0..$self->{XDIM}-1) {
414 0         0 $dx=0; $dy=0; $dz=0; $count=0;
  0         0  
  0         0  
  0         0  
415 0         0 $bx=0; $by=0; $bz=0;
  0         0  
  0         0  
416 0         0 $temp=0;
417 0 0       0 if ($i<$self->{XDIM}-1)
418             {
419 0         0 for $k (0..$self->{IDIM}-1) {
420 0         0 $temp = $self->map($i,$j,$k) - $self->map($i+1,$j,$k);
421 0         0 $dx += $temp ** 2;
422 0         0 $bx = 1;
423             }
424             }
425 0         0 $temp=0;
426 0 0       0 if ($j < $self->{YDIM}-1) {
427 0 0       0 if ($j%2) {
428 0         0 for $k (0..$self->{IDIM}-1)
429             {
430 0         0 $temp = $self->map($i,$j,$k) - $self->map($i,$j+1,$k);
431 0         0 $dy += $temp ** 2;
432 0         0 $by=1;
433             }
434             }
435             else {
436 0 0       0 if ($i>0) {
437 0         0 for $k (0..$self->{IDIM}-1) {
438 0         0 $temp = $self->map($i,$j,$k) - $self->map($i-1,$j+1,$k);
439 0         0 $dy += $temp ** 2;
440 0         0 $by=1;
441             }
442             }
443             else {
444 0         0 $temp=0;
445             }
446             }
447             }
448 0         0 $temp=0;
449 0 0       0 if ($j < $self->{YDIM}-1) {
450 0 0       0 if (!($j%2)) {
451 0         0 for $k (0..$self->{IDIM}-1) {
452 0         0 $temp = $self->map($i,$j,$k) - $self->map($i,$j+1,$k);
453 0         0 $dz += $temp ** 2;
454             }
455 0         0 $bz=1;
456             }
457             else {
458 0 0       0 if ($i < $self->{XDIM}-1) {
459 0         0 for $k (0..$self->{IDIM}-1) {
460 0         0 $temp = $self->map($i,$j,$k) - $self->map($i+1,$j+1,$k);
461 0         0 $dz += $temp ** 2;
462             }
463 0         0 $bz=1;
464             }
465             }
466             }
467             else {
468 0         0 $temp=0;
469             }
470            
471 0 0       0 if ($bx) {
472 0         0 $umat[2*$i+1+(2*$j)*($self->{XDIM}*2-1)] = sqrt($dx);
473             }
474 0 0       0 if ($by) {
475 0 0       0 if ($j%2) {
476 0         0 $umat[2*$i+(2*$j+1)*($self->{XDIM}*2-1)] = sqrt($dy);
477             }
478             else {
479 0         0 $umat[2*$i-1+(2*$j+1)*($self->{XDIM}*2-1)] = sqrt($dy);
480             }
481             }
482 0 0       0 if ($bz) {
483 0 0       0 if ($j%2) {
484 0         0 $umat[2*$i+1+(2*$j+1)*($self->{XDIM}*2-1)] = sqrt($dz);
485             }
486             else {
487 0         0 $umat[2*$i+(2*$j+1)*($self->{XDIM}*2-1)] = sqrt($dz);
488             }
489             }
490             }
491             }
492             }
493            
494             # Set the values corresponding to the model vectors themselves
495             # to medians of the surrounding values
496 0 0       0 if ($self->{TOPOLOGY} eq 'rect') {
497             # Rectangular topology
498             # medians of the 4-neighborhood
499 0         0 for ($j=0; $j<$self->{YDIM} * 2 - 1; $j+=2) {
500 0         0 for ($i=0;$i<$self->{XDIM} * 2 - 1; $i+=2) {
501 0 0 0     0 if($i>0 and $j>0 and $i<$self->{XDIM} * 2 - 2 and $j<$self->{YDIM} * 2 - 2) {
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
      0        
      0        
      0        
      0        
      0        
      0        
502             # in the middle of the map
503 0         0 $medtable[0] = $umat[$i-1+$j*($self->{XDIM}*2-1)];
504 0         0 $medtable[1] = $umat[$i+1+$j*($self->{XDIM}*2-1)];
505 0         0 $medtable[2] = $umat[$i+($j-1)*($self->{XDIM}*2-1)];
506 0         0 $medtable[3] = $umat[$i+($j+1)*($self->{XDIM}*2-1)];
507 0         0 $#medtable = 3;
508 0         0 @medtable = sort{$a <=> $b} @medtable;
  0         0  
509             # Actually mean of two median values
510 0         0 $umat[$i+$j*($self->{XDIM}*2-1)]=($medtable[1]+$medtable[2])/2.0;
511             }
512             elsif($j==0 and $i>0 and $i<$self->{XDIM} * 2 - 2) {
513             # in the upper edge
514 0         0 $medtable[0]=$umat[$i-1+$j*($self->{XDIM}*2-1)];
515 0         0 $medtable[1]=$umat[$i+1+$j*($self->{XDIM}*2-1)];
516 0         0 $medtable[2]=$umat[$i+($j+1)*($self->{XDIM}*2-1)];
517 0         0 $#medtable = 2;
518 0         0 @medtable = sort{$a<=>$b} @medtable;
  0         0  
519 0         0 $umat[$i+$j*($self->{XDIM}*2-1)]=$medtable[1];
520             }
521             elsif($j==$self->{YDIM} * 2 - 2 and $i>0 and $i<$self->{XDIM} * 2 - 2) {
522             # in the lower edge
523 0         0 $medtable[0]=$umat[$i-1+$j*($self->{XDIM}*2-1)];
524 0         0 $medtable[1]=$umat[$i+1+$j*($self->{XDIM}*2-1)];
525 0         0 $medtable[2]=$umat[$i+($j-1)*($self->{XDIM}*2-1)];
526 0         0 $#medtable = 2;
527 0         0 @medtable = sort{$a<=>$b} @medtable;
  0         0  
528 0         0 $umat[$i+$j*($self->{XDIM}*2-1)]=$medtable[1];
529             }
530             elsif($i==0 and $j>0 and $j<$self->{YDIM} * 2 - 2) {
531             # in the left edge
532 0         0 $medtable[0]=$umat[$i+1+$j*($self->{XDIM}*2-1)];
533 0         0 $medtable[1]=$umat[$i+($j-1)*($self->{XDIM}*2-1)];
534 0         0 $medtable[2]=$umat[$i+($j+1)*($self->{XDIM}*2-1)];
535 0         0 $#medtable = 2;
536 0         0 @medtable = sort{$a<=>$b} @medtable;
  0         0  
537 0         0 $umat[$i+$j*($self->{XDIM}*2-1)]=$medtable[1];
538             }
539             elsif($i==$self->{XDIM} * 2 - 2 and $j>0 and $j<$self->{YDIM} * 2 - 2) {
540             # in the right edge
541 0         0 $medtable[0]=$umat[$i-1+$j*($self->{XDIM}*2-1)];
542 0         0 $medtable[1]=$umat[$i+($j-1)*($self->{XDIM}*2-1)];
543 0         0 $medtable[2]=$umat[$i+($j+1)*($self->{XDIM}*2-1)];
544 0         0 $#medtable = 2;
545 0         0 @medtable = sort{$a<=>$b} @medtable;
  0         0  
546 0         0 $umat[$i+$j*($self->{XDIM}*2-1)]=$medtable[1];
547             }
548             elsif($i==0 && $j==0) {
549             # the upper left-hand corner
550 0         0 $umat[$i+$j*($self->{XDIM}*2-1)]=($umat[$i+1+$j*($self->{XDIM}*2-1)]+$umat[$i+($j+1)*($self->{XDIM}*2-1)])/2.0;
551             }
552             elsif($i==$self->{XDIM} * 2 - 2 and $j==0) {
553             # the upper right-hand corner
554 0         0 $umat[$i+$j*($self->{XDIM}*2-1)]=($umat[$i-1+$j*($self->{XDIM}*2-1)]+$umat[$i+($j+1)*($self->{XDIM}*2-1)])/2.0;
555             }
556             elsif($i==0 and $j==$self->{YDIM} * 2 - 2) {
557             # the lower left-hand corner
558 0         0 $umat[$i+$j*($self->{XDIM}*2-1)]=($umat[$i+1+$j*($self->{XDIM}*2-1)]+$umat[$i+($j-1)*($self->{XDIM}*2-1)])/2.0;
559             }
560             elsif($i==$self->{XDIM} * 2 - 2 and $j==$self->{YDIM} * 2 - 2) {
561             # the lower right-hand corner
562 0         0 $umat[$i+$j*($self->{XDIM}*2-1)]=($umat[$i-1+$j*($self->{XDIM}*2-1)]+$umat[$i+($j-1)*($self->{XDIM}*2-1)])/2.0;
563             }
564             }
565             }
566             }
567             else {
568             # Hexagonal topology
569 0         0 for ($j=0; $j<$self->{YDIM}*2-1; $j+=2) {
570 0         0 for ($i=0; $i<$self->{XDIM}*2-1; $i+=2) {
571 0 0 0     0 if($i>0 and $j>0 and $i<$self->{XDIM} * 2 - 2 and $j<$self->{YDIM} * 2 - 2) {
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
      0        
      0        
      0        
      0        
      0        
      0        
572             # in the middle of the map
573 0         0 $medtable[0]=$umat[$i-1+$j*($self->{XDIM}*2-1)];
574 0         0 $medtable[1]=$umat[$i+1+$j*($self->{XDIM}*2-1)];
575 0 0       0 if(!($j%4)) {
576 0         0 $medtable[2]=$umat[$i-1+($j-1)*($self->{XDIM}*2-1)];
577 0         0 $medtable[3]=$umat[$i+($j-1)*($self->{XDIM}*2-1)];
578 0         0 $medtable[4]=$umat[$i-1+($j+1)*($self->{XDIM}*2-1)];
579 0         0 $medtable[5]=$umat[$i+($j+1)*($self->{XDIM}*2-1)];
580             }
581             else {
582 0         0 $medtable[2]=$umat[$i+($j-1)*($self->{XDIM}*2-1)];
583 0         0 $medtable[3]=$umat[$i+1+($j-1)*($self->{XDIM}*2-1)];
584 0         0 $medtable[4]=$umat[$i+($j+1)*($self->{XDIM}*2-1)];
585 0         0 $medtable[5]=$umat[$i+1+($j+1)*($self->{XDIM}*2-1)];
586             }
587 0         0 $#medtable = 5;
588 0         0 @medtable = sort{$a<=>$b} @medtable;
  0         0  
589             # Actually mean of two median values
590 0         0 $umat[$i+$j*($self->{XDIM}*2-1)]=($medtable[2]+$medtable[3])/2;
591             }
592             elsif($j==0 and $i>0 and $i<$self->{XDIM} * 2 - 2) {
593             # in the upper edge
594 0         0 $medtable[0]=$umat[$i-1+$j*($self->{XDIM}*2-1)];
595 0         0 $medtable[1]=$umat[$i+1+$j*($self->{XDIM}*2-1)];
596 0         0 $medtable[2]=$umat[$i+($j+1)*($self->{XDIM}*2-1)];
597 0         0 $medtable[3]=$umat[$i-1+($j+1)*($self->{XDIM}*2-1)];
598 0         0 $#medtable = 3;
599 0         0 @medtable = sort{$a<=>$b} @medtable;
  0         0  
600             # Actually mean of two median values
601 0         0 $umat[$i+$j*($self->{XDIM}*2-1)]=($medtable[1]+$medtable[2])/2;
602             }
603             elsif($j==$self->{YDIM} * 2 - 2 and $i>0 and $i<$self->{XDIM} * 2 - 2) {
604             # in the lower edge
605 0         0 $medtable[0]=$umat[$i-1+$j*($self->{XDIM}*2-1)];
606 0         0 $medtable[1]=$umat[$i+1+$j*($self->{XDIM}*2-1)];
607 0 0       0 if(!($j%4)) {
608 0         0 $medtable[2]=$umat[$i-1+($j-1)*($self->{XDIM}*2-1)];
609 0         0 $medtable[3]=$umat[$i+($j-1)*($self->{XDIM}*2-1)];
610             }
611             else {
612 0         0 $medtable[2]=$umat[$i+($j-1)*($self->{XDIM}*2-1)];
613 0         0 $medtable[3]=$umat[$i+1+($j-1)*($self->{XDIM}*2-1)];
614             }
615 0         0 $#medtable = 3;
616 0         0 @medtable = sort{$a<=>$b} @medtable;
  0         0  
617             # Actually mean of two median values
618 0         0 $umat[$i+$j*($self->{XDIM}*2-1)]=($medtable[1]+$medtable[2])/2;
619             }
620             elsif($i==0 and $j>0 and $j<$self->{YDIM} * 2 - 2) {
621             # in the left edge
622 0         0 $medtable[0]=$umat[$i+1+$j*($self->{XDIM}*2-1)];
623 0 0       0 if(!($j%4)) {
624 0         0 $medtable[1]=$umat[$i+($j-1)*($self->{XDIM}*2-1)];
625 0         0 $medtable[2]=$umat[$i+($j+1)*($self->{XDIM}*2-1)];
626 0         0 $#medtable = 2;
627 0         0 @medtable = sort{$a<=>$b} @medtable;
  0         0  
628 0         0 $umat[$i+$j*($self->{XDIM}*2-1)]=$medtable[1];
629             }
630             else {
631 0         0 $medtable[1]=$umat[$i+($j-1)*($self->{XDIM}*2-1)];
632 0         0 $medtable[2]=$umat[$i+1+($j-1)*($self->{XDIM}*2-1)];
633 0         0 $medtable[3]=$umat[$i+($j+1)*($self->{XDIM}*2-1)];
634 0         0 $medtable[4]=$umat[$i+1+($j+1)*($self->{XDIM}*2-1)];
635 0         0 $#medtable = 4;
636 0         0 @medtable = sort{$a<=>$b} @medtable;
  0         0  
637 0         0 $umat[$i+$j*($self->{XDIM}*2-1)]=$medtable[2];
638             }
639             }
640             elsif($i==$self->{XDIM} * 2 - 2 and $j>0 and $j<$self->{YDIM} * 2 - 2) {
641             # in the right edge
642 0         0 $medtable[0]=$umat[$i-1+$j*($self->{XDIM}*2-1)];
643 0 0       0 if($j%4) {
644 0         0 $medtable[1]=$umat[$i+($j-1)*($self->{XDIM}*2-1)];
645 0         0 $medtable[2]=$umat[$i+($j+1)*($self->{XDIM}*2-1)];
646 0         0 $#medtable = 2;
647 0         0 @medtable = sort{$a<=>$b} @medtable;
  0         0  
648 0         0 $umat[$i+$j*($self->{XDIM}*2-1)]=$medtable[1];
649             }
650             else {
651 0         0 $medtable[1]=$umat[$i+($j-1)*($self->{XDIM}*2-1)];
652 0         0 $medtable[2]=$umat[$i-1+($j-1)*($self->{XDIM}*2-1)];
653 0         0 $medtable[3]=$umat[$i+($j+1)*($self->{XDIM}*2-1)];
654 0         0 $medtable[4]=$umat[$i-1+($j+1)*($self->{XDIM}*2-1)];
655 0         0 $#medtable = 4;
656 0         0 @medtable = sort{$a<=>$b} @medtable;
  0         0  
657 0         0 $umat[$i+$j*($self->{XDIM}*2-1)]=$medtable[2];
658             }
659             }
660             elsif($i==0 and $j==0) {
661             # the upper left-hand corner
662 0         0 $umat[$i+$j*($self->{XDIM}*2-1)]=($umat[$i+1+$j*($self->{XDIM}*2-1)]+$umat[$i+($j+1)*($self->{XDIM}*2-1)])/2.0;
663             }
664             elsif($i==$self->{XDIM} * 2 - 2 and $j==0) {
665             # the upper right-hand corner
666 0         0 $medtable[0]=$umat[$i-1+$j*($self->{XDIM}*2-1)];
667 0         0 $medtable[1]=$umat[$i-1+($j+1)*($self->{XDIM}*2-1)];
668 0         0 $medtable[2]=$umat[$i+($j+1)*($self->{XDIM}*2-1)];
669 0         0 $#medtable = 2;
670 0         0 @medtable = sort{$a<=>$b} @medtable;
  0         0  
671 0         0 $umat[$i+$j*($self->{XDIM}*2-1)]=$medtable[1];
672             }
673             elsif($i==0 and $j==$self->{YDIM} * 2 - 2) {
674             # the lower left-hand corner
675 0 0       0 if(!($j%4)) {
676 0         0 $umat[$i+$j*($self->{XDIM}*2-1)]=($umat[$i+1+$j*($self->{XDIM}*2-1)]+$umat[$i+($j-1)*($self->{XDIM}*2-1)])/2.0;
677             }
678             else {
679 0         0 $medtable[0]=$umat[$i+1+$j*($self->{XDIM}*2-1)];
680 0         0 $medtable[1]=$umat[$i+($j-1)*($self->{XDIM}*2-1)];
681 0         0 $medtable[2]=$umat[$i+1+($j-1)*($self->{XDIM}*2-1)];
682 0         0 $#medtable = 2;
683 0         0 @medtable = sort{$a<=>$b} @medtable;
  0         0  
684 0         0 $umat[$i+$j*($self->{XDIM}*2-1)]=$medtable[1];
685             }
686             }
687             elsif($i==$self->{XDIM} * 2 - 2 and $j==$self->{YDIM} * 2 - 2) {
688             # the lower right-hand corner
689 0 0       0 if($j%4) {
690 0         0 $umat[$i+$j*($self->{XDIM}*2-1)]=($umat[$i-1+$j*($self->{XDIM}*2-1)]+$umat[$i+($j-1)*($self->{XDIM}*2-1)])/2.0;
691             }
692             else {
693 0         0 $medtable[0]=$umat[$i-1+$j*($self->{XDIM}*2-1)];
694 0         0 $medtable[1]=$umat[$i+($j-1)*($self->{XDIM}*2-1)];
695 0         0 $medtable[2]=$umat[$i-1+($j-1)*($self->{XDIM}*2-1)];
696 0         0 $#medtable = 2;
697 0         0 @medtable = sort{$a<=>$b} @medtable;
  0         0  
698 0         0 $umat[$i+$j*($self->{XDIM}*2-1)]=$medtable[1];
699             }
700             }
701             }
702             }
703             }
704              
705             # scale values to (0..1)
706              
707 0         0 my @umat_sort = sort{$a<=>$b} @umat;
  0         0  
708 0         0 $bw = $umat_sort[$#umat_sort] - $umat_sort[0];
709 0         0 $min = $umat_sort[0];
710 0         0 for $i (0..$self->{XDIM} * 2 - 2) {
711 0         0 for $j (0..$self->{YDIM} * 2 - 2) {
712 0         0 $umat[$i+$j*($self->{XDIM}*2-1)] = ($umat[$i+$j*($self->{XDIM}*2-1)]-$min)/$bw;
713             }
714             }
715              
716 0         0 return \@umat;
717             }
718              
719             ##################################################
720             #
721             #
722             #
723             ##################################################
724              
725             sub x_dim {
726 0     0 1 0 my $self = shift;
727 0         0 return $self->{XDIM};
728             }
729              
730             sub y_dim {
731 0     0 1 0 my $self = shift;
732 0         0 return $self->{YDIM};
733             }
734              
735             sub i_dim {
736 246     246 1 910 my $self = shift;
737 246         510 return $self->{IDIM};
738             }
739              
740             sub topology {
741 0     0 1 0 my $self = shift;
742 0         0 return $self->{TOPOLOGY};
743             }
744              
745             sub neighborhood {
746 0     0 1 0 my $self = shift;
747 0         0 return $self->{NEIGHBORHOOD};
748             }
749              
750             sub map {
751 0     0 1 0 my $self = shift;
752 0         0 my ($x, $y, $z) = @_;
753 0         0 return $self->{MAP}->[($y*$self->{XDIM} + $x) * $self->{IDIM} + $z];
754             }
755              
756             sub label {
757 192     192 1 590 my $self = shift;
758 192         168 my ($x, $y) = @_;
759 192 50 33     1266 croak "Invalid argument" if (($x<0) or ($x>=$self->{XDIM}) or ($y<0) or ($y>=$self->{YDIM}));
      33        
      33        
760 192         361 return $self->{LABELS}->[$y * $self->{XDIM} + $x];
761             }
762              
763             ################################################################################
764             #
765             # "Private" methods.
766             #
767             ################################################################################
768              
769             sub _find_two_eigenvectors_and_mean {
770 1     1   1 my $self = shift;
771 1         2 my $data = shift;
772              
773 1         1 my $k = 0;
774 1         15 my $i = 0;
775 1         2 my $j = 0;
776              
777 1         2 my $n = $self->{IDIM};
778              
779 1         2 my @r = ();
780 1         1 my @m = ();
781 1         1 my @u = ();
782 1         2 my @v = ();
783 1         1 my @k2 = ();
784            
785 1         2 my @mu = ();
786 1         2 my $patterns_count = @$data / $n;
787              
788 1         4 for ($k=0; $k<$patterns_count; $k++) {
789 100         123 for ($i=0; $i<$n; $i++) {
790 500 50       1047 if ($data->[$i + $k * $n] =~ /^-?\d+\.?\d*$/) { # Is a real number
791 500         594 $m[$i] += $data->[$i + $k * $n];
792 500         740 $k2[$i]++;
793             }
794             }
795             }
796            
797 1         3 $i = 0;
798 1         2 foreach my $k2item (@k2) {
799 5         6 $m[$i++] /= $k2item;
800             }
801              
802 1         4 for ($k=0; $k < $patterns_count; $k++) {
803 100         120 for ($i=0; $i<$n; $i++) {
804 500 50       973 if ($data->[$k * $n + $i] =~ /^-?\d+\.?\d*$/) { # Is a real number
805 500         609 for ($j=$i; $j<$n; $j++) {
806 1500 50       2846 if ($data->[$k * $n + $j] =~ /^-?\d+\.?\d*$/) { # Is a real number
807 1500         3336 $r[$i * $n + $j] += ($data->[$k * $n + $i] - $m[$i]) * ($data->[$k * $n + $j] - $m[$j]);
808             }
809             }
810             }
811             }
812             }
813              
814 1         5 for ($i=0; $i<$n; $i++) {
815 5         13 for ($j=$i; $j<$n; $j++) {
816 15         26 $r[$j * $n + $i]=$r[$i * $n + $j] /= $patterns_count;
817             }
818             }
819              
820 1         3 for ($i=0; $i<2; $i++) {
821 2         4 for ($j=0; $j<$n; $j++) {
822 10         22 $u[$i*$n+$j] = rand(2) - 1.0;
823             }
824              
825 2         8 $self->_normalize(\@u, $i * $n);
826 2         5 $mu[$i] = 1.0;
827             }
828              
829 1         9 for ($k=0; $k<10; $k++) {
830 10         13 for ($i=0; $i<2; $i++) {
831 20         25 for ($j=0; $j<$n; $j++) {
832 100         125 $v[$i * $n + $j] = $mu[$i] * $self->_inner_product(\@r, $j*$n, \@u, $i*$n, $n) + $u[$i * $n + $j];
833             }
834             }
835              
836 10         13 $self->_gram_schmidt(\@v, $n, 2);
837 10         9 my $sum = 0.0;
838            
839 10         15 for ($i=0; $i<2; $i++) {
840 20         23 for ($j=0; $j<$n; $j++) {
841 100         143 $sum += abs($v[$i * $n + $j] / $self->_inner_product(\@r, $j*$n, \@v, $i*$n, $n));
842             }
843 20         31 $mu[$i] = $sum / $n;
844             }
845 10         23 @u = @v;
846             }
847              
848 1         6 my @eigen1 = ();
849 1         2 my @eigen2 = ();
850              
851 1         3 for ($j=0; $j<$n; $j++) {
852 5         9 $eigen1[$j] = $u[$j] / sqrt($mu[0]);
853             }
854 1         10 for ($j=0; $j<$n; $j++) {
855 5         9 $eigen2[$j] = $u[$j+$n] / sqrt($mu[1]);
856             }
857              
858 1         5 return \(@eigen1, @eigen2, @m)
859             }
860              
861             sub _inner_product {
862 222     222   145 my $self = shift;
863 222         155 my ($data1, $start_index1, $data2, $start_index2, $count) = @_;
864              
865 222         150 my $sum = 0;
866 222         263 for (my $i=0; $i<$count; $i++) {
867 1110         1676 $sum += $data1->[$start_index1++] * $data2->[$start_index2++]
868             }
869 222         423 return $sum;
870             }
871              
872             sub _normalize {
873 22     22   21 my $self = shift;
874 22         9 my $data = shift;
875 22         16 my $start_index = shift;
876              
877 22         26 my $sum = sqrt($self->_inner_product($data, $start_index, $data, $start_index, $self->{IDIM}));
878              
879 22         37 for (my $i=$start_index; $i<$start_index + $self->{IDIM}; $i++) {
880 110         180 $data->[$i] /= $sum;
881             }
882             }
883              
884             sub _gram_schmidt {
885 10     10   9 my $self = shift;
886 10         7 my $data = shift;
887 10         7 my $n = shift;
888 10         6 my $e = shift;
889              
890 10         9 my @w = ();
891 10         6 my $sum = 0;
892              
893 10         15 for (my $i=0; $i<$e; $i++) {
894 20         26 for (my $t=0; $t<$n; $t++) {
895 100         89 $sum = $data->[$i * $n + $t];
896 100         129 for (my $j=0; $j<$i; $j++) {
897 50         57 for (my $p=0; $p<$n; $p++) {
898 250         464 $sum -= $w[$j * $n + $t] * $w[$j * $n + $p] * $data->[$i * $n + $p];
899             }
900             }
901 100         157 $w[$i * $n + $t] = $sum;
902             }
903 20         23 $self->_normalize(\@w, $i * $n);
904             }
905 10         28 @$data = @w;
906             }
907              
908             sub _get_hexa_dist {
909 50000     50000   39893 my $self = shift;
910 50000         38775 my ($bx, $by, $tx, $ty) = @_;
911              
912 50000         35114 my $diff = $bx - $tx;
913              
914 50000 100       65538 if ((($by - $ty) % 2) != 0) {
915 23005 100       24242 if (($by % 2) == 0) {
916 13990         10947 $diff -= 0.5;
917             }
918             else {
919 9015         7101 $diff += 0.5;
920             }
921             }
922            
923 50000         43328 my $temp = $diff ** 2;
924 50000         33071 $diff = $by - $ty;
925 50000         43734 $temp += 0.75 * $diff ** 2;
926 50000         110286 return sqrt($temp);
927             }
928              
929             sub _get_rect_dist {
930 0     0   0 my $self = shift;
931 0         0 my ($bx, $by, $tx, $ty) = @_;
932              
933 0         0 my $diff = $bx - $tx;
934 0         0 my $temp = $diff ** 2;
935 0         0 $diff = $by - $ty;
936 0         0 $temp += $diff ** 2;
937 0         0 return sqrt($temp);
938             }
939              
940             sub _adapt_bubble {
941 2000     2000   1695 my $self = shift;
942 2000         2038 my ($bx, $by, $radius, $alpha, $data) = @_;
943              
944 2000         1367 my $dist_func;
945 2000 50       3023 if ($self->{TOPOLOGY} eq 'rect') {
946 0         0 $dist_func = "_get_rect_dist";
947             }
948             else {
949 2000         1800 $dist_func = "_get_hexa_dist";
950             }
951              
952 2000         3773 for(my $x=0; $x<$self->{XDIM}; $x++) {
953 10000         14495 for(my $y=0; $y<$self->{YDIM}; $y++) {
954 50000 100       64004 if ($self->$dist_func($bx, $by, $x, $y) <= $radius) {
955 27667         39760 for (my $i=0; $i<$self->{IDIM}; $i++) {
956 138335 50       284688 if ($data->[$i] =~ /^-?\d+\.?\d*$/) { # Is a real number
957 138335         379267 $self->{MAP}->[($y * $self->{XDIM} + $x) * $self->{IDIM} + $i] +=
958             $alpha * ($data->[$i] - $self->{MAP}->[($y * $self->{XDIM} + $x) * $self->{IDIM} + $i]);
959             }
960             }
961             }
962             }
963             }
964             }
965              
966             sub _adapt_gaussian {
967 0     0   0 my $self = shift;
968 0         0 my ($bx, $by, $radius, $alpha, $data) = @_;
969              
970 0         0 my $dist_func;
971 0 0       0 if ($self->{TOPOLOGY} eq 'rect') {
972 0         0 $dist_func = "_get_rect_dist";
973             }
974             else {
975 0         0 $dist_func = "_get_hexa_dist";
976             }
977              
978 0         0 for(my $x=0; $x<$self->{XDIM}; $x++) {
979 0         0 for(my $y=0; $y<$self->{YDIM}; $y++) {
980 0         0 my $dd = $self->$dist_func($bx, $by, $x, $y);
981 0         0 my $alp = $alpha * exp(-$dd ** 2 / (2.0 * $radius ** 2)); # -$dd**2 - ????
982 0         0 for (my $i=0; $i<$self->{IDIM}; $i++) {
983 0 0       0 if ($data->[$i] =~ /^-?\d+\.?\d*$/) { # Is a real number
984 0         0 $self->{MAP}->[($y * $self->{XDIM} + $x) * $self->{IDIM} + $i] +=
985             $alp * ($data->[$i] - $self->{MAP}->[($y * $self->{XDIM} + $x) * $self->{IDIM} + $i]);
986             }
987             }
988             }
989             }
990             }
991              
992             sub _alpha_linear {
993 2000     2000   1634 my $self = shift;
994 2000         1782 my ($iter, $epoches, $alpha) = @_;
995 2000         2647 return $alpha * ($epoches - $iter) / $epoches;
996             }
997              
998             sub _alpha_inverse_t {
999 0     0     my $self = shift;
1000 0           my ($iter, $epoches, $alpha) = @_;
1001 0           my $c = $epoches / $AI::NeuralNet::SOM::INV_ALPHA_CONSTANT;
1002 0           return $alpha * $c /($c + $iter) / $epoches;
1003             }
1004              
1005             1;
1006              
1007             __END__