Math-Amoeba-0.05/0000755000076400007640000000000011047302516013536 5ustar tom.chautom.chauMath-Amoeba-0.05/examples/0000755000076400007640000000000011047302516015354 5ustar tom.chautom.chauMath-Amoeba-0.05/examples/poly.pl0000644000076400007640000000141611047302275016700 0ustar tom.chautom.chauuse strict; ######################################################################## # This script is to test the ability of the optimizer to tune: # # f(x) = sum_i (x_i - i) where i from 1 to N. # ######################################################################## use Math::Amoeba qw(MinimiseND); &main(); sub main { my $N = 10; my $tol = 1e-5; my $maxiter = 10000; my $y = [1..$N]; my $guess = [ map { rand()*2 - 1 } (1..$N) ]; my $scale = [ map { 1 } (1..$N) ]; my $func = sub { my $x = [@_]; my $cost = 0; foreach my $i (0..$N-1) { $cost += ($x->[$i] - $y->[$i])**2; } return $cost; }; my ($opt, $cost) = MinimiseND($guess, $scale, \&$func, $tol, $maxiter); printf "[%s]: $cost\n", join(',', map { sprintf("%.2f", $_) } @$opt); } Math-Amoeba-0.05/Makefile.PL0000644000076400007640000000041511047301641015506 0ustar tom.chautom.chauuse strict; use ExtUtils::MakeMaker; use Config; WriteMakefile( 'NAME' => 'Math::Amoeba', 'LINKTYPE' => '$(INST_PM)', 'VERSION_FROM' => 'lib/Math/Amoeba.pm', 'dist' => { SUFFIX => 'gz', COMPRESS => 'gzip -f' }, 'clean' => { 'FILES' => '*~' }, ); Math-Amoeba-0.05/Changes0000644000076400007640000000133511047277365015050 0ustar tom.chautom.chauFuture Plan =========== 1) Convert the module in OO style while keeping the original methods unchanged. Recent changes ============== 0.05 ---- 1) Replaced expensive centroid re-calculation by adjustment approach. 2) Restructured loops into functions, to ease profiling and understanding. 3) Replace the hard-coded full contraction scale from "0.5" to "$BETA". 4) Removed unnecessary data copying. 5) Various performance tuning. 0.04 ---- 1) Minor bug fix in the verbose flag. 0.03 ---- 1) Added verbose flag. 2) Fixed POD. 3) Parsed version number from the library in "Makefile.PL". 0.02 ---- 1) Fixed a minor bug to avoid division by zero 2) Avoided unnecessary loop 3) Re-organized the file organizationk, added test case. Math-Amoeba-0.05/t/0000755000076400007640000000000011047302516014001 5ustar tom.chautom.chauMath-Amoeba-0.05/t/1_pod.t0000644000076400007640000000020111047276440015167 0ustar tom.chautom.chauuse Test::More; eval "use Test::Pod 1.00"; plan skip_all => "Test::Pod 1.00 required for testing POD" if $@; all_pod_files_ok(); Math-Amoeba-0.05/t/0_poly.t0000644000076400007640000000173610360661666015412 0ustar tom.chautom.chauuse strict; ######################################################################## # This script is to test the ability of the optimizer to tune: # # f(x) = sum_i (x_i - i) where i from 1 to N. # ######################################################################## use Test::More (tests => 11); sub BEGIN { use_ok('Math::Amoeba', 0.02, 'MinimiseND'); } sub testPoly { my $tol = 1e-5; my $N = 9; my $y = [1..$N]; my $func = sub { my $x = [@_]; my $cost = 0; foreach my $i (0..$N-1) { $cost += ($x->[$i] - $y->[$i])**2; } return $cost; }; my $guess = [ map { rand()*2 - 1 } (1..$N) ]; my $scale = [ map { 1 } (1..$N) ]; my ($opt, $cost) = MinimiseND($guess, $scale, \&$func, $tol, 10000); ok(abs($cost) < $tol, "Optimization success ($cost vs 0)"); foreach my $i (0..$N-1) { ok( abs($opt->[$i] - $y->[$i]) < 1e-2, sprintf( qq{The %d th parameter error okay (%.5f vs %.5f).}, $i, $opt->[$i], $y->[$i] ) ); } } &testPoly(); Math-Amoeba-0.05/t/2_pod_coverage.t0000644000076400007640000000024111047277527017056 0ustar tom.chautom.chauuse Test::More; eval "use Test::Pod::Coverage 1.00"; plan skip_all => "Test::Pod::Coverage 1.00 required for testing POD coverage" if $@; all_pod_coverage_ok(); Math-Amoeba-0.05/lib/0000755000076400007640000000000011047302516014304 5ustar tom.chautom.chauMath-Amoeba-0.05/lib/Math/0000755000076400007640000000000011047302516015175 5ustar tom.chautom.chauMath-Amoeba-0.05/lib/Math/Amoeba.pm0000644000076400007640000002015311047301731016716 0ustar tom.chautom.chau# This library file contains Amoeba n-D Minimisation routine for Perl # $Id: Amoeba.pm,v 1.2 1995/12/24 12:37:46 willijar Exp $ # $Id: Amoeba.pm,v 1.2 1995/12/24 12:37:46 willijar Exp $ package Math::Amoeba; use strict; use warnings; our $VERSION = 0.05; use Carp; use constant TINY => 1e-16; use Exporter; our @ISA=qw(Exporter); our @EXPORT_OK=qw(ConstructVertices EvaluateVertices Amoeba MinimiseND); =head1 NAME Math::Amoeba - Multidimensional Function Minimisation =head1 SYNOPSIS use Math::Amoeba qw(ConstructVertices EvaluateVertices Amoeba MinimiseND); my ($vertice,$y)=MinimiseND(\@guess,\@scales,\&func,$tol,$itmax,$verbose); my @vertices=ConstructVertices(\@vector,\@offsets); my @y=EvaluateVertices(\@vertices,\&func); my ($vertice,$y)=Amoeba(\@vertices,\@y,\&func,$tol,$itmax,$verbose); =head1 DESCRIPTION This is an implimenation of the Downhill Simpex Method in Multidimensions (Nelder and Mead) for finding the (local) minimum of a function. Doing this in Perl makes it easy for that function to actually be the output of another program such as a simulator. Arrays and the function are passed by reference to the routines. =over =item C The simplest use is the B function. This takes a reference to an array of guess values for the parameters at the function minimum, a reference to an array of scales for these parameters (sensible ranges around the guess in which to look), a reference to the function, a convergence tolerence for the minimum, the maximum number of iterations to be taken and the verbose flag (default ON). It returns an array consisting of a reference to the function parameters at the minimum and the value there. =item C The B function is the actual implimentation of the Downhill Simpex Method in Multidimensions. It takes a reference to an array of references to arrays which are the initial n+1 vertices (where n is the number of function parameters), a reference to the function valuation at these vertices, a reference to the function, a convergence tolerence for the minimum, the maximum number of iterations to be taken and the verbose flag (default ON). It returns an array consisting of a reference to the function parameters at the minimum and the value there. =item C The B is used by B to construct the initial vertices for B as the initial guess plus the parameter scale parameters as vectors along the parameter axis. =item C The B takes these set of vertices, calling the function for each one and returning the vector of results. =back =head1 EXAMPLE use Math::Amoeba qw(MinimiseND); sub afunc { my ($a,$b)=@_; print "$a\t$b\n"; return ($a-7)**2+($b+3)**2; } my @guess=(1,1); my @scale=(1,1); ($p,$y)=MinimiseND(\@guess,\@scale,\&afunc,1e-7,100); print "(",join(',',@{$p}),")=$y\n"; produces the output (6.99978191653352,-2.99981241563247)=1.00000008274829 =head1 LICENSE PERL =cut my ($ALPHA,$BETA,$GAMMA)=(1.0,0.5,2.0); sub MinimiseND { my ($guesses,$scales,$func,$tol,$itmax, $verbose)=@_; my @p=ConstructVertices($guesses,$scales); my @y=EvaluateVertices(\@p,$func); return Amoeba(\@p,\@y,$func,$tol,$itmax, $verbose); } sub ConstructVertices { # given 2 vector references constructs an amoeba # returning the vertices my ($vector,$ofs)=@_; my $n=$#{$vector}; my @vector=@{$vector}; my (@p,@y,$i); $p[0]=[]; @{$p[0]}=@{$vector}; for($i=0; $i<=$n; $i++) { my $v=[]; @{$v}=@{$vector}; $v->[$i]+=$ofs->[$i]; $p[$i+1]=$v; } return @p; } sub EvaluateVertices { # evaluates functions for all vertices of the amoeba my ($p,$func)=@_; my ($i,@y); for($i=0; $i<=$#{$p}; $i++) { $y[$i]=&$func(@{$p->[$i]}); } return @y; } sub Amoeba { my ($p,$y,$func,$ftol,$itmax, $verbose)=@_; my $n=$#{$p}; # no points # Default parameters $verbose = (defined($verbose)) ? $verbose : 1; if (!$itmax) { $itmax=200; } if (!$ftol) { $ftol=1e-6; } # Member variables my ($i,$j); my $iter=0; my ($ilo, $inhi, $ihi); my ($pbar, $pr, $pe, $pc, $ypr, $ype, $ypc); # To control the recalculation of centroid my $recalc = 1; my $ihi_o; # Loop until any of stopping conditions hit while (1) { ($ilo, $inhi, $ihi) = _FindMarkers($y); # Stopping conditions my $rtol = 2*abs($y->[$ihi]-$y->[$ilo])/(abs($y->[$ihi])+abs($y->[$ilo])+TINY); if ($rtol<$ftol) { last; } if ($iter++>$itmax) { carp "Amoeba exceeded maximum iterations\n" if ($verbose); last; } # Determine the Centroid if ($recalc) { $pbar = _CalcCentroid($p, $ihi); } else { _AdjustCentroid($pbar, $p, $ihi_o, $ihi); } # Reset the re-calculation flag, and remember the current highest $recalc = 0; # Determine the reflection point, evaluate its value $pr = _CalcReflection($pbar, $p->[$ihi], $ALPHA); $ypr = &$func(@$pr); # if it gives a better value than best point, try an # additional extrapolation by a factor gamma, accept best if ($ypr < $y->[$ilo]) { $pe = _CalcReflection($pbar, $pr, -$GAMMA); $ype=&$func(@$pe); if ($ype < $y->[$ilo]) { $p->[$ihi] = $pe; $y->[$ihi] = $ype; } else { $p->[$ihi] = $pr; $y->[$ihi] = $ypr; } } # if reflected point worse than 2nd highest elsif ($ypr >= $y->[$inhi]) { # if it is better than highest, replace it if ($ypr < $y->[$ihi] ) { $p->[$ihi] = $pr; $y->[$ihi] = $ypr; } # look for an intermediate lower point by performing a # contraction of the simplex along one dimension $pc = _CalcReflection($pbar, $p->[$ihi], -$BETA); $ypc = &$func(@$pc); # if contraction gives an improvement, accept it if ($ypc < $y->[$ihi]) { $p->[$ihi] = $pc; $y->[$ihi] = $ypc; } # otherwise cant seem to remove high point # so contract around lo (best) point else { for($i=0; $i<=$n; $i++) { if ($i!=$ilo) { $p->[$i] = _CalcReflection($p->[$ilo], $p->[$i], -$BETA); $y->[$i] = &$func(@{$p->[$i]}); } } $recalc = 1; } } # if reflected point is in-between lowest and 2nd highest else { $p->[$ihi] = $pr; $y->[$ihi] = $ypr; } # Remember the replacing position and its value $ihi_o = $ihi; } return ($p->[$ilo],$y->[$ilo]); } # Helper function - find the lowest, 2nd highest and highest position sub _FindMarkers { my $y = shift; my ($ilo, $inhi, $ihi); my ($i, $n); $n = @$y - 1; $ilo=0; if ($y->[0]>$y->[1]) { $ihi=0; $inhi=1; } else { $ihi=1; $inhi=0; } for($i = 0; $i <= $n; $i++) { if ($y->[$i] < $y->[$ilo]) { $ilo = $i; } if ($y->[$i] > $y->[$ihi]) { $inhi = $ihi; $ihi = $i; } elsif ($y->[$i] > $y->[$inhi] && $ihi != $i) { $inhi = $i; } } return ($ilo, $inhi, $ihi); } # Determine the centroid (except the highest point) sub _CalcCentroid { my ($p, $ihi) = @_; my ($i, $j, $n); $n = @$p - 1; my $pbar = []; for($j=0; $j<$n; $j++) { for($i=0; $i<=$n; $i++) { if ($i!=$ihi) { $pbar->[$j] += $p->[$i][$j]; } } $pbar->[$j] /= $n; } return $pbar; } # Adjust the centroid only sub _AdjustCentroid { my ($pbar, $p, $ihi_o, $ihi) = @_; my ($j, $n); $n = @$pbar; if ($ihi_o != $ihi) { for($j=0; $j<$n; $j++) { $pbar->[$j] += ($p->[$ihi_o][$j] - $p->[$ihi][$j]) / $n; } } } # Determine the reflection point sub _CalcReflection { my ($p1, $p2, $scale) = @_; my $j; my $n = @$p1; my $pr = []; for($j=0; $j<$n; $j++) { $pr->[$j] = $p1->[$j] + $scale*($p1->[$j]-$p2->[$j]); } return $pr; } return 1; __END__ =head1 HISTORY See "REAME". =head1 BUGS Let me know. =head1 AUTHOR John A.R. Williams Tom Chau =head1 SEE ALSO "Numerical Recipies: The Art of Scientific Computing" W.H. Press, B.P. Flannery, S.A. Teukolsky, W.T. Vetterling. Cambridge University Press. ISBN 0 521 30811 9. =head1 COPYRIGHT Copyright (c) 1995 John A.R. Williams. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. Since 2005, this module was co-developed with Tom. Math-Amoeba-0.05/MANIFEST0000644000076400007640000000026611047302476014700 0ustar tom.chautom.chauChanges examples/poly.pl lib/Math/Amoeba.pm Makefile.PL MANIFEST This list of files META.yml Module meta-data (added by MakeMaker) README t/0_poly.t t/1_pod.t t/2_pod_coverage.t Math-Amoeba-0.05/README0000644000076400007640000000105111047275477014432 0ustar tom.chautom.chauDescription =========== This module provides numerical optimization by Downhill Simplex method. Copyright ========= Copyright (c) 1995 John A.R. Williams. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. Since 2005, this module was co-developed with Tom. Prerequisites ============= 1) strict 2) constant 3) Carp Build and install ================= perl Makefile.PL make make test make install Changes/Enhancement planned =========================== See "ChangeLog". Math-Amoeba-0.05/META.yml0000664000076400007640000000046211047302516015013 0ustar tom.chautom.chau# http://module-build.sourceforge.net/META-spec.html #XXXXXXX This is a prototype!!! It will change in the future!!! XXXXX# name: Math-Amoeba version: 0.05 version_from: lib/Math/Amoeba.pm installdirs: site requires: distribution_type: module generated_by: ExtUtils::MakeMaker version 6.30