File Coverage

blib/lib/Math/ES.pm
Criterion Covered Total %
statement 393 407 96.5
branch 84 136 61.7
condition 21 38 55.2
subroutine 31 31 100.0
pod 6 9 66.6
total 535 621 86.1


line stmt bran cond sub pod time code
1             package Math::ES;
2              
3             require 5.005_62;
4 1     1   777 use strict;
  1         1  
  1         30  
5 1     1   5 use warnings;
  1         1  
  1         27  
6 1     1   702 use FileHandle;
  1         18518  
  1         7  
7              
8 1     1   1458 use Math::Random qw( random_permuted_index );
  1         8238  
  1         8688  
9              
10             require Exporter;
11             # use AutoLoader qw(AUTOLOAD);
12              
13             our @ISA = qw(Exporter);
14              
15             our %EXPORT_TAGS = ();
16             our @EXPORT_OK = ();
17             our @EXPORT = ();
18             our $VERSION = '0.08'; # Change version number in POD !
19              
20             # --------------------------------------------------------------------
21              
22             my $debug = 0;
23              
24             #
25             # Selection schemes:
26             # 1 : n best survive
27             # 2 : n-1 best survive, last choses randomly
28             # 3 : GA Roulette (not implemented, yet)
29             #
30              
31             # Package variable
32             my $count = 0;
33             my $file = 'es';
34             my $debug_suffix = '.dbg';
35             my $log_suffix = '.log';
36              
37              
38             # --------------------------------------------------------------------
39             #
40             # Constructor method
41             #
42             sub new {
43              
44 3     3 0 9823 my $obj = shift;
45              
46 3         10 $count++;
47              
48             # Preset with default values
49 3         38 my $eso = bless {
50             'populations' => 2,
51             'individuals' => 5,
52             'parents' => 2,
53             'children' => 10,
54             'elite' => 1,
55             'selection_scheme' => 1,
56              
57             'generations' => 50,
58             'stepwidth_const' => 1,
59             'stepwidth_var' => 1.5,
60             'variance_mutator' => 0.5,
61              
62             'isolation' => 25,
63             'migrators' => 1,
64              
65             'genes' => [],
66             'gene_deviations' => [],
67             'max_gene_values' => [],
68             'min_gene_values' => [],
69             'rating_function' => '',
70              
71             'log' => 1,
72             'debug' => 0,
73              
74             'log_handle' => FileHandle->new(),
75             'debug_handle' => FileHandle->new(),
76              
77             'log_file' => "$file-$count$log_suffix",
78             'debug_file' => "$file-$count$debug_suffix",
79             }, $obj;
80              
81             # Overwrite with user specific values
82 3 50       303 $eso->set_values(@_) if (@_);
83 3         11 return $eso;
84             }
85              
86             # --------------------------------------------------------------------
87             #
88             # Add user specific values
89             #
90             sub set_values {
91 3     3 1 5 my $obj = shift;
92             # Add or overwrite
93 3         7 %{$obj} = ((%{$obj}) ,@_);
  3         75  
  3         50  
94 3         25 return ($obj);
95             }
96              
97              
98             # --------------------------------------------------------------------
99             #
100             # Validate control parameters, array conformities etc.
101             #
102             sub validate {
103 14     14 1 30 my $obj = shift;
104              
105 14         22 my $msg = '';
106              
107 14 50       56 $msg .= " Number of populations must be greater than zero\n" if ($obj->{'populations'} < 1);
108 14 50       193 $msg .= " Number of individuals must be greater than zero\n" if ($obj->{'individuals'} < 1);
109 14 50       42 $msg .= " Number of parents must be greater than zero\n" if ($obj->{'parents'} < 1);
110 14 50       46 $msg .= " Number of children must be greater than zero\n" if ($obj->{'children'} < 1);
111 14 50       45 $msg .= " Number of children must be greater than or equal to number of individuals\n"
112             if ($obj->{'children'} < $obj->{'individuals'});
113 14 50       53 $msg .= " Number of elite must be less than number of individuals\n"
114             if ($obj->{'elite'} >= $obj->{'individuals'});
115 14 50 66     62 $msg .= " Selection scheme must be 1 or 2\n" if ($obj->{'selection_scheme'} != 1 and
116             $obj->{'selection_scheme'} != 2);
117 14 50       44 $msg .= " Number of generations must be greater than zero\n" if ($obj->{'generations'} < 1);
118 14 50       41 $msg .= " variance_mutator must be positive\n" if ($obj->{'variance_mutator'} < 0);
119 14 50       47 $msg .= " Number of isolation cycles must not be negative\n" if ($obj->{'isolation'} < 0);
120 14 50       49 $msg .= " Number of migrators must not be negative\n" if ($obj->{'migrators'} < 0);
121            
122 14         21 my $ng = @{$obj->{'genes'}};
  14         29  
123 14         20 my $ngd = @{$obj->{'gene_deviations'}};
  14         25  
124 14         17 my $gmx = @{$obj->{'max_gene_values'}};
  14         23  
125 14         19 my $gmn = @{$obj->{'min_gene_values'}};
  14         23  
126              
127 14 50       42 $msg .= " Number of gene_deviations ($ngd) must be equal to number of genes ($ng)\n"
128             unless ($ng == $ngd);
129 14 50       33 $msg .= " Number of max_gene_values ($gmx) must be equal to number of genes ($ng)\n"
130             unless ($ng == $gmx);
131 14 50       642 $msg .= " Number of min_gene_values ($gmn) must be equal to number of genes ($ng)\n"
132             unless ($ng == $gmn);
133            
134 14         52 for my $i (1..$ng) {
135 86         151 my $g = $obj->{'genes'}[$i-1];
136 86         271 my $max = $obj->{'max_gene_values'}[$i-1];
137 86         143 my $min = $obj->{'min_gene_values'}[$i-1];
138 86 50 33     329 $msg .= " max_gene_value $i ($max) is smaller than gene $i ($g)\n"
139             if ($ng == $gmx and $max < $g );
140 86 50 33     352 $msg .= " min_gene_value $i ($min) is greater than gene $i ($g)\n"
141             if ($ng == $gmn and $min > $g );
142             }
143              
144 14 100       105 if ($obj->{'populations'} == 1) {
145 2 50       9 $msg .= " Isolation feature cannot be used for a single population\n"
146             if ($obj->{'isolation'} > 0);
147 2 50       8 $msg .= " Migration feature cannot be used for a single population\n"
148             if ($obj->{'migrators'} > 0);
149             }
150              
151 14 50       107 $msg .= " Rating function is missing\n"
152             unless (ref($obj->{'rating_function'}) =~ /CODE/);
153              
154 14 50       31 print "Validated\n" if ($debug);
155 14         37 return ($msg);
156             }
157              
158             # --------------------------------------------------------------------
159             #
160             # go Darwin go
161             #
162             sub start {
163              
164 3     3 1 17 my $obj = shift;
165            
166 3         7 my $debug = $obj->{'debug'};
167 3         8 my $log = $obj->{'log'};
168 3         10 $| = 1;
169              
170             # Validate
171 3         16 my $msg = $obj->validate();
172 3 50       10 return ($msg) if ($msg);
173              
174             # Files
175 3         7 my $dfh = $obj->{'debug_handle'};
176 3         8 my $lfh = $obj->{'log_handle'};
177 3 50       10 if ($debug) {
178 0         0 open ($dfh, ">".$obj->{'debug_file'});
179             }
180 3 50       7 if ($log) {
181 3         87647 open ($lfh, ">".$obj->{'log_file'});
182             }
183              
184             # Setup
185 3         19 my $npop = $obj->{'populations'};
186 3         11 my @populations = ();
187 3         17 for (my $i=1; $i<=$npop; $i++) {
188 8 50       17 print $dfh "Creating population number $i ...\n" if ($debug);
189 8         38 my $pop = Math::ES::Population->new (
190             'individuals' => $obj->{'individuals'},
191             'parents' => $obj->{'parents'},
192             'children' => $obj->{'children'},
193             'elite' => $obj->{'elite'},
194             'selection_scheme' => $obj->{'selection_scheme'},
195             'migrators' => $obj->{'migrators'},
196            
197             'stepwidth_const' => $obj->{'stepwidth_const'},
198             'stepwidth_var' => $obj->{'stepwidth_var'},
199             'variance_mutator' => $obj->{'variance_mutator'},
200            
201 8         29 'genes' => [@{$obj->{'genes'}}],
202 8         26 'max_gene_values' => [@{$obj->{'max_gene_values'}}],
203 8         109 'min_gene_values' => [@{$obj->{'min_gene_values'}}],
204 0         0 'gene_deviations' => [@{$obj->{'gene_deviations'}}],
205             'max_gene_deviations' =>
206 0         0 ( defined($obj->{'max_gene_deviations'}) ? [@{$obj->{'max_gene_deviations'}}] : [ ] ),
207             'min_gene_deviations' =>
208 8 50       42 ( defined($obj->{'min_gene_deviations'}) ? [@{$obj->{'min_gene_deviations'}}] : [ ] ),
    50          
209             'rating_function' => $obj->{'rating_function'},
210            
211             'debug' => $obj->{'debug'},
212             'debug_handle' => $obj->{'debug_handle'},
213             );
214 8         21 push (@populations, $pop);
215 8 50       31 print $dfh "done\n" if ($debug);
216             }
217              
218 3         12 $obj->{'populations_list'} = [@populations];
219              
220 3         11 $obj->run;
221             }
222              
223             # --------------------------------------------------------------------
224             #
225             # go Darwin go
226             #
227             sub run {
228 11     11 1 172 my $obj = shift;
229            
230 11         26 my $debug = $obj->{'debug'};
231 11         23 my $log = $obj->{'log'};
232 11         17 my $dfh = $obj->{'debug_handle'};
233 11         26 my $lfh = $obj->{'log_handle'};
234             # $| = 1;
235              
236             # 0, Validate
237 11         42 my $msg = $obj->validate();
238 11 50       29 return ($msg) if ($msg);
239              
240             # 1, Setup
241 11         15 my @populations = @{$obj->{'populations_list'}};
  11         34  
242 11         15 my $nmig = $obj->{'migrators'};
243 11         24 my $niso = $obj->{'isolation'};
244              
245             # 2, Evaluate first generation
246 11         24 my @pop_rate_list;
247             my @pop_rate_ranked;
248 11         19 foreach my $pop (@populations) {
249             # Evaluate function
250 40         81 push (@pop_rate_list, $pop->rate_individuals());
251              
252             # Sort individuals
253 40         90 push (@pop_rate_ranked, $pop->rank_individuals());
254             }
255            
256              
257             # --- Loop
258 11         26 my $maxgn = $obj->{'generations'};
259 11         33 for (my $gn = 1; $gn <= $maxgn; $gn++) {
260            
261             # This should go to log file
262 520 50       1382 if ($log) {
263 520         2483 print $lfh ">>","-"x80,"\n";
264 520         1399 print $lfh ">>Generation $gn\n";
265             }
266              
267             # 3, Create children
268 520         926 foreach my $pop (@populations) {
269 1380         4654 $pop->manage_children();
270            
271 1380         5803 $pop->do_selection();
272              
273              
274 1380 50       4182 if ($log) {
275 1380         4415 my $ra_p= $pop->rank_individuals();
276 1380         7061 print $lfh " Ranking list:\t";
277 1380         2997 foreach my $p (@$ra_p) {
278 9400         60054 printf $lfh " %10.5f", $p;
279             }
280 1380         7259 print $lfh "\tBest genes: ",$pop->{'individuals_list'}[0]->pretty_genes;
281 1380         6417 print $lfh "\n";
282             }
283              
284             }
285              
286             # 4, Do migration
287 520 100 66     2482 if ($nmig > 0 and scalar(@populations) > 1 ) {
288 100         495 $obj->do_migration();
289             }
290              
291             # Do mixing
292 520 100 66     6996 if ($niso > 0 and scalar(@populations) > 1 and ($gn % $niso) == 0) {
      100        
293 3         15 $obj->do_mixing();
294             }
295              
296            
297             }
298              
299 11         66 return ($obj->return_best_value(), [$obj->return_best_genes()]);
300             }
301              
302             # --------------------------------------------------------------------
303             #
304             # Do migration of n individuals
305             #
306             sub do_migration {
307 100     100 0 141 my $obj = shift;
308              
309 100         282 my $debug = $obj->{'debug'};
310 100         201 my $dfh = $obj->{'debug_handle'};
311 100         185 my $nmig = $obj->{'migrators'};
312 100         134 my @populations = @{$obj->{'populations_list'}};
  100         355  
313              
314 100         426 for my $i (1..$nmig) {
315            
316 200         337 my @migrators = ();
317            
318             # Fetch migrator
319 200         395 foreach my $pop (@populations) {
320 600         1641 push (@migrators, $pop->withdraw_random_individual());
321             }
322            
323             # Insert migrator (cyclic changed)
324 200         339 my $p = shift (@populations); push (@populations, $p);
  200         308  
325 200         342 foreach my $pop (@populations) {
326 600         11923 $pop->integrate_individual( shift(@migrators) );
327             }
328             }
329              
330             # Debug
331 100 50       396 print $dfh "Migrated $nmig individual(s)\n" if ($debug);
332              
333 100         281 return (1);
334              
335             }
336              
337             # --------------------------------------------------------------------
338             #
339             # Do mixing of all populations after n generations of isolation
340             #
341             sub do_mixing {
342 3     3 0 7 my $obj = shift;
343              
344 3         7 my @all_indy = ();
345 3         8 my @idx = ();
346 3         6 my @nindy = ();
347            
348 3         7 my $debug = $obj->{'debug'};
349 3         7 my $dfh = $obj->{'debug_handle'};
350 3         7 my $niso = $obj->{'isolation'};
351 3         4 my @populations = @{$obj->{'populations_list'}};
  3         12  
352              
353             # Empty all populations
354 3         6 foreach my $pop (@populations) {
355 10         17 my $n1 = $pop->{'individuals'};
356 10 50       27 print $dfh "\t$n1 individuals in current pop\n" if ($debug);
357 10         26 push (@all_indy, $pop->withdraw_all_individual);
358 10         28 push (@nindy, $n1);
359             }
360              
361 3 50       13 print $dfh "\t",scalar(@all_indy)," individuals in total\n" if ($debug);
362            
363             # Now fill again ...
364 3         5 my $n2 = scalar (@all_indy);
365 3         16 @idx = &random_permuted_index($n2);
366            
367 3 50       209 print $dfh " Indexvector : \n",join(":",@idx),"\n" if ($debug);
368              
369             # ... all populations ...
370 3         7 foreach my $pop (@populations) {
371 10         15 my $n1 = shift(@nindy);
372            
373             # ... with randomly choosen individuals.
374 10         23 for my $i (1..$n1) {
375 80         109 my $idx = shift(@idx);
376 80 50 33     363 if (defined($idx) and defined($all_indy[$idx])) {
377 80         182 $pop->integrate_individual( $all_indy[$idx] );
378             }
379             else {
380 0         0 print $dfh " Oops, we lost an individual: $i from $n1\n";
381             }
382             }
383             }
384              
385             # Debug
386 3 50       11 print $dfh "Mixing done\n" if ($debug);
387              
388 3         27 return (1);
389              
390             }
391              
392             # --------------------------------------------------------------------
393             #
394             # Retrieve best genes from all populations
395             #
396             sub return_best_genes {
397 21     21 1 539 my $obj = shift;
398              
399 21         29 my @populations = @{$obj->{'populations_list'}};
  21         63  
400            
401 21         33 my @best_indys = ();
402 21         36 foreach my $pop (@populations) {
403              
404             # Be sure, that we have an ordered list.
405 76         147 $pop->rank_individuals();
406            
407 76         196 push (@best_indys, $pop->{'individuals_list'}[0]);
408             }
409              
410 21         86 @best_indys = sort { $a->rate() <=> $b->rate() } (@best_indys);
  87         192  
411              
412 21         33 return (@{$best_indys[0]{'genes'}});
  21         221  
413             }
414              
415             # --------------------------------------------------------------------
416             #
417             # Retrieve best function value from all populations
418             #
419             sub return_best_value {
420 21     21 1 59 my $obj = shift;
421              
422 21         53 my @populations = @{$obj->{'populations_list'}};
  21         69  
423            
424 21         40 my @best_indys = ();
425 21         42 foreach my $pop (@populations) {
426              
427             # Be sure, that we have an ordered list.
428 76         141 $pop->rank_individuals();
429            
430 76         199 push (@best_indys, $pop->{'individuals_list'}[0]);
431             }
432              
433 21         74 @best_indys = sort { $a->rate() <=> $b->rate() } (@best_indys);
  87         168  
434              
435 21         55 return ($best_indys[0]->rate);
436             }
437              
438             # --------------------------------------------------------------------
439             # --------------------------------------------------------------------
440              
441             package Math::ES::Population;
442 1     1   23 use Math::Random qw( random_uniform_integer );
  1         1  
  1         1840  
