File Coverage

blib/lib/AI/NeuralNet/Kohonen/Visual.pm
Criterion Covered Total %
statement 16 18 88.8
branch n/a
condition n/a
subroutine 6 6 100.0
pod n/a
total 22 24 91.6


line stmt bran cond sub pod time code
1             package AI::NeuralNet::Kohonen::Visual;
2            
3 1     1   3402 use vars qw/$VERSION/;
  1         3  
  1         168  
4             $VERSION = 0.3; # 05 May 2006 pod and packaging
5            
6             =head1 NAME
7            
8             AI::NeuralNet::Kohonen::Visual - Tk-based Visualisation
9            
10             =head1 SYNOPSIS
11            
12             Test the test file in this distribution, or:
13            
14             package YourClass;
15             use base "AI::NeuralNet::Kohonen::Visual";
16            
17             sub get_colour_for { my ($self,$x,$y) = (shift,shift,shift);
18             # From here you return a TK colour name.
19             # Get it as you please; for example, values of a 3D map:
20             return sprintf("#%02x%02x%02x",
21             (int (255 * $self->{map}->[$x]->[$y]->{weight}->[0])),
22             (int (255 * $self->{map}->[$x]->[$y]->{weight}->[1])),
23             (int (255 * $self->{map}->[$x]->[$y]->{weight}->[2])),
24             );
25             }
26            
27             exit;
28             1;
29            
30             And then:
31            
32             use YourClass;
33             my $net = AI::NeuralNet::Kohonen::Visual->new(
34             display => 'hex',
35             map_dim => 39,
36             epochs => 19,
37             neighbour_factor => 2,
38             targeting => 1,
39             table => "3
40             1 0 0 red
41             0 1 0 yellow
42             0 0 1 blue
43             0 1 1 cyan
44             1 1 0 yellow
45             1 .5 0 orange
46             1 .5 1 pink",
47             );
48             $net->train;
49             $net->plot_map;
50             $net->main_loop;
51            
52             exit;
53            
54            
55             =head1 DESCRIPTION
56            
57             Provides TK-based visualisation routines for C.
58             Replaces the earlier C.
59            
60             This is a sub-class of C
61             that impliments extra methods to make use of TK.
62            
63             This moudle is itself intended to be sub-classed by you,
64             where you provide a version of the method C:
65             see L and L for details.
66            
67            
68             =head1 CONSTRUCTOR (new)
69            
70             The following paramter fields are added to the base module's fields:
71            
72             =over 4
73            
74             =item display
75            
76             Set to C for display as a unified distance matrix, rather than
77             as the default plain grid;
78            
79             =item display_scale
80            
81             Set with a factor to effect the size of the display.
82            
83             =item show_bmu
84            
85             Show the current BMU during training.
86            
87             =item show_training
88            
89             Display updates during training.
90            
91             =item label_bmu
92            
93             =item label_all
94            
95             Displays labels...
96            
97             =item MainLoop
98            
99             Calls TK's C at the end of training.
100            
101             =item missing_colour
102            
103             When selecting a colour using L,
104             every node weight holding the value of C
105             will be given the value of this paramter. If this paramter
106             is not defined, the default is 0.
107            
108             =back
109            
110             =cut
111            
112 1     1   6 use strict;
  1         2  
  1         104  
113 1     1   15 use warnings;
  1         2  
  1         35  
114 1     1   6 use Carp qw/cluck carp confess croak/;
  1         2  
  1         100  
115            
116 1     1   5 use base "AI::NeuralNet::Kohonen";
  1         2  
  1         562  
117            
118 1     1   425 use Tk;
  0            
  0            
