File Coverage

blib/lib/Algorithm/Evolutionary/Simple.pm
Criterion Covered Total %
statement 89 90 98.8
branch 8 8 100.0
condition 20 33 60.6
subroutine 15 16 93.7
pod 10 10 100.0
total 142 157 90.4


line stmt bran cond sub pod time code
1             package Algorithm::Evolutionary::Simple;
2              
3 2     2   119033 use warnings;
  2         6  
  2         74  
4 2     2   12 use strict;
  2         5  
  2         56  
5 2     2   10 use Carp qw(croak);
  2         10  
  2         116  
6              
7             our $VERSION = '0.3'; # Probably such an increase is not guaranteed, but...
8              
9 2     2   37 use base 'Exporter';
  2         2  
  2         202  
10 2     2   345 use Sort::Key::Top qw(rnkeytop) ;
  2         1381  
  2         1493  
11              
12             our @EXPORT_OK= qw( random_chromosome max_ones max_ones_fast spin
13             get_pool_roulette_wheel get_pool_binary_tournament
14             produce_offspring mutate crossover single_generation );
15              
16             # Module implementation here
17             sub random_chromosome {
18 32     32 1 12808 my $length = shift;
19 32         40 my $string = '';
20 32         60 for (1..$length) {
21 1024 100       1357 $string .= (rand >0.5)?1:0;
22             }
23 32         73 $string;
24             }
25              
26             sub max_ones {
27 126     126 1 25779 my $str=shift;
28 126         127 my $count = 0;
29 126         174 while ($str) {
30 3997         4829 $count += chop($str);
31             }
32 126         287 $count;
33             }
34              
35             sub max_ones_fast {
36 0     0 1 0 ($_[0] =~ tr/1/1/);
37             }
38              
39             sub get_pool_roulette_wheel {
40 7   66 7 1 2727 my $population = shift || croak "No population here";
41 6   66     21 my $fitness_of = shift || croak "need stuff evaluated";
42 5   66     19 my $need = shift || croak "I need to know the new population size";
43 4   66     15 my $total_fitness = shift || croak "I need the total fitness";
44              
45 3         44 my @wheel = map( $fitness_of->{$_}/$total_fitness, @$population);
46 3         9 my @slots = spin( \@wheel, scalar(@$population));
47             # my $slots = scalar(@$population);
48             # my @slots = map( $_*$slots, @wheel );;
49 3         4 my @pool;
50 3         3 my $index = 0;
51 3         4 do {
52 208         200 my $p = $index++ % @slots;
53 208         186 my $copies = $slots[$p];
54 208         275 for (1..$copies) {
55 96         169 push @pool, $population->[$p];
56             }
57             } while ( @pool < $need );
58              
59 3         20 @pool;
60             }
61              
62             sub get_pool_binary_tournament {
63 4   66 4 1 2166 my $population = shift || croak "No population here";
64 3   66     15 my $fitness_of = shift || croak "need stuff evaluated";
65 2   66     13 my $need = shift || croak "I need to know the new population size";
66              
67 1         2 my $total_fitness = 0;
68 1         2 my @pool;
69 1         2 my $population_size = @$population;
70 1         2 do {
71 32         36 my $one = $population->[rand( $population_size )];
72 32         35 my $another = $population->[rand( $population_size )];
73 32 100       49 if ( $fitness_of->{$one} > $fitness_of->{$another} ) {
74 11         17 push @pool, $one;
75             } else {
76 21         37 push @pool, $another;
77             }
78             } while ( @pool < $need );
79              
80 1         7 @pool;
81             }
82              
83             sub spin {
84 3     3 1 6 my ( $wheel, $slots ) = @_;
85 3         20 return map( $_*$slots, @$wheel );
86             }
87              
88             sub produce_offspring {
89 4   33 4 1 911 my $pool = shift || croak "Pool missing";
90 4   33     7 my $offspring_size = shift || croak "Population size needed";
91 4         5 my @population = ();
92 4         6 my $population_size = scalar( @$pool );
93 4         11 for ( my $i = 0; $i < $offspring_size/2; $i++ ) {
94 62         83 my $first = $pool->[rand($population_size)];
95 62         68 my $second = $pool->[rand($population_size)];
96 62         67 push @population, crossover( $first, $second );
97             }
98 4         18 map( $_ = mutate($_), @population );
99 4         20 return @population;
100             }
101              
102             sub mutate {
103 124     124 1 112 my $chromosome = shift;
104 124         140 my $mutation_point = int(rand( length( $chromosome )));
105 124 100       180 substr($chromosome, $mutation_point, 1,
106             ( substr($chromosome, $mutation_point, 1) eq 1 )?0:1 );
107 124         171 return $chromosome;
108             }
109              
110             sub crossover {
111 62     62 1 66 my ($chromosome_1, $chromosome_2) = @_;
112 62         59 my $length = length( $chromosome_1 );
113 62         74 my $xover_point_1 = int rand( $length - 2 );
114 62         72 my $range = 1 + int rand ( $length - $xover_point_1 );
115 62         60 my $swap_chrom = $chromosome_1;
116 62         77 substr($chromosome_1, $xover_point_1, $range,
117             substr($chromosome_2, $xover_point_1, $range) );
118 62         75 substr($chromosome_2, $xover_point_1, $range,
119             substr($swap_chrom, $xover_point_1, $range) );
120 62         136 return ( $chromosome_1, $chromosome_2 );
121             }
122              
123             sub single_generation {
124 4   66 4 1 1249 my $population = shift || croak "No population";
125 3   66     14 my $fitness_of = shift || croak "No fitness cache";
126 2         4 my $total_fitness = shift;
127 2 100       6 if ( !$total_fitness ) {
128 1         7 map( $total_fitness += $fitness_of->{$_}, @$population);
129             }
130 2         4 my $population_size = @{$population};
  2         4  
131 2     64   17 my @best = rnkeytop { $fitness_of->{$_} } 2 => @$population; # Extract elite
  64         80  
132 2         10 my @reproductive_pool = get_pool_roulette_wheel( $population, $fitness_of,
133             $population_size, $total_fitness ); # Reproduce
134 2         5 my @offspring = produce_offspring( \@reproductive_pool, $population_size - 2 ); #Obtain offspring
135 2         11 unshift( @offspring, @best ); #Insert elite at the beginning
136 2         13 @offspring; # return
137             }
138              
139             "010101"; # Magic true value required at end of module
140             __END__