443              
444             sub new {
445 8     8   16 my $name = shift;
446 8         202 my $obj = bless {@_}, $name;
447            
448 8         23 my $debug = $obj->{'debug'};
449 8         14 my $dfh = $obj->{'debug_handle'};
450              
451 8         14 my $nindiv = $obj->{'individuals'};
452 8         14 $obj->{'pop_counter'} = 0;
453              
454 8 50       21 print $dfh " Creating population with $nindiv members\n" if ($debug);
455            
456 8         12 my @individuals = ();
457 8         21 for (my $j=1; $j <= $nindiv; $j++) {
458 60 50       111 print $dfh "\tCreating individuum $j out of $nindiv ... " if ($debug);
459             # Guarantee a individual with the input genes
460 60         69 my $do_mutate = 1;
461 60 100       112 $do_mutate = 0 if ($j == 1);
462 60         188 my $indi = Math::ES::Individuum->new (
463             'pop_rate_individuals' => undef,
464 60         398 'genes' => [@{$obj->{'genes'}}],
465 60         160 'gene_deviations' => [@{$obj->{'gene_deviations'}}],
466 60         342 'max_gene_values' => [@{$obj->{'max_gene_values'}}],
467 60         78 'min_gene_values' => [@{$obj->{'min_gene_values'}}],
468             'rating_function' => $obj->{'rating_function'},
469              
470             'stepwidth_const' => $obj->{'stepwidth_const'},
471             'stepwidth_var' => $obj->{'stepwidth_var'},
472             'variance_mutator' => $obj->{'variance_mutator'},
473            
474             'mutate' => $do_mutate,
475              
476             'debug' => $obj->{'debug'},
477             );
478 60         138 push (@individuals, $indi);
479 60 50       197 print $dfh " ok\n" if ($debug);
480             }
481 8         31 $obj->{'individuals_list'} = [@individuals];
482 8 50       21 print $dfh " done\n" if ($debug);
483              
484             # ---
485              
486 8         25 return $obj;
487             }
488              
489             # -------------
490              
491             # Create n children stemming from m parents, mutate them, rate them
492             sub manage_children {
493 1380     1380   2427 my $obj = shift;
494            
495 1380         10329 my $debug = $obj->{'debug'};
496 1380         2804 my $dfh = $obj->{'debug_handle'};
497              
498 1380         2826 my $nchld = $obj->{'children'};
499 1380         2425 my $nindy = $obj->{'individuals'};
500 1380         2575 my $npar = $obj->{'parents'};
501              
502 1380         2318 my @new_children = ();
503              
504 1380         2915 $obj->{'children_list'} = [];
505              
506 1380 50       119055 if ($debug) {
507 0         0 print $dfh " Managing children\n";
508              
509 0         0 print $dfh " Parents\n";
510 0         0 my $pp=0;
511 0         0 foreach my $p (@{$obj->{'individuals_list'}}) {
  0         0  
512 0         0 print $dfh "Parent $pp = ",$p->pretty_genes(),"\n";
513 0         0 $pp++;
514             }
515             }
516              
517             # Create children
518 1380         3446 for my $nc (1..$nchld) {
519 25700         73518 my $child = Math::ES::Individuum->new();
520              
521             # Determine parents
522 25700         46548 my @parents_idx = ();
523 25700         48147 my @parents_list = ();
524 25700         48828 for my $np (1..$npar) {
525 56144         171523 my $num = random_uniform_integer(1, 0,$nindy-1);
526 56144 100       923675 if (grep(/^$num$/, @parents_idx)) {
527 4744         7232 redo;
528             }
529             else {
530 51400         83566 push (@parents_idx, $num) ;
531 51400         141575 push (@parents_list, $obj->{'individuals_list'}[$num]);
532             }
533             }
534              
535             # Now do the origination (data copy and crossover)
536 25700 50       58586 print $dfh " Parents chosen for crossover ",join(' : ',@parents_idx),"\n" if($debug);
537 25700         64190 $child->originate(@parents_list);
538              
539             # ... mutate it ...
540 25700         73263 $child->mutate();
541              
542             # ... and rate it
543 25700         73861 $child->rate();
544              
545 25700         27055 push (@{$obj->{'children_list'}}, $child);
  25700         61091  
546              
547 25700 50       93702 print $dfh "Child $nc = ",$child->pretty_genes()," >=> ",$child->rate(),"\n" if ($debug);
548             }
549              
550 1380         6808 $obj->rank_children();
551            
552 1380         3040 return(@{$obj->{'children_list'}});
  1380         5552  
553             }
554              
555             # -------------
556              
557             sub rate_individuals {
558 2892     2892   4009 my $obj = shift;
559            
560 2892 100 66     14626 unless (exists($obj->{'pop_rate_individuals'}) or defined($obj->{'pop_rate_individuals'}) ) {
561 8         13 $obj->{'pop_rate_individuals'} = 0;
562 8         9 foreach my $indy (@{$obj->{'individuals_list'}}) {
  8         18  
563 60         134 $obj->{'pop_rate_individuals'} += $indy->rate();
564             }
565             }
566              
567 2892         4922 return($obj->{'pop_rate_individuals'});
568             }
569              
570             # -------------
571              
572             sub rank_individuals {
573 2852     2852   7019 my $obj = shift;
574              
575 2852         8008 $obj->rate_individuals();
576              
577 2852         3804 my @temp = sort { $a->rate() <=> $b->rate() } (@{$obj->{'individuals_list'}});
  41895         85112  
  2852         8585  
578 2852         9362 $obj->{'individuals_list'} = [@temp];
579              
580 2852         5939 my @temp2;
581 2852         4223 foreach my $indy (@{$obj->{'individuals_list'}}) {
  2852         6286  
582 22250         41613 push (@temp2, $indy->rate());
583             }
584 2852         10272 $obj->{'ranked_rates_individuals'} = [@temp2];
585 2852         11851 return(\@temp2);
586             }
587              
588             # -------------
589              
590             sub rate_children {
591 1380     1380   2024 my $obj = shift;
592            
593 1380 100 66     7321 unless (exists($obj->{'pop_rate_children'}) or defined($obj->{'pop_rate_children'}) ) {
594 8         363 $obj->{'pop_rate_children'} = 0;
595 8         11 foreach my $indy (@{$obj->{'children_list'}}) {
  8         22  
596 160         1825 $obj->{'pop_rate_children'} += $indy->rate();
597             }
598             }
599              
600 1380         3830 return($obj->{'pop_rate_children'});
601             }
602              
603             # -------------
604              
605             sub rank_children {
606 1380     1380   2089 my $obj = shift;
607              
608 1380         4254 $obj->rate_children();
609              
610 1380         1848 my @temp = sort { $a->rate() <=> $b->rate() } (@{$obj->{'children_list'}});
  75101         141570  
  1380         10507  
611 1380         8133 $obj->{'children_list'} = [@temp];
612              
613 1380         3884 my @temp2;
614 1380         2164 foreach my $indy (@{$obj->{'children_list'}}) {
  1380         3298  
615 25700         47557 push (@temp2, $indy->rate());
616             }
617 1380         11174 $obj->{'ranked_rates_children'} = [@temp2];
618 1380         8475 return(\@temp2);
619             }
620              
621             # -------------
622              
623             sub do_selection {
624 1380     1380   2175 my $obj = shift;
625              
626 1380         2457 my @new_indies = ();
627              
628 1380         3004 my $nchld = $obj->{'children'};
629 1380         3722 my $nindy = $obj->{'individuals'};
630 1380         2285 my $elite = $obj->{'elite'};
631              
632             # Respect the elite
633 1380 100 66     9237 if ($elite > 0 and $elite <= $nindy ) {
634 1080         1728 my @temp = sort { $a->rate() <=> $b->rate() } (@{$obj->{'children_list'}}, @{$obj->{'individuals_list'}});
  60828         108784  
  1080         2284  
  1080         4961  
635            
636 1080         3286 for my $i (1..$elite) {
637 1280         5760 push (@new_indies, $temp[$i-1]);
638             }
639             }
640              
641             # Deal with the rest
642 1380         3933 my $nrest = $nindy - $elite;
643 1380 50       3738 if ($nrest > 0) {
644              
645             # Selection according to scheme
646 1380         2792 my $scheme = $obj->{'selection_scheme'};
647              
648             # 1 = Select n best
649 1380 100       3549 if ($scheme == 1) {
    50          
650 1180         2486 foreach my $i (1..$nrest) {
651 6520         11464 push (@new_indies, $obj->{'children_list'}[$i-1]);
652             }
653             }
654              
655             # 2 = Select n-1 best and one random other
656             elsif ($scheme == 2) {
657 200         497 foreach my $i (1..$nrest-1) {
658 1400         2378 push (@new_indies, $obj->{'children_list'}[$i-1]);
659             }
660 200         818 my $lastone = random_uniform_integer(0, $nrest, $nchld);
661 200         2463 push (@new_indies, $obj->{'children_list'}[$lastone-1]);
662             }
663            
664             }
665              
666             # Move to next generation
667 1380         4462 $obj->{'individuals_list'} = [@new_indies];
668 1380         55593 $obj->{'pop_counter'}++;
669            
670             }
671              
672             # -------------
673             # Withdraw a number of individuals from the population
674             # but spare the elite.
675             #
676             sub withdraw_random_individual {
677 600     600   664 my $obj = shift;
678              
679 600   50     4587 my $num = (shift || 1);
680              
681 600         747 my ($nindy, $elite);
682 600         985 $elite = $obj->{'elite'};
683              
684 600         1064 my @withdrawn = ();
685 600         1107 for my $i (1..$num) {
686              
687 600         1050 $nindy = $obj->{'individuals'};
688 600 50       1425 last if ($nindy-$elite <= 0);
689 600 50       1150 last if ($nindy == 0);
690              
691 600         2386 my $num = random_uniform_integer(0, $elite+1, $nindy);
692              
693 600         6912 $obj->{'individuals'}--;
694 600         758 push (@withdrawn, splice(@{$obj->{'individuals_list'}},$num-1,1));
  600         13373  
695             }
696              
697 600         1514 $obj->rank_individuals();
698              
699 600         2106 return(@withdrawn);
700             }
701              
702             # -------------
703             # Withdraw all individuals
704             #
705             sub withdraw_all_individual {
706 10     10   16 my $obj = shift;
707              
708              
709 10         11 my @withdrawn = @{$obj->{'individuals_list'}};
  10         34  
710 10         19 $obj->{'individuals_list'} = [];
711 10         24 $obj->{'individuals'} = 0;
712              
713 10         35 return(@withdrawn);
714             }
715              
716             # -------------
717             # Add a number of new individuals to the population
718             #
719             sub integrate_individual {
720 680     680   800 my $obj = shift;
721            
722 680         1114 foreach my $indy (@_) {
723 680         1069 $obj->{'individuals'}++;
724 680         779 push (@{$obj->{'individuals_list'}}, $indy);
  680         2160  
725             }
726              
727 680         1409 $obj->rank_individuals();
728              
729 680         2180 return($obj);
730             }
731              
732              
733             # --------------------------------------------------------------------
734             # --------------------------------------------------------------------
735             package Math::ES::Individuum;
736 1     1   7 use Math::Random qw(random_normal random_uniform);
  1         1  
  1         934  
