File Coverage

lib/Algorithm/Evolutionary/Individual/Tree.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 1     1   5 use strict; #-*-cperl-*-
  1         16  
  1         31  
2 1     1   5 use warnings;
  1         2  
  1         54  
3              
4             =head1 NAME
5              
6             Tree - A Direct Acyclic Graph, or tree, useful for Genetic Programming-Style stuff
7              
8             =head1 SYNOPSIS
9              
10             use Algorithm::Evolutionary::Individual::Tree;
11             #Hash with primitives, arity, and range for constants that multiply it
12              
13             my $primitives = { sum => [2, -1, 1],
14             multiply => [2, -1, 1],
15             substract => [2, -1, 1],
16             divide => [2, -1, 1],
17             x => [0, -10, 10],
18             y => [0, -10, 10] };
19              
20             my $indi = new Algorithm::Evolutionary::Individual::Tree $primitives, 5 ; # Build random tree with knwo primitives
21             # and depth up to 5
22              
23             my $indi5 = $indi->clone(); #Creates a copy of the individual
24              
25             print $indi3->asString(); #Prints the individual
26             print $indi3->asXML() #Prints it as XML. See L for more info on this
27              
28             =head1 Base Class
29              
30             L
31              
32             =head1 DESCRIPTION
33              
34             Tree-like individual for genetic programming. Uses direct acyclic graphs
35             as representation for trees, which is very convenient. This class has
36             not been tested extensively, so it might not work.
37              
38             =cut
39              
40             package Algorithm::Evolutionary::Individual::Tree;
41              
42 1     1   5 use Carp;
  1         1  
  1         59  
43 1     1   4 use Exporter;
  1         1  
  1         69  
44              
45             our ($VERSION) = ( '$Revision: 3.1 $ ' =~ / (\d+\.\d+)/ );
46              
47 1     1   1403 use Tree::DAG_Node;
  1         30175  
  1         42  
48              
49 1     1   72 use Algorithm::Evolutionary::Individual::Base;
  0            
  0            
