File Coverage

blib/lib/PDL/Opt/Simplex.pm
Criterion Covered Total %
statement 64 67 95.5
branch 19 24 79.1
condition 3 5 60.0
subroutine 6 6 100.0
pod 0 2 0.0
total 92 104 88.4


line stmt bran cond sub pod time code
1             package PDL::Opt::Simplex;
2 1     1   260917 use strict;
  1         3  
  1         41  
3 1     1   6 use warnings;
  1         2  
  1         59  
4 1     1   8 use PDL;
  1         2  
  1         43  
5 1     1   130583 use PDL::Exporter;
  1         6  
  1         7  
6              
7             our @ISA = qw/PDL::Exporter/;
8             our @EXPORT_OK = qw/simplex make_simplex/;
9             our %EXPORT_TAGS = ( Func => [@EXPORT_OK] );
10             our $VERSION = '2.097';
11              
12             *simplex = \&PDL::simplex;
13              
14             sub make_simplex {
15 7     7 0 3667 my ($init, $initsize) = @_;
16 7         41 my ( $nd, $nd2, @otherdims ) = ( $init->dims, 1 );
17 7 100       29 pop @otherdims if @otherdims; # drop the spurious "1"
18 7 50 66     47 return unless $nd2 == $nd + 1 or $nd2 == 1;
19 7 100       33 return $init if $nd2 == $nd + 1;
20 5         110 my $simp = PDL->zeroes( $nd, $nd + 1, @otherdims );
21             # Constructing a tetrahedron:
22             # At step n (starting from zero)
23             # take vertices 0..n and move them 1/(n+1) to negative dir on axis n.
24             # Take vertex n+1 and move it n/(n+1) to positive dir on axis n
25 5         22 my $uppertri = $simp->copy;
26 5         126 ones($nd * ($nd+1) / 2)->tritosquare($uppertri->slice(",:-2")->t);
27 5         1008 my $iseq = sequence($nd);
28 5         554 my $pjseq = $iseq / ( $iseq + 1 );
29 5         433 $simp .= $init - $initsize * $pjseq * $uppertri;
30 5         447 $simp->slice(",1:")->diagonal(0,1) += $initsize * (1-$pjseq);
31 5         636 $simp;
32             }
33              
34             sub PDL::simplex {
35 6     6 0 322660 my ( $init, $initsize, $minsize, $maxiter, $sub, $logsub, $t ) = @_;
36 6   50     31 my $simp = make_simplex($init, $initsize) // return;
37 6         39 my $nd = $simp->dim(0);
38 6         24 my $vals = $sub->($simp);
39 6         618 my $ssize;
40 6         24 while ($maxiter--) {
41 354         16285 $ssize = ( $simp - $simp->slice(":,0") )->magnover->maxover;
42 354 100       19263 $logsub->( $simp, $vals, $ssize ) if $logsub;
43 354 100       1366 last unless $ssize > $minsize;
44 348 50       28026 my $valsn = !$t ? $vals : $vals - $t * log( $vals->random + 0.00001 );
45 348         3218 my $minind = $valsn->minimum_ind;
46 348         1206 my $maxind = $valsn->maxover_n_ind(2);
47 348         10326 my $maxind0 = $maxind->at(0);
48 348         4600 my @worstvals = $valsn->index($maxind)->list;
49 348         7467 my $bestval = $valsn->at($minind);
50 348         14872 my $ssum = ($simp->t->sumover - (my $worst = $simp->slice(":,($maxind0)"))) / $nd;
51 348         31198 my $new = 2 * $ssum - $worst;
52 348         9241 my $val = $sub->($new)->at(0);
53 348 50       21175 $val += $t * log( rand() + 0.00001 ) if $t;
54 348         655 my $removetop = 0;
55 348 100       1064 if ( $val < $bestval ) {
    100          
56 115         283 my $newnew = $new + $ssum - $worst;
57 115         2814 my $val2 = $sub->($newnew);
58 115 100       5269 if ( $val2->at(0) < $val ) { # CASE1 Reflection and Expansion
59 80         861 $new = $newnew;
60 80         145 $val = $val2;
61             } # else CASE2 Reflection
62 115         541 $removetop = 1;
63             } elsif ( $val < $worstvals[1] ) { # CASE3 Reflection
64 39         58 $removetop = 1;
65             } else {
66 194         474 my $newnew = 0.5 * ($ssum + $worst);
67 194         6432 my $val2 = $sub->($newnew);
68 194 50       9760 if ( $val2->at(0) < $worstvals[0] ) { # CASE4 Contraction
69 194         2146 $new = $newnew;
70 194         301 $val = $val2;
71 194         351 $removetop = 1;
72             }
73             }
74 348 50       773 if ($removetop) {
75 348         1102 $worst .= $new;
76 348         5965 $vals->slice( "($maxind0)" ) .= $val;
77             } else { # CASE5 Multiple Contraction
78 0         0 $simp .= 0.5 * ($simp->slice(":,$minind") + $simp);
79 0         0 my $idx = which( sequence($nd+1) != $minind );
80 0         0 $vals->index($idx) .= $sub->($simp->dice_axis(1,$idx));
81             }
82             }
83 6         511 my $mmind = $vals->minimum_ind;
84 6         46 return ( $simp->slice(":,$mmind"), $ssize, $vals->index($mmind) );
85             }
86              
87             1;
88              
89             __END__