737              
738             # -----------
739             # Constructor of a new individuum
740             #
741             sub new {
742 25760     25760   41772 my $name = shift;
743 25760         76378 my $obj = bless {@_}, $name;
744              
745 25760         58287 $obj->{'indy_rate'} = undef;
746              
747 25760 100       57400 if ($obj->{'mutate'}) {
748 52         105 $obj->mutate;
749             }
750              
751 25760         45706 return ($obj);
752             }
753              
754             # -----------
755             # Return the rating function value of the individuum
756             #
757             #
758             sub rate {
759 429887     429887   526214 my $obj = shift;
760            
761              
762             # Call the rating function (if no value is present)
763             #
764             # &function(@values) returns a result
765 429887 100       834813 unless (defined $obj->{'indy_rate'}) {
766 25760         25492 $obj->{'indy_rate'} = &{$obj->{'rating_function'}}( @{$obj->{'genes'}} );
  25760         84474  
  25760         46956  
767             }
768 429887         1237003 return ($obj->{'indy_rate'});
769             }
770              
771              
772             # -----------
773             # Do mutation on individuum
774             #
775             # $obj->mutate();
776             #
777             sub mutate {
778              
779 25752     25752   37781 my $obj = shift;
780              
781             # Firstly mutate deviations
782 25752         37864 my $i=-1;
783 25752         25612 foreach my $gd (@{$obj->{'gene_deviations'}}) {
  25752         60370  
784 142656         383633 my $rnn = random_normal(0,0, $obj->{'variance_mutator'});
785 142656         1175847 $i++;
786 142656         225606 my $tmp = $gd * exp($rnn);
787 142656 50 33     652204 if (defined($obj->{'max_gene_deviations'}[$i]) and
    50 33        
788             $tmp > $obj->{'max_gene_deviations'}[$i]) {
789 0         0 $gd = $obj->{'max_gene_deviations'}[$i];
790             }
791             elsif (defined($obj->{'min_gene_deviations'}[$i]) and
792             $tmp < $obj->{'min_gene_deviations'}[$i]) {
793 0         0 $gd = $obj->{'min_gene_deviations'}[$i];
794             }
795             else {
796 142656         254245 $gd = $tmp;
797             };
798             }
799              
800             # Secondly mutate genes
801 25752         36938 my $n = @{$obj->{'genes'}};
  25752         45925  
802 25752         62098 for (my $i=0; $i<$n; $i++) {
803              
804 719138         997489 Try: {
805 142656         151693 my $var = $obj->{'stepwidth_var'};
806 719138 100       1658469 my $factor = ( random_uniform() > 0.5 ? $var : 1/$var ) * $obj->{'stepwidth_const'};
807            
808 719138         4209032 my $gd = $obj->{'gene_deviations'}[$i];
809 719138         1826651 my $rnn = random_normal(0,0,$gd);
810            
811 719138         6344406 my $temp = $obj->{'genes'}[$i] + ($rnn * $factor);
812 719138 100       1818894 redo Try if ($temp > $obj->{'max_gene_values'}[$i]);
813 439389 100       1036059 redo Try if ($temp < $obj->{'min_gene_values'}[$i]);
814              
815 142656         451512 $obj->{'genes'}[$i] = $temp;
816             }
817              
818             }
819            
820 25752         41872 return (1);
821             }
822              
823             # -----------
824             # Simulate the originating process of a new individuum.
825             #
826             # $child_obj->originate($parent1, $parent1, ...)
827             #
828             sub originate {
829 25700     25700   34081 my $obj = shift;
830 25700         44851 my @parents = @_; # Allow more than 1 or 2 cross over parents
831              
832 25700         30071 my $np = @parents;
833              
834             # Copy all info from first parent
835 25700         57664 $parents[0]->copy_to($obj);
836              
837             # ... but reset the value !!!
838 25700         36501 $obj->{'indy_rate'} = undef;
839              
840 25700         27415 my $n = @{$obj->{'genes'}};
  25700         41196  
841            
842             # We have more than one parent, do the crossover
843 25700 50       53786 unless ($np == 1) {
844              
845             # Iterate over the genes
846 25700         60541 for (my $i=0; $i<$n; $i++) {
847 142400         342324 my $rnu = random_uniform();
848             # print "Random Number: $rnu\n";
849              
850             # Find the appropriate parent
851 142400         839214 Parent: for (my $p=0; $p<$np; $p++) {
852 213318 100       549578 if ($rnu <= 1/$np*($p+1)) {
853 142400         295192 $obj->{'genes'}[$i] = $parents[$p]->{'genes'}[$i];
854 142400         259465 $obj->{'gene_deviations'}[$i] = $parents[$p]->{'gene_deviations'}[$i];
855 142400         377880 last Parent;
856             }
857             }
858             }
859            
860             }
861 25700         44815 return ($obj);
862             }
863              
864             # -----------
865             # Copy operator for an individuum
866             # $from_obj->copy_to($to_obj);
867              
868             sub copy_to {
869 25700     25700   30011 my $obj = shift;
870 25700         25466 my $new = shift;
871            
872 25700         27535 foreach (keys (%{$obj})) {
  25700         134346  
873 359752         515399 my $temp = $obj->{$_};
874 359752 100       834351 if (ref($temp) =~ 'ARRAY') {
    50          
875 154152         615508 $new->{$_} = [@$temp];
876             }
877             elsif (ref($temp) =~ 'HASH') {
878 0         0 $new->{$_} = {%$temp};
879             }
880             else {
881 205600         407443 $new->{$_} = $temp; # Scalars and programs go here
882             }
883             };
884 25700         79325 return ($new);
885             }
886              
887             # -----------
888             # Return the genes and variances in a 'pretty' style
889             #
890             sub pretty_genes {
891 1380     1380   2611 my $obj = shift;
892              
893 1380         1717 my $n = @{$obj->{'genes'}};
  1380         3376  
894 1380         7605 my $output;
895              
896             # Iterate over the genes
897 1380         4633 for (my $i=0; $i<$n; $i++) {
898 8160         70250 $output .= sprintf("%10.6f", $obj->{'genes'}[$i])
899             . ' (' . sprintf("%10.6f", $obj->{'gene_deviations'}[$i]) . ')';
900             }
901 1380         6578 return ($output);
902             }
903              
904             1;
905              
906             __END__