50              
51             our @ISA = qw (Algorithm::Evolutionary::Individual::Base);
52              
53             =head1 METHODS
54              
55             =head2 new( $primitives, $depth, $fitness )
56              
57             Creates a new tree using a primitives hashref, max depth, and a
58             ref-to-fitness
59              
60             =cut
61              
62             sub new {
63             my $class = shift;
64             my $self = {_primitives => shift,
65             _depth => shift,
66             _fitness => undef };
67             my @keys = keys %{$self->{_primitives}};
68             $self->{_keys} = \@keys;
69             bless $self, $class;
70             $self->randomize();
71             return $self;
72             }
73              
74             =head2 set
75              
76             Sets values of an individual; takes a hash as input
77              
78             =cut
79              
80             sub set {
81             my $self = shift;
82             my $hash = shift || croak "No params here";
83             for ( keys %{$hash} ) {
84             $self->{"_$_"} = $hash->{$_};
85             }
86             $self->{_tree} = undef;
87             $self->{_fitness} = undef;
88             }
89              
90             =head2 randomize
91              
92             Assigns random values to the elements
93              
94             =cut
95              
96             sub randomize {
97             my $self = shift;
98             $self->{_tree} = Tree::DAG_Node->new();
99             my $name;
100             do {
101             $name = $self->{'_keys'}[rand( @{$self->{'_keys'}} - 1 )];
102             } until $self->{'_primitives'}{$name}[0] > 1; #0 is arity
103             #Compute random constant
104             my $ct = $self->{'_primitives'}{$name}[1]
105             + rand( $self->{'_primitives'}{$name}[2] - $self->{'_primitives'}{$name}[1]);
106             $self->{'_tree'}->name( $name ); #Root node
107             $self->{'_tree'}->attributes( { constant => $ct} );
108             $self->growSubTree( $self->{'_tree'}, $self->{_depth} );
109             }
110              
111              
112             =head2 fromString
113              
114             Probably useless, in this case. To be evolved.
115              
116             =cut
117              
118             sub fromString {
119             my $class = shift;
120             my $str = shift;
121             my $sep = shift || ",";
122             my $self = { _array => split( $sep, $str ),
123             _fitness => undef };
124             bless $self, $class;
125             return $self;
126             }
127              
128             =head2 clone
129              
130             Similar to a copy ctor: creates a new individual from another one
131              
132             =cut
133              
134             sub clone {
135             my $indi = shift || croak "Indi to clone missing ";
136             my $self = { _fitness => undef,
137             _depth => $indi->{_depth} };
138             %{$self->{_primitives}} = %{$indi->{_primitives}};
139             @{$self->{_keys}} = @{$indi->{_keys}};
140             $self->{_tree} = $indi->{_tree}->copy_tree();
141             bless $self, __PACKAGE__;
142             return $self;
143             }
144              
145              
146             =head2 asString
147              
148             Prints it
149              
150             =cut
151              
152             sub asString {
153             my $self = shift;
154             #my $lol = $self->{_tree}->tree_to_lol();
155             # my $str = lolprint( @$lol );
156             # $str .= " -> ";
157             # if ( defined $self->{_fitness} ) {
158             # $str .=$self->{_fitness};
159             # }
160             my $node = $self->{_tree};
161             my $str;
162             $node->walk_down( { callback => \&nodePrint,
163             callbackback => \&closeParens,
164             str => \$str,
165             primitives => $self->{_primitives}} );
166             # print $self->{_tree}->tree_to_lol_notation();
167             return $str;
168             }
169              
170             =head2 nodePrint
171              
172             Prints a node
173              
174             =cut
175              
176             sub nodePrint {
177             my $node = shift;
178             my $options = shift;
179             my $strRef = $options->{str};
180             ${$strRef} .= ($node->attributes()->{constant}?($node->attributes()->{constant}. "*"):""). $node->name();
181             if ( $options->{primitives}{$node->name()}[0] > 0 ) { #That's the arity
182             ${$strRef} .= "( ";
183             } elsif ( $options->{primitives}{$node->name()}[0] == 0 ){ #Add comma
184             if ($node->right_sister() ) {
185             ${$strRef} .= ", ";
186             }
187             }
188            
189             }
190              
191             =head2 closeParens
192              
193             Internal subrutine: closes node parenthesis
194              
195             =cut
196              
197             sub closeParens {
198             my $node = shift;
199             my $options = shift;
200             my $strRef = $options->{str};
201             if ( $options->{primitives}{$node->name()}[0] > 0 ) { #That's the arity
202             ${$strRef} .= " ) ";
203             if ($node->right_sister() ) {
204             ${$strRef} .= ", ";
205             }
206             }
207            
208             }
209              
210              
211             =head2 Atom
212              
213             Returns the tree, which is atomic by itself. Cannot be used as lvalue
214              
215             =cut
216              
217             sub Atom {
218             my $self = shift;
219             return $self->{'_tree'};
220             }
221              
222             =head2 asXML
223              
224             Prints it as XML. It prints the tree as String, which does not mean
225             you will be able to get it back from this form. It's done just for
226             compatibity, reading from this format will be available. In the future.
227              
228             =cut
229              
230             sub asXML {
231             my $self = shift;
232             my $str = $self->SUPER::asXML();
233             # my $str2 = ">\nasString()."]]> ";
234             my $str2 = ">\n ";
235             $str =~ s/\/>/$str2/e ;
236             return $str.$str2."\n";
237             }
238              
239              
240             =head2 addAtom
241              
242             Dummy sub
243              
244             =cut
245              
246             sub addAtom {
247             my $self = shift;
248             $self->{_tree} = Tree::DAG_Node->new();
249             $self->{'_tree'}->name( "dummy root node" ); #Root node
250             $self->{'_tree'}->attributes( { constant => 0 } );
251             }
252              
253             =head2 lolprint
254              
255             Print the list of lists that composes the tree, using prefix notation
256              
257             =cut
258              
259             sub lolprint {
260             my @ar = @_;
261             my $str;
262             if ( $#ar > 0 ) {
263             $str = $ar[$#ar]."(";
264             for ( @ar[0..$#ar-1] ) {
265             if ( ref $_ eq 'ARRAY' ) {
266             $str .= lolprint( @$_ );
267             } else {
268             $str .= $_;
269             }
270             $str .= ", " if ($_ != $ar[$#ar-1]);
271             }
272             $str .= " )";
273              
274             } else {
275             $str = $ar[0];
276             }
277             return $str;
278             }
279              
280             =head2 growSubTree
281              
282             Grows a random tree, with primitives as indicated, and a certain depth. Depth
283             defaults to 4
284              
285             =cut
286              
287             sub growSubTree {
288             my $self = shift;
289             my $tree = shift;
290             my $depth = shift || 4;
291             return if $depth == 1;
292             for ( my $i = 0; $i < $self->{_primitives}{$tree->name()}[0]; $i++ ) {
293             my $primitive;
294             if ( $depth > 2 ) {
295             $primitive = $self->{_keys}[rand( @{$self->{_keys}} )];
296             } else {
297             do {
298             $primitive = $self->{_keys}[rand( @{$self->{_keys}} )];
299             } until $self->{_primitives}{$primitive}[0] == 0;
300             }
301             my $shiquiya = $tree->new_daughter();
302             #Generate constant
303             my $ct = $self->{_primitives}{$primitive}[1]
304             + rand( $self->{_primitives}{$primitive}[2] - $self->{_primitives}{$primitive}[1]);
305             $shiquiya->name($primitive);
306             $shiquiya->attributes( { constant => $ct} );
307             $self->growSubTree( $shiquiya, $depth-1);
308             }
309             }
310              
311             =head2 size()
312              
313             Returns 1, since it's got only 1 Atom
314              
315             =cut
316              
317             sub size {
318             my $self = shift;
319             return 1;
320             }
321              
322             =head1 Copyright
323            
324             This file is released under the GPL. See the LICENSE file included in this distribution,
325             or go to http://www.fsf.org/licenses/gpl.txt
326              
327             CVS Info: $Date: 2009/07/28 11:30:56 $
328             $Header: /media/Backup/Repos/opeal/opeal/Algorithm-Evolutionary/lib/Algorithm/Evolutionary/Individual/Tree.pm,v 3.1 2009/07/28 11:30:56 jmerelo Exp $
329             $Author: jmerelo $
330             $Revision: 3.1 $
331             $Name $
332              
333             =cut