amoeba.pl
author A.M. Thurnherr <athurnherr@yahoo.com>
Sat, 24 Jul 2021 09:38:16 -0400
changeset 46 70e566505a12
parent 0 a5233793bf69
permissions -rw-r--r--
V7.3

#======================================================================
#                    A M O E B A . P L 
#                    doc: Wed Aug 23 05:11:48 2006
#                    dlm: Wed Aug 23 23:52:12 2006
#                    (c) 2006 A.M. Thurnherr
#                    uE-Info: 88 0 NIL 0 0 72 2 2 4 NIL ofnI
#======================================================================

# perlified amoeba implementation of NR code

# NOTES:
#	- 0-based arrays
#	- amoeba returns undef if NMAX is exceeded and # of evals otherwise

use strict;

sub amotry($$$$$$)
{
	my($pR,$yR,$psumR,$funR,$ihi,$fac) = @_;
	my(@ptry);

	my($ndim) = scalar(@{$pR->[0]});
	my($fac1) = (1-$fac) / $ndim;
	my($fac2) = $fac1 - $fac;
	
	for (my($j)=0; $j<$ndim; $j++) {
		$ptry[$j] = $psumR->[$j]*$fac1 - $pR->[$ihi][$j]*$fac2;
	}
	my($ytry) = &$funR(@ptry);
	if ($ytry < $yR->[$ihi]) {
		$yR->[$ihi] = $ytry;
		for (my($j)=0; $j<$ndim; $j++) {
			$psumR->[$j] += $ptry[$j] - $pR->[$ihi][$j];
			$pR->[$ihi][$j] = $ptry[$j];
		}
	}
	return $ytry;
}

sub get_psum($$)
{
	my($pR,$psumR) = @_;

	for (my($j)=0; $j<@{$pR->[0]}; $j++) {
		my($sum);
		for ($sum=my($i)=0; $i<=@{$pR->[0]}; $i++) {
			$sum += $pR->[$i][$j];
		}
		$psumR->[$j] = $sum;
	}
}

sub amoeba($$$$)
{
	my($pR,$yR,$ftol,$funR,$NMAX) = @_;
	my($nfunk) = 0;
	my($ndim) = scalar(@{$pR->[0]});
	my(@psum);
	
	&get_psum($pR,\@psum);

	while (1) {
		my($i,$ihi,$inhi,$j);
		my($sum);
		
		my($ilo) = 0;
		if ($yR->[0] > $yR->[1]) {
			$ihi = 0; $inhi = 1;
		} else {
			$ihi = 1; $inhi = 0;
		}

		for ($i=0; $i<$ndim+1; $i++) {
			if ($yR->[$i] <= $yR->[$ilo]) {
				$ilo = $i;
			}
			if ($yR->[$i] > $yR->[$ihi]) {
				$inhi = $ihi;
				$ihi  = $i;
			} elsif ($yR->[$i] > $yR->[$inhi] && $i != $ihi) {
				$inhi = $i;
			}
		}
		print(STDERR "best = $yR->[$ilo]\n");

		my($rtol) = 2 * abs($yR->[$ihi] - $yR->[$ilo]) /
						(abs($yR->[$ihi]) + abs($yR->[$ilo]));
		if ($rtol < $ftol) {
			my($tmp) = $yR->[0]; $yR->[0] = $yR->[$ilo]; $yR->[$ilo] = $tmp;
			for ($i=0; $i<$ndim; $i++) {
				my($tmp) = $pR->[1][$i]; $pR->[1][$i] = $pR->[$ilo][$i];
				$pR->[$ilo][$i] = $tmp;
			}
			return $nfunk;
		}
		
		return undef if ($nfunk >= $NMAX);
		$nfunk += 2;
		
		my($ytry) = amotry($pR,$yR,\@psum,$funR,$ihi,-1);
		if ($ytry <= $yR->[$ilo]) {
			$ytry = amotry($pR,$yR,\@psum,$funR,$ihi,2);
		} elsif ($ytry >= $yR->[$inhi]) {
			my($ysave) = $yR->[$ihi];
			$ytry = amotry($pR,$yR,\@psum,$funR,$ihi,0.5);
			if ($ytry >= $ysave) {
				for ($i=0; $i<$ndim+1; $i++) {
					if ($i != $ilo) {
						for ($j=0; $j<$ndim; $j++) {
							$pR->[$i][$j] = $psum[$j] =
								0.5 * ($pR->[$i][$j] + $pR->[$ilo][$j]);
						}
						$yR->[$i] = &$funR(@psum);
					}
				}
				$nfunk += $ndim;
				&get_psum($pR,\@psum);
			}
		} else {
			--$nfunk;
		}
	}
}

1;