File Coverage

blib/lib/Algorithm/Genetic/Diploid/Population.pm
Criterion Covered Total %
statement 56 56 100.0
branch 6 6 100.0
condition 3 3 100.0
subroutine 7 7 100.0
pod 3 3 100.0
total 75 75 100.0


line stmt bran cond sub pod time code
1             package Algorithm::Genetic::Diploid::Population;
2 2     2   11 use strict;
  2         3  
  2         90  
3 2     2   11 use List::Util qw'sum shuffle';
  2         5  
  2         156  
4 2     2   11 use Algorithm::Genetic::Diploid::Base;
  2         3  
  2         65  
5 2     2   12 use base 'Algorithm::Genetic::Diploid::Base';
  2         3  
  2         1747  
6              
7             my $log = __PACKAGE__->logger;
8              
9             =head1 NAME
10              
11             Algorithm::Genetic::Diploid::Population - A population of individuals that turns over
12              
13             =head1 METHODS
14              
15             =over
16              
17             =item new
18              
19             Constructor takes named arguments, creates a default, empty list of individuals
20              
21             =cut
22              
23             sub new {
24             shift->SUPER::new(
25 1     1 1 9 'individuals' => [],
26             @_,
27             );
28             }
29              
30             =item individuals
31              
32             Getter and setter for the list of individuals
33              
34             =cut
35              
36             sub individuals {
37 151     151 1 276 my $self = shift;
38 151 100       397 if ( @_ ) {
39 51         189 $self->{'individuals'} = \@_;
40 51         842 $log->debug("assigning ".scalar(@_)." individuals to population");
41             }
42 151         281 return @{ $self->{'individuals'} };
  151         674  
43             }
44              
45             =item turnover
46              
47             Moves the population on to the next generation, i.e.
48             1. compute fitness of all individuals
49             2. mate up to reproduction rate in proportion to fitness
50              
51             =cut
52              
53             sub turnover {
54 50     50 1 110 my ( $self, $gen, $env, $optimum ) = @_;
55 50         201 my $log = $self->logger;
56 50         322 $log->debug("going to breed generation $gen against optimum $optimum");
57            
58             # sort all individuals by fitness, creates array refs
59             # where 0 element is Individual, 1 element is its fitness
60 11163         13491 my @fittest = sort { $a->[1] <=> $b->[1] }
  2500         7682  
61 50         152 map { [ $_, $_->fitness($optimum,$env) ] }
62             $self->individuals;
63 50         657 $log->debug("sorted current generation by fitness");
64 50         294 $log->info("*** fittest at generation $gen: ".$fittest[0]->[0]->phenotype($env));
65            
66             # get the highest index of Individual
67             # that still gets to reproduce
68 50         277 my $maxidx = int( $self->experiment->reproduction_rate * $#fittest );
69 50         336 $log->debug("individuals up to index $maxidx will breed");
70            
71             # take the slice of Individuals that get to reproduce
72 50         349 my @breeders = @fittest[ 0 .. $maxidx ];
73 50         329 $log->debug("number of breeders: ".scalar(@breeders));
74            
75             # compute the total fitness, to know how much each breeder gets to
76             # contribute to the next generation
77 50         112 my $total_fitness = sum map { $_->[1] } @breeders;
  900         1300  
78 50         485 $log->debug("total fitness is $total_fitness");
79            
80             # compute the population size, which we need to divide up over the
81             # breeders in proportion of their fitness relative to total fitness
82 50         175 my $popsize = scalar $self->individuals;
83 50         316 $log->debug("population size will be $popsize");
84            
85             # here we make breeding pairs
86 50         114 my @children;
87 50         162 ORGY: while( @children < $popsize ) {
88 50         198 for my $i ( 0 .. $#breeders ) {
89 858         3299 my $quotum_i = $breeders[$i]->[1] / $total_fitness * $popsize * 2;
90 858         2375 for my $j ( 0 .. $#breeders ) {
91 15409         38848 my $quotum_j = $breeders[$j]->[1] / $total_fitness * $popsize * 2;
92 15409         61519 my $count_i = $breeders[$i]->[0]->child_count;
93 15409         59934 my $count_j = $breeders[$j]->[0]->child_count;
94 15409 100 100     78450 if ( $count_i < $quotum_i && $count_j < $quotum_j ) {
95 2500         12889 push @children, $breeders[$i]->[0]->breed($breeders[$j]->[0]);
96 2500         28195 $log->debug("bred child ".scalar(@children)." by pairing $i and $j");
97 2500 100       13842 last ORGY if @children == $popsize;
98             }
99             }
100             }
101             }
102            
103 50         204 my %genes = map { $_->id => 1 } map { $_->genes } map { $_->chromosomes } @children;
  5000         12295  
  5000         13130  
  2500         7111  
104 50         1411 $log->debug("generation $gen has ".scalar(keys(%genes))." distinct genes");
105            
106             # now the population consists of the children
107 50         311 $self->individuals(@children);
108 50         86 return @{ $fittest[0] };
  50         24560  
109             }
110              
111             =back
112              
113             =cut
114              
115             1;