home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Source Code 1992 March
/
Source_Code_CD-ROM_Walnut_Creek_March_1992.iso
/
msdos
/
math
/
praxis.arc
/
PRAXIS.I
< prev
next >
Wrap
Text File
|
1987-07-15
|
23KB
|
548 lines
(****************************************************************************)
(* p r o c e d u r e p r a x i s *)
(* *)
(* p r a x i s is for the minimization of a function in several *)
(* variables. the algorithm used is a modification of a conjugate *)
(* gradient method developed by powell. changes are due to brent, *)
(* who gives an algol w program. *)
(* users who are interested in more of the details should read *)
(* - powell, m. j. d., 1962. an efficient method for finding *)
(* the minimum of a function of several variables without *)
(* calculating derivatives, computer journal 7, 155-162. *)
(* - brent, r. p., 1973. algorithms for minimization without *)
(* derivatives. prentice hall, englewood cliffs. *)
(* if you have any further comments, questions, etc. please feel free *)
(* and contact me. *)
(* *)
(* karl gegenfurtner 02/17/86 *)
(* turbo - p version 1.0 *)
(****************************************************************************)
(* the use of praxis is fairly simple. there are only two parameters: *)
(* - x is of type vector and contains on input an initial guess *)
(* of the solution. on output x holds the final solution of the *)
(* system of equations. *)
(* - n is of type integer and gives the number of unknown parameters *)
(* the result of praxis is the least calculated value of the function f *)
(* f is one of the global parameters used by praxis: *)
(* - f(x,n) is declared forward and is the function to be minimized *)
(* all other globals are optional, i.e. you can change them or else *)
(* praxis will use some default values, which are adequate for most *)
(* problems under consideration. *)
(* - prin controls the printout from praxis *)
(* 0: no printout at all *)
(* 1: only initial and final values *)
(* 2: detailed map of the minimization process *)
(* 3: also prints eigenvalues and eigenvectors of the *)
(* direction matices in use (for insiders only). *)
(* - t is the tolerance for the precision of the solution *)
(* praxis returns if the criterion *)
(* 2 * ||x(k)-x(k-1)|| <= sqrt(macheps) * ||x(k)|| + t *)
(* is fulfilled more than ktm times, where *)
(* - ktm is another global parameter. it's default value is 1 and *)
(* a value of 4 leads to a very(!) cautious stopping criterion *)
(* - macheps is the relative machine precision and is *)
(* 1.0 e-15 using an 8087 and turbo-87 and *)
(* 1.0 e-06 without 8087. *)
(* - h is a steplength parameter and shoul be set to the expected *)
(* distance to the solution. an exceptional large or small value *)
(* of h leads to slower 3 on the first few iterations. *)
(* - scbd is a scaling parameter and should be set to about 10. *)
(* the default is 1 and with that value no scaling is done at all *)
(* it's only necessary when the parameters are scaled very different *)
(* - illc is a boolean variable and should be set to true if the *)
(* problem is known to be illconditioned. *)
(****************************************************************************)
function power(a,b: real):real;
begin power := exp(b*ln(a));
end; (* power *)
procedure minfit(n:integer;eps,tol:real;var ab:matrix;var q:vector);
label 1,
2,
3,
4;
var l, kt,
l2,
i, j, k: integer;
c, f, g,
h, s, x,
y, z: real;
e: vector;
begin (* householders reduction to bidiagonal form *)
x:= 0; g:= 0;
for i:= 1 to n do
begin e[i]:= g; s:= 0; l:= i+1;
for j:= i to n do
s:= s + sqr(ab[j,i]);
if s<tol then g:= 0 else
begin f:= ab[i,i];
if f<0
then g:= sqrt(s)
else g:= - sqrt(s);
h:= f*g-s; ab[i,i]:= f - g;
for j:= l to n do
begin f:= 0;
for k:= i to n do
f:= f + ab[k,i]*ab[k,j];
f:= f/h;
for k:= i to n do
ab[k,j]:= ab[k,j] + f*ab[k,i];
end; (* j *)
end; (* if *)
q[i]:= g; s:= 0;
if i<=n
then for j:= l to n do
s:= s + sqr(ab[i,j]);
if s<tol then g:= 0 else
begin f:= ab[i,i+1];
if f<0
then g:= sqrt(s)
else g:= - sqrt(s);
h:= f*g-s; ab[i,i+1]:= f-g;
for j:= l to n do e[j]:= ab[i,j]/h;
for j:= l to n do
begin s:= 0;
for k:= l to n do s:= s + ab[j,k]*ab[i,k];
for k:= l to n do ab[j,k]:= ab[j,k] + s*e[k];
end; (* j *)
end; (* if *)
y:= abs(q[i])+abs(e[i]);
if y > x then x:= y;
end; (* i *)
(* accumulation of right hand transformations *)
for i:= n downto 1 do
begin if g<>0.0 then
begin h:= ab[i,i+1]*g;
for j:= l to n do ab[j,i]:= ab[i,j]/h;
for j:= l to n do
begin s:= 0;
for k:= l to n do s:= s + ab[i,k]*ab[k,j];
for k:= l to n do ab[k,j]:= ab[k,j] + s*ab[k,i];
end; (* j *)
end; (* if *)
for j:= l to n do
begin ab[j,i]:= 0;
ab[i,j]:= 0;
end;
ab[i,i]:= 1; g:= e[i]; l:= i;
end; (* i *)
(* diagonalization to bidiagonal form *)
eps:= eps*x;
for k:= n downto 1 do
begin kt:= 0;
1: kt:= kt + 1;
if kt > 30 then
begin e[k]:= 0;
writeln('+++ qr failed');
end;
for l2:= k downto 1 do
begin l:= l2;
if abs(e[l])<=eps then goto 2;
if abs(q[l-1])<=eps then goto 4;
end; (* l2 *)
4: c:= 0; s:= 1;
for i:= l to k do
begin f:= s*e[i]; e[i]:= c*e[i];
if abs(f)<=eps then goto 2;
g:= q[i];
if abs(f) < abs(g)
then h:= abs(g)*sqrt(1+sqr(f/g))
else if f <> 0
then h:= abs(f)*sqrt(1+sqr(g/f))
else h:= 0;
q[i]:= h;
if h = 0 then
begin h:= 1;
g:= 1;
end;
c:= g/h; s:= -f/h;
end; (* i *)
2:
z:= q[k];
if l=k then goto 3;
(* shift from bottom 2*2 minor *)
x:= q[l]; y:= q[k-1]; g:= e[k-1]; h:= e[k];
f:= ((y-z)*(y+z) + (g-h)*(g+h))/(2*h*y);
g:= sqrt(sqr(f)+1);
if f<=0
then f:= ((x-z)*(x+z)+h*(y/(f-g)-h))/x
else f:= ((x-z)*(x+z)+h*(y/(f+g)-h))/x;
(* next qr transformation *)
s:= 1; c:= 1;
for i:= l+1 to k do
begin g:= e[i]; y:= q[i]; h:= s*g; g:= g*c;
if abs(f)<abs(h)
then z:= abs(h)*sqrt(1+sqr(f/h))
else if f<>0
then z:= abs(f)*sqrt(1+sqr(h/f))
else z:= 0;
e[i-1]:= z;
if z=0 then
begin f:= 1;
z:= 1;
end;
c:= f/z; s:= h/z;
f:= x*c + g*s; g:= - x*s + g*c; h:= y*s;
y:= y*c;
for j:= 1 to n do
begin x:= ab[j,i-1]; z:= ab[j,i];
ab[j,i-1]:= x*c + z*s;
ab[j,i]:= - x*s + z*c;
end;
if abs(f)<abs(h)
then z:= abs(h)*sqrt(1+sqr(f/h))
else if f<>0
then z:= abs(f)*sqrt(1+sqr(h/f))
else z:= 0;
q[i-1]:= z;
if z=0 then
begin z:= 1;
f:= 1;
end;
c:= f/z; s:= h/z;
f:= c*g + s*y; x:= -s*g + c*y;
end; (* i *)
e[l]:= 0; e[k]:= f; q[k]:= x;
goto 1;
3: if z<0 then
begin q[k]:= -z;
for j:= 1 to n do ab[j,k]:= - ab[j,k];
end;
end; (* k *)
end; (* minfit *)
function praxis(var x:vector; n:integer):real;
label 5, 6, 7;
var i, j,
k, k2,
nl, nf, kl, kt: integer;
s, sl, dn, dmin,
fx, f1,
lds, ldt, sf, df,
qf1, qd0, qd1,
qa, qb, qc,
m2, m4,
small, vsmall,
large, vlarge,
ldfac, t2: real;
d, y, z,
q0, q1: vector;
v: matrix;
procedure sort; (* sort d and v in descending order *)
var k, i, j: integer;
s: real;
begin for i:= 1 to n-1 do
begin k:= i; s:= d[i];
for j:= i+1 to n do
if d[j] > s then
begin k:= j; s:= d[j];
end;
if k>i then
begin d[k]:= d[i]; d[i]:= s;
for j:= 1 to n do
begin s:= v[j,i];
v[j,i]:= v[j,k];
v[j,k]:= s;
end;
end; (* if *)
end; (* for *)
end; (* sort *)
procedure print; (* print a line of traces *)
var i: integer;
begin writeln('------------------------------------------------------');
writeln('chi square reduced to ... ', fx);
writeln(' ... after ',nf,' function calls ...');
writeln(' ... including ',nl,' linear searches.');
writeln('current values of x ...');
for i:= 1 to n do
write(x[i]);
writeln;
end; (* print *)
procedure matprint(s:prstring;v:matrix;n,m:integer);
var k, i: integer;
begin writeln;
writeln(s);
for k:= 1 to n do
begin for i:= 1 to m do
write(v[k,i]);
writeln;
end;
writeln;
end; (* matprint *)
procedure vecprint(s:prstring;v:vector;n:integer);
var i: integer;
begin writeln;
writeln(s);
for i:= 1 to n do
write(v[i]);
writeln;
end; (* vecprint *)
procedure min(j, nits:integer; var d2, x1:real; f1:real;fk:boolean);
label 5, 6;
var k, i: integer;
dz: boolean;
x2, xm, f0,
f2, fm, d1, t2,
s, sf1, sx1: real;
function flin(l:real):real;
var i: integer;
t: vector;
begin if j>0 then (* linear search *)
for i:= 1 to n do
t[i]:= x[i]+l*v[i,j]
else begin (* search along parabolic space curve *)
qa:= l*(l-qd1)/(qd0*(qd0+qd1));
qb:= (l+qd0)*(qd1-l)/(qd0*qd1);
qc:= l*(l+qd0)/(qd1*(qd0+qd1));
for i:= 1 to n do
t[i]:= qa*q0[i]+qb*x[i]+qc*q1[i];
end; (* else *)
nf:= nf+1;
flin:= f(t, n);
end; (* flin *)
begin (* min *)
sf1:= f1; sx1:= x1;
k:= 0; xm:= 0; fm:= fx; f0:= fx; dz:= (d2<macheps);
(* find step size *)
s:= 0; for i:= 1 to n do s:= s + sqr(x[i]);
s:= sqrt(s);
if dz
then t2:= m4*sqrt(abs(fx)/dmin + s*ldt) + m2*ldt
else t2:= m4*sqrt(abs(fx)/d2 + s*ldt) + m2*ldt;
s:= m4*s + t;
if dz and (t2>s) then t2:= s;
if (t2<small) then t2:= small;
if (t2>(0.01*h)) then t2:= 0.01*h;
if fk and (f1<=fm) then begin xm:= x1; fm:= f1; end;
if not fk or (abs(x1)<t2) then
begin if x1>0 then x1:= t2 else x1:= - t2;
f1:= flin(x1);
end;
if f1<=fm then begin xm:= x1; fm:= f1; end;
5: if dz then
begin if f0<f1 then x2:= - x1 else x2:= 2*x1;
f2:= flin(x2);
if f2 <= fm then begin xm:= x2; fm:= f2; end;
d2:= (x2*(f1-f0) - x1*(f2-f0))/(x1*x2*(x1-x2));
end;
d1:= (f1-f0)/x1 - x1*d2; dz:= true;
if d2 <= small
then if d1<0
then x2:= h
else x2:= -h
else x2:= -0.5*d1/d2;
if abs(x2)>h
then if x2>0
then x2:= h
else x2:= -h;
6: f2:= flin(x2);
if (k<nits) and (f2>f0) then
begin k:= k + 1;
if (f0<f1) and ((x1*x2)>0) then goto 5;
x2:= 0.5*x2; goto 6;
end;
nl:= nl + 1;
if f2>fm then x2:= xm else fm:= f2;
if abs(x2*(x2-x1))>small
then d2:= (x2*(f1-f0) - x1*(fm-f0))/(x1*x2*(x1-x2))
else if k>0
then d2:= 0;
if d2<=small then d2:= small;
x1:= x2; fx:= fm;
if sf1<fx then begin fx:= sf1; x1:= sx1; end;
if j>0
then for i:= 1 to n do
x[i]:= x[i] + x1*v[i,j];
end; (* min *)
procedure quad; (* look for a minimum along the curve q0, q1, q2 *)
var i: integer;
l, s: real;
begin s:= fx; fx:= qf1; qf1:= s; qd1:= 0;
for i:= 1 to n do
begin s:= x[i]; l:= q1[i]; x[i]:= l; q1[i]:= s;
qd1:= qd1 + sqr(s - l);
end;
s:= 0; qd1:= sqrt(qd1); l:= qd1;
if (qd0>0) and (qd1>0) and (nl >= (3*n*n)) then
begin min(0, 2, s, l, qf1, true);
qa:= l*(l-qd1)/(qd0*(qd0+qd1));
qb:= (l+qd0)*(qd1-l)/(qd0*qd1);
qc:= l*(l+qd0)/(qd1*(qd0+qd1));
end else
begin fx:= qf1; qa:= 0; qb:= 0; qc:= 1;
end;
qd0:= qd1;
for i:= 1 to n do
begin s:= q0[i]; q0[i]:= x[i];
x[i]:= qa*s + qb*x[i] + qc*q1[i];
end;
end; (* quad *)
begin (**** p r a x i s ****)
(* initialization *)
small:= sqr(macheps); vsmall:= sqr(small);
large:= 1.0/small; vlarge:= 1.0/vsmall;
m2:= sqrt(macheps); m4:= sqrt(m2);
if illc then ldfac:= 0.1 else ldfac:= 0.01;
nl:= 0; kt:= 0; nf:= 1; fx:= f(x, n); qf1:= fx;
t2:= small + abs(t); t:= t2; dmin:= small;
if h<(100*t) then h:= 100*t; ldt:= h;
for i:= 1 to n do for j:= 1 to n do
if i=j then v[i,j]:= 1 else v[i,j]:= 0;
d[1]:= 0; qd0:= 0;
for i:= 1 to n do q1[i]:= x[i];
if prin > 1 then
begin writeln;writeln('---------- enter function praxis ------------');
writeln('current paramter settings are:');
writeln('... scaling ... ',scbd);
writeln('... macheps ... ',macheps);
writeln('... tol ... ',t);
writeln('... maxstep ... ',h);
writeln('... illc ... ',illc);
writeln('... ktm ... ',ktm);
end;
if prin > 0 then print;
5: (* main loop *)
sf:= d[1]; s:= 0; d[1]:= 0;
(* minimize along first direction *)
min(1, 2, d[1], s, fx, false);
if s<= 0
then for i:= 1 to n do
v[i,1]:= - v[i,1];
if (sf<= (0.9*d[1])) or ((0.9*sf)>=d[1])
then for i:= 2 to n do
d[i]:= 0;
for k:= 2 to n do
begin for i:= 1 to n do y[i]:= x[i];
sf:= fx;
illc:= illc or (kt>0);
6: kl:= k; df:= 0;
if illc then (* random step to get off resolution valley *)
begin for i:= 1 to n do
begin z[i]:= (0.1*ldt + t2*power(10,kt))*(random(1)-0.5);
s:= z[i];
for j:= 1 to n do x[j]:= x[j]+s*v[j,i];
end; (* i *)
fx:= f(x, n); nf:= nf + 1;
end; (* if *)
for k2:= k to n do (* minimize along non-conjugate directions *)
begin sl:= fx; s:= 0;
min(k2, 2, d[k2], s, fx, false);
if illc
then s:= d[k2]*sqr(s+z[k2])
else s:= sl - fx;
if df<s then begin df:= s; kl:= k2; end;
end; (* k2 *)
if not illc and (df < abs(100*macheps*fx)) then
begin illc:= true; goto 6;
end;
if (k=2) and (prin>1) then vecprint('new direction ...', d, n);
for k2:= 1 to k-1 do (* minimize along conjugate directions *)
begin s:= 0;
min(k2, 2, d[k2], s, fx, false);
end; (* k2 *)
f1:= fx; fx:= sf; lds:= 0;
for i:= 1 to n do
begin sl:= x[i]; x[i]:= y[i]; y[i]:= sl - y[i]; sl:= y[i];
lds:= lds + sqr(sl);
end;
lds:= sqrt(lds);
if lds>small then
begin for i:= kl-1 downto k do
begin for j:= 1 to n do v[j,i+1]:= v[j,i];
d[i+1]:= d[i];
end;
d[k]:= 0;
for i:= 1 to n do v[i,k]:= y[i]/lds;
min(k, 4, d[k], lds, f1, true);
if lds<=0 then
begin lds:= -lds;
for i:= 1 to n do v[i,k]:= -v[i,k];
end;
end; (* if *)
ldt:= ldfac*ldt; if ldt<lds then ldt:= lds;
if prin > 1 then print;
t2:= 0; for i:= 1 to n do t2:= t2 + sqr(x[i]);
t2:= m2*sqrt(t2) + t;
if ldt > (0.5*t2) then kt:= 0 else kt:= kt+1;
if kt>ktm then goto 7;
end; (* k *)
(* try quadratic extrapolation in case *)
(* we are stuck in a curved valley *)
quad;
dn:= 0;
for i:= 1 to n do
begin d[i]:= 1.0/sqrt(d[i]);
if dn<d[i] then dn:= d[i];
end;
if prin>2 then matprint('new matrix of directions ...', v, n, n);
for j:= 1 to n do
begin s:= d[j]/dn;
for i:= 1 to n do v[i,j]:= s*v[i,j];
end;
if scbd > 1 then (* scale axis to reduce condition number *)
begin s:= vlarge;
for i:= 1 to n do
begin sl:= 0;
for j:= 1 to n do sl:= sl + sqr(v[i,j]);
z[i]:= sqrt(sl);
if z[i]<m4 then z[i]:= m4;
if s>z[i] then s:= z[i];
end; (* i *)
for i:= 1 to n do
begin sl:= s/z[i]; z[i]:= 1.0/sl;
if z[i] > scbd then
begin sl:= 1/scbd;
z[i]:= scbd;
end;
end;
end; (* if *)
for i:= 2 to n do for j:= 1 to i-1 do
begin s:= v[i,j]; v[i,j]:= v[j,i]; v[j,i]:= s;
end;
minfit(n, macheps, vsmall, v, d);
if scbd>1 then
begin for i:= 1 to n do
begin s:= z[i];
for j:= 1 to n do v[i,j]:= v[i,j]*s;
end;
for i:= 1 to n do
begin s:= 0;
for j:= 1 to n do s:= s + sqr(v[j,i]);
s:= sqrt(s); d[i]:= s*d[i]; s:= 1.0/s;
for j:= 1 to n do v[j,i]:= s*v[j,i];
end;
end;
for i:= 1 to n do
begin if (dn*d[i])>large
then d[i]:= vsmall
else if (dn*d[i])<small
then d[i]:= vlarge
else d[i]:= power(dn*d[i],-2);
end;
sort; (* the new eigenvalues and eigenvectors *)
dmin:= d[n];
if dmin < small then dmin:= small;
illc:= (m2*d[1]) > dmin;
if (prin > 2) and (scbd > 1)
then vecprint('scale factors ...', z, n);
if prin > 2 then vecprint('eigenvalues of a ...', d, n);
if prin > 2 then matprint('eigenvectors of a ...', v, n, n);
goto 5; (* back to main loop *)
7: if prin > 0 then
begin vecprint('final solution is ...', x, n);
writeln; writeln('chisq reduced to ',fx,' after ',nf, ' function calls.');
end;
praxis:= fx;
end; (**** praxis ****)