119             use Tk::Canvas;
120             use Tk::Label;
121             use Tk qw/DoOneEvent DONT_WAIT/;
122            
123            
124            
125             =head1 METHOD train
126            
127             Over-rides the base class to provide TK displays of the map.
128            
129             =cut
130            
131             sub train { my ($self,$epochs) = (shift,shift);
132             $epochs = $self->{epochs} unless defined $epochs;
133             $self->{display_scale} = 10 if not defined $self->{display_scale};
134            
135             &{$self->{train_start}} if exists $self->{train_start};
136            
137             $self->prepare_display if not defined $self->{_mw} or ref $self->{_mw} ne 'MainWindow';
138            
139             # Replaces Tk's MainLoop
140             for (0..$self->{epochs}) {
141             if ($self->{_quit_flag}) {
142             $self->{_mw}->destroy;
143             $self->{_mw} = undef;
144             return;
145             }
146             $self->{t}++; # Measure epoch
147             &{$self->{epoch_start}} if exists $self->{epoch_start};
148            
149             for (0..$#{$self->{input}}){
150             my $target = $self->_select_target;
151             my $bmu = $self->find_bmu($target);
152            
153             $self->_adjust_neighbours_of($bmu,$target);
154            
155             if (exists $self->{show_training}){
156             if ($self->{show_bmu}){
157             $self->plot_map(bmu_x=>$bmu->[1],bmu_y=>$bmu->[2]);
158             } else {
159             $self->plot_map;
160             }
161             $self->{_label_txt} = sprintf("Epoch: %04d",$self->{t})." "
162             . "Learning: $self->{l} "
163             . sprintf("BMU: %02d,%02d",$bmu->[1],$bmu->[2])." "
164             .( exists $target->{class}? "Target: [$target->{class}] " : "")
165             ;
166             $self->{_canvas}->update;
167             $self->{_label}->update;
168             DoOneEvent(DONT_WAIT); # be kind and process XEvents if they arise
169             }
170             }
171            
172             $self->_decay_learning_rate;
173             &{$self->{epoch_end}} if exists $self->{epoch_end};
174             }
175            
176             $self->{_label_txt} = "Did $self->{t} epochs: ";
177             $self->{_label_txt} .= "now smoothed." if $self->{smoothing};
178             $_->smooth if $self->{smooth};
179             $self->plot_map if $self->{MainLoop};
180             &{$self->{train_end}} if exists $self->{train_end};
181             MainLoop if $self->{MainLoop};
182            
183             return 1;
184             }
185            
186             =head1 METHOD get_colour_for
187            
188             This method is intended to be sub-classed.
189            
190             Currently it only operates on the first three elements
191             of a weight vector, turning them into RGB values.
192            
193             It returns the a TK colour for a node at position C,C in the
194             C paramter.
195            
196             Accepts: C and C co-ordinates in the map.
197            
198             =cut
199            
200             sub get_colour_for { my ($self,$x,$y) = (shift,shift,shift);
201             my $_0 = $self->{map}->[$x]->[$y]->{weight}->[0];
202             $_0 = $self->{missing_colour} || 0 if $_0 eq $self->{missing_mask};
203             my $_1 = $self->{map}->[$x]->[$y]->{weight}->[1];
204             $_1 = $self->{missing_colour} || 0 if $_1 eq $self->{missing_mask};
205             my $_2 = $self->{map}->[$x]->[$y]->{weight}->[2];
206             $_2 = $self->{missing_colour} || 0 if $_2 eq $self->{missing_mask};
207             return sprintf("#%02x%02x%02x",
208             (int (255 * $_0)),
209             (int (255 * $_1)),
210             (int (255 * $_2)),
211             );
212             }
213            
214            
215             =head1 METHOD prepare_display
216            
217             Depracated: see L.
218            
219             =cut
220            
221             sub prepare_display {
222             return $_[0]->create_empty_map;
223             }
224            
225             =head1 METHOD create_empty_map
226            
227             Sets up a TK C and C to
228             act as an empty map.
229            
230             =cut
231            
232             sub create_empty_map { my $self = shift;
233             my ($w,$h);
234             if ($self->{display} and $self->{display} eq 'hex'){
235             $w = ($self->{map_dim_x}+1) * ($self->{display_scale}+2);
236             $h = ($self->{map_dim_y}+1) * ($self->{display_scale}+2);
237             } else {
238             $w = ($self->{map_dim_x}+1) * ($self->{display_scale});
239             $h = ($self->{map_dim_y}+1) * ($self->{display_scale});
240             }
241             $self->{_mw} = MainWindow->new(
242             -width => $w + 20,
243             -height => $h + 20,
244             );
245             $self->{_mw}->fontCreate(qw/TAG -family verdana -size 8 -weight bold/);
246             $self->{_mw}->resizable( 0, 0);
247             $self->{_quit_flag} = 0;
248             $self->{_mw}->protocol('WM_DELETE_WINDOW' => sub {$self->{_quit_flag}=1});
249             $self->{_canvas} = $self->{_mw}->Canvas(
250             -width => $w,
251             -height => $h,
252             -relief => 'raised',
253             -border => 2,
254             );
255             $self->{_canvas}->pack(-side=>'top');
256             $self->{_label} = $self->{_mw}->Button(
257             -command => sub { $self->{_mw}->destroy;$self->{_mw} = undef; },
258             -relief => 'groove',
259             -text => ' ',
260             -wraplength => $w,
261             -textvariable => \$self->{_label_txt}
262             );
263             $self->{_label}->pack(-side=>'top');
264             return 1;
265             }
266            
267            
268             =head1 METHOD plot_map
269            
270             Plots the map on the existing canvas. Arguments are supplied
271             in a hash with the following keys as options:
272            
273             The values of C and C represent The I and I
274             co-ordinates of unit to highlight using the value in the
275             C to highlight it with colour. If no C is provided,
276             it default to red.
277            
278             When called, this method also sets the object field flag C:
279             currently, this prevents C from calling this routine.
280            
281             See also L.
282            
283             =cut
284            
285             sub plot_map { my ($self,$args) = (shift,{@_});
286             $self->{plotted} = 1;
287             # MW may have been destroyed
288             $self->prepare_display if not defined $self->{_mw};
289             my $yo = 5+($self->{display_scale}/2);
290             for my $x (0..$self->{map_dim_x}){
291             for my $y (0..$self->{map_dim_y}){
292             my $colour;
293             if ($args->{bmu_x} and $args->{bmu_x}==$x and $args->{bmu_y}==$y){
294             $colour = $args->{hicol} || 'red';
295             } else {
296             $colour = $self->get_colour_for ($x,$y);
297             }
298             if ($self->{display} and $self->{display} eq 'hex'){
299             my $xo = 5+($y % 2) * ($self->{display_scale}/2);
300            
301             $self->{_canvas}->create(
302             polygon => [
303             $xo + (($x)*$self->{display_scale} ),
304             $yo + (($y)*$self->{display_scale} ),
305            
306             # polygon only:
307             $xo + (($x)*($self->{display_scale})+($self->{display_scale}/2) ),
308             $yo + (($y)*($self->{display_scale})-($self->{display_scale}/2) ),
309             #
310            
311             $xo + (($x)*($self->{display_scale})+$self->{display_scale} ),
312             $yo + (($y)*$self->{display_scale} ),
313            
314             $xo + (($x)*($self->{display_scale})+$self->{display_scale} ),
315             $yo + (($y)*($self->{display_scale})+($self->{display_scale}/2) ),
316            
317             # Polygon only:
318             $xo + (($x)*($self->{display_scale})+($self->{display_scale}/2) ),
319             $yo + (($y)*($self->{display_scale})+($self->{display_scale}) ),
320             #
321            
322             $xo + (($x)*$self->{display_scale} ),
323             $yo + (($y)*($self->{display_scale})+($self->{display_scale}/2) ),
324            
325             ],
326             -outline => "black",
327             -fill => $colour,
328             );
329             }
330             else {
331             $self->{_canvas}->create(
332             rectangle => [
333             $x*$self->{display_scale} +1,
334             $y*$self->{display_scale} +1,
335             $x*($self->{display_scale})+$self->{display_scale} +1,
336             $y*($self->{display_scale})+$self->{display_scale} +1
337             ],
338             -outline => "black",
339             -fill => $colour,
340             );
341             }
342            
343             # Label
344             if ($self->{label_all}){
345             my $txt;
346             unless ( $txt = $self->{map}->[$x]->[$y]->{class}){
347             $txt = "";
348             }
349             $self->label_map($x,$y,"+$txt");
350             }
351            
352             }
353             }
354             if ($self->{label_bmu}){
355             my $txt;
356             unless ( $txt = $self->{map}->[$args->{bmu_x}]->[$args->{bmu_y}]->{class}){
357             $txt = "";
358             }
359             $self->label_map(
360             $args->{bmu_x}, $args->{bmu_y}, "+$txt"
361             );
362             }
363            
364             $self->{_canvas}->update;
365             $self->{_label}->update;
366            
367             return 1;
368             }
369            
370             =head1 METHOD label_map
371            
372             Put a text label on the map for the node at the I co-ordinates
373             supplied in the first two paramters, using the text supplied in the
374             third.
375            
376             Very naive: no attempt to check the text will appear on the map.
377            
378             =cut
379            
380             sub label_map { my ($self,$x,$y,$t) = (shift,shift,shift,shift);
381             $self->{_canvas}->createText(
382             $x*$self->{display_scale}+($self->{display_scale}),
383             $y*$self->{display_scale}+($self->{display_scale}),
384             -text => $t,
385             -anchor => 'w',
386             -fill => 'white',
387             -font => 'TAG',
388             );
389             }
390            
391            
392             =head1 METHOD main_loop
393            
394             Calls TK's C to keep a window open until the user closes it.
395            
396             =cut
397            
398             sub main_loop { my $self = shift;
399             $self->plot_map unless $self->{plotted};
400             MainLoop;
401             }
402            
403            
404            
405             1;
406            
407             __END__