This repository has been archived on 2023-08-20. You can view files and clone it, but cannot push or open issues or pull requests.
yap-6.3/CLPQR/clpqr/examples/monash/rkf45

512 lines
16 KiB
Plaintext
Raw Normal View History

/*
**********************************************************************
*
* CLP(R) Version 2.0 (Example Programs Release)
* (C) Copyright, March 1986, Monash University
*
**********************************************************************
*/
% use clpr -z 1e-20
/*
* RKF45 implementation in CLP(R): See [Harland & Michaylov].
*
* Assumes that the function is evaluated by a predicate of the form
*
* eval(T, Y, Yp), where T is the independent variable,
* Y the current vector of solutions,
* Yp the new vector derivative
*
* e.g. eval(T, [Y1,Y2], [T*T + Y1*Y1, T*T - Y2*Y2]).
*
* Note: machine epsilon is available as #zero (pre-defined constant).
*
* Separate "mode" from the rest - may help efficiency
*/
/*
* solve(Y, T, Tout, Incr, Relerr, Abserr)
* Predicate called by the user - if he wants to specify a mode, he calls
* solve/7 direct (only needed for single_step mode).
*
* Y - initial position vector
* T - starting point for independent variable
* Tout - last point at which output is desired
* Incr - default output increment
* Relerr - relative error tolerance
* Abserr - absolute error tolerance
*/
solve(Y, Tstart, Tend, Incr, Relerr, Abserr) :-
solve(Y, Tstart, Tend, Incr, Relerr, Abserr, normal).
/*
* Error checks
*/
solve(_, _, _, 0, _, _, normal) :-
printf("Increment must be non-zero for normal mode\n",[]).
solve(_, _, _, _, Relerr, _, _) :-
Relerr < 0,
printf("Relative error must be non-negative\n",[]).
solve(_, _, _, _, _, Abserr, _) :-
Abserr < 0,
printf("Relative error must be non-negative\n",[]).
solve(_, T, T, _, _, _, _) :-
printf("No interval\n",[]).
/*
* Everything seems ok, so proceed
*/
solve(Y, T, Tend, Incr, Relerr, Abserr, Mode) :-
set_yp(T, Y, Yp), % Nfe = Nfe + 1
output(T, Y),
solve1(user(Y, T, Tend, Incr, Relerr, Abserr, Mode),
work(Yp, 1, 0)). % Work out the rest
% Nfe = 1
% Kop = 0
/*
* Commence main iteration
*/
solve1(user(_, T, Tend, Incr, _, _, _), work(_, _, Nfe, _, _)) :-
abs(T - Tend) < Incr / 4,
printf("\nIteration finished\n------------------\n",[]),
printf(" %d derivative evaluations\n",[Nfe]).
solve1(user(Y, T, Tend, Incr, Relerr, Abserr, reset(Mode, relerr)), _) :-
remin(Remin),
Relerr < 2* #zero + Remin,
printf("Warned you to reset it larger than %f\n", [Relerr]).
solve1(user(Y, T, Tend, Incr, Relerr, Abserr, reset(Mode, relerr)),
work(Yp, H, Nfe, Kop, Tout)) :-
remin(Remin),
Relerr >= 2* #zero + Remin,
solve1(user(Y, T, Tend, Incr, Relerr, Abserr, Mode),
work(Yp, H, Nfe, Kop, Tout)).
solve1(user(Y, T, Tend, Incr, Relerr, Abserr, reset(Mode, abserr, Oerr)), _) :-
Abserr <= Oerr,
printf("Abserr must be increased - not set to %f\n", [Abserr]).
solve1(user(Y, T, Tend, Incr, Relerr, Abserr, reset(Mode, abserr, Oerr)),
work(Yp, H, Nfe, Kop, Tout)) :-
Abserr > Oerr,
solve1(user(Y, T, Tend, Incr, Relerr, Abserr, Mode),
work(Yp, H, Nfe, Kop, Tout)).
solve1(user(Y, T, Tend, Incr, Relerr, Abserr, Mode), work(Yp, Nfe, Kop)) :-
% First time - need to set H
set_tout(T, Tend, Incr, Tout), % Next output point
set_hinit(T, Tout-T, Y, Yp, Relerr, Abserr, H),
set_kop(Kop, H, Tout-T, Newkop),
stop_or_iter(user(Y, T, Tend, Incr, Relerr, Abserr, normal),
work(Yp, H, Nfe, Newkop, Tout)).
solve1(user(Y, T, Tend, Incr, Relerr, Abserr, Mode),
% H already set - continue on
work(Yp, H, Nfe, Kop, _)) :-
set_tout(T, Tend, Incr, Tout),
sign(Tout-T, S),
set_kop(Kop, S*H, Tout-T, Newkop),
stop_or_iter(user(Y, T, Tend, Incr, Relerr, Abserr, Mode),
work(Yp, S*H, Nfe, Newkop, Tout)).
/*
* Perform certain error checks
*/
stop_or_iter(User, work(Yp, H, Nfe, Kop, Tout)) :-
Kop >= 100,
check_out(User, work(Yp, H, Nfe, 0, Tout), output_excess).
stop_or_iter(user(Y, T, Tend, Incr, Relerr, Abserr, Mode), Work) :-
remin(Remin),
Relerr < 2* #zero + Remin,
check_out(user(Y, T, Tend, Incr, Relerr, Abserr, Mode), Work, relerr).
stop_or_iter(user(Y, T, Tend, Incr, Relerr, Abserr, Mode),
work(Yp, H, Nfe, Kop, Tout)) :-
Abs = abs(Tout-T),
Tabs = abs(T),
% Use Euler approximation
Abs <= 26* #zero*Tabs,
set_euler(Tout-T, Y, Yp, NewY),
% Nfe = Nfe + 1
set_yp(Tout, NewY, NewYp),
print_or_it(user(NewY, Tout, Tend, Incr, Relerr, Abserr,
mode(Mode, output)), work(NewYp, H, Nfe+1, Kop, Tout)).
stop_or_iter(User, Work) :-
% Go on with main loop .......
iter(User, Work).
iter(user(Y, T, Tend, Incr, Relerr, Abserr, Mode),
work(Yp, H, Nfe, Kop, Tout)) :-
set_modeh(Tout-T, H, NewH, Out),
iter1(user(Y, T, Tend, Incr, Relerr, Abserr, mode(Mode, Out, nofail)),
work(Yp, NewH, Nfe, Kop, Tout)).
iter1(user(Y, T, Tend, Incr, Relerr, Abserr, mode(Mode, Out, Hmode)),
work(Yp, H, Nfe, Kop, Tout)) :-
max_func_eval(Maxfunc),
Nfe >= Maxfunc,
check_out(user(Y, T, Tend, Incr, Relerr, Abserr, Mode),
work(Yp, H, Nfe, Kop, Tout), max_func).
iter1(user(Y, T, Tend, Incr, Relerr, Abserr, Mode),
work(Yp, H, Nfe, Kop, Tout)) :-
fehl(Y, T, Yp, H, Sum, Abserr, Relerr, Errest, Ind),
stop_or_iter1(user(Y, T, Tend, Incr, Relerr, Abserr, Mode),
work(Yp, H, Nfe+5, Kop, Tout), Sum, Errest, Ind).
stop_or_iter1(user(Y, T, Tend, Incr, Relerr, Abserr, mode(Mode, Out, Hmode)),
Work, _, _, vanished) :-
check_out(user(Y, T, Tend, Incr, Relerr, Abserr, Mode), Work, vanished).
stop_or_iter1(user(Y, T, Tend, Incr, Relerr, Abserr, Mode),
work(Yp, H, Nfe, Kop, Tout), Sum, Esttol, okay) :-
Esttol > 1,
set_smin(Esttol, S),
check_h(user(Y, T, Tend, Incr, Relerr, Abserr, Mode),
work(Yp, S*H, Nfe, Kop, Tout)).
stop_or_iter1(user(Y, T, Tend, Incr, Relerr, Abserr, mode(Mode, Out, Hmode)),
work(Yp, H, Nfe, Kop, Tout), Sum, Esttol, okay) :-
Esttol <= 1,
% Nfe = Nfe+1
set_yp(T+H, Sum, NewYp),
set_hmax(Esttol, Hmode, T, H, NewH),
print_or_it(user(Sum, T+H, Tend, Incr, Relerr, Abserr,
mode(Mode, Out)), work(NewYp, NewH, Nfe+1, Kop, Tout)).
print_or_it(user(Y, T, Tend, Incr, Relerr, Abserr,
mode(normal,proceed)), Work) :-
iter(user(Y, T, Tend, Incr, Relerr, Abserr, normal), Work).
print_or_it(user(Y, T, Tend, Incr, Relerr, Abserr,
mode(normal, output)), Work) :-
output(T, Y),
solve1(user(Y, T, Tend, Incr, Relerr, Abserr, normal), Work).
print_or_it(user(Y, T, Tend, Incr, Relerr, Abserr,
mode(single_step, _)), Work) :-
check_out(user(Y, T, Tend, Incr, Relerr, Abserr, single_step), Work).
check_h(user(Y, T, Tend, Incr, Relerr, Abserr, mode(Mode, _, _)),
work(Yp, H, Nfe, Kop, Tout)) :-
Habs = abs(H),
Tabs = abs(T),
Habs <= 26* #zero*Tabs,
printf("Tabs = %e , Habs = %e , #zero = %e \n",[Tabs,Habs,#zero]),
check_out(user(Y, T, Tend, Incr, Relerr, Abserr, Mode),
work(Yp, H, Nfe, Kop, Tout), step_small).
check_h(user(Y, T, Tend, Incr, Relerr, Abserr, mode(Mode, Out, _)), Work) :-
iter1(user(Y, T, Tend, Incr, Relerr, Abserr, mode(Mode,proceed,hfail)),
Work).
/*
* check_out(user(Y, Tstart, Tend, Incr, Relerr, Abserr, Mode),
* work(Yp, H, Nfe, Kop, Tout), Flag)
* Checks the status variable Flag, and takes appropriate action.
* See if there is a user-defined procedure
*/
check_out(User, Work, Flag) :-
user_error(User, Work, Flag),
!.
% No user procedure, so use the default ones.
check_out(user(Y, T, Tend, Incr, Relerr, Abserr, single_step),
Work, single_step) :-
printf("(single_step) T: %10.6f Y: %10.6f\n",[T,Y]),
solve(Y, T, Tend, Incr, Relerr, Abserr, single_step),!.
check_out(user(Y, T, Tend, Incr, Relerr, Abserr, Mode), Work, relerr) :-
% Iflag = 3
remin(Remin),
Rer = 3* #zero + Remin,
printf("Relative error too small - reset to %f\n", [Rer]),
solve1(user(Y, T, Tend, Incr, Rer, Abserr, reset(Mode, relerr)), Work).
check_out(User, work(Yp, H, Nfe, Kop, Tout), max_func) :-
% Iflag = 4
max_func_eval(Max),
Nfe >= Max,
printf("Lots of function evaluations - %f needed so far\n",[Max]),
solve1(User, work(Yp, H, 0, Kop, Tout)).
check_out(user(Y, T, Tend, Incr, Relerr, Abserr, Mode), Work, vanished) :-
% Iflag = 5
abmin(Absmin),
printf("Abserr reset to %f\n", [Absmin]),
solve1(user(Y, T, Tend, Incr, Relerr, 10*Abserr + Absmin,
reset(Mode, abserr, Abserr)), Work).
check_out(user(Y, T, Tend, Incr, Relerr, Abserr, normal), Work, step_small) :-
% Iflag = 6
printf("Relerr too small; reset to %f\n", [10*Relerr]),
solve1(user(Y, T, Tend, Incr, 10*Relerr, Abserr, normal), Work).
check_out(User, work(Yp, H, Nfe, Kop, Tout), output_excess) :-
% Iflag = 7
printf("Excessive output; this will be inefficient",[]),
solve1(User, work(Yp, H, Nfe, 0, Tout)).
check_out(_, _, stuffed) :-
% Iflag = 8
printf("Improper call - wrong number of arguments?\n").
check_out(_, _, missing) :-
printf("User-supplied predicate eval is missing or wrong\n"),
printf("Format is eval(T, Y, Yp),\n",[]),
printf("where T is the independent variable, ",[]),
printf("Y the vector, and Yp its derivative.",[]),
printf("Y and Yp are CLP lists - [Y1, Y2, Y3], [Yp1, Yp2, Yp3] etc.",[]).
check_out(_, _, Other) :-
printf("Damned if I know what''s wrong!\n",[]).
/*
* output(T, Y).
* output(user(Y, T, Tend, Incr, Relerr, Abserr), work(Yp, H, Nfe, Kop, Tout))
* Output predicate. If there is a user_output predicate (matching the above
* format), then it is used. Otherwise, T and Y are written to standard output.
*/
output(T, Y) :-
user_output(T, Y),
!.
output(T, Y) :-
printf("T: %10.6f Y: ",[T]),
write_list(Y).
write_list([L|List]) :-
printf(" %10.6f ",[L]),
write_list(List).
write_list([]) :-
nl.
/*
* Set routines - setting values of variables given certain conditions etc.
*/
set_yp(T, Y, Yp) :-
eval(T, Y, Yp).
set_yp(T, [Y], [Yp]) :-
% For one var case with no list functor
eval(T, Y, Yp).
set_hinit(T, Dt, Y, Yp, Relerr, Abserr, H) :-
H1 = abs(Dt),
set_maxtol(H1, Y, Yp, Relerr, Abserr, Maxtol),
set_h1(H1, T, Dt, Maxtol, H).
set_maxtol(H, [Y|Yrest], [Yp|Yprest], Relerr, Abserr, Maxtol) :-
Yabs = abs(Y),
Tol1 = Relerr*Yabs + Abserr,
Tol1 > 0,
Ypabs = abs(Yp),
set_maxh(Ypabs, H, Tol1, H1),
set_maxtol(H1, Yrest, Yprest, Relerr, Abserr, Tol2),
Maxtol = max(Tol1, Tol2).
set_maxtol(H, [Y|Yrest], [Yp|Yprest], Relerr, Abserr, Maxtol) :-
Yabs = abs(Y),
Relerr*Yabs + Abserr <= 0,
set_maxtol(H, Yrest, Yprest, Relerr, Abserr, Maxtol).
set_maxtol(_, [], [], _, _, 0).
set_maxh(0, H, _, H).
set_maxh(Ypabs, H, Tol, NewH) :-
Ypabs > 0,
NewH = min(H, pow(Tol/Ypabs, 1/5)). % From abs(Yp)*h**5 <= Tol
set_h1(H, T, Dt, Tol, 26* #zero*Max) :-
Tol <= 0,
Tabs = abs(T),
Dtabs = abs(Dt),
Max = max(Tabs, Dtabs).
set_h1(H, T, Dt, Tol, NewH) :-
Tol > 0,
Tabs = abs(T),
Dtabs = abs(Dt),
Max = max(Tabs, Dtabs),
NewH = max(H, 26* #zero*Max).
set_modeh(Dt, H, H, proceed) :-
Dtabs = abs(Dt),
Habs = abs(H),
Dtabs >= 2*Habs.
set_modeh(Dt, H, Dt/2, proceed) :-
Habs = abs(H),
Dtabs = abs(Dt),
Dtabs < 2*Habs,
Dtabs > Habs.
set_modeh(Dt, H, Dt, output) :-
Habs = abs(H),
Dtabs = abs(Dt),
Dtabs <= Habs.
set_kop(Kop, H, Dt, Kop+1) :-
Habs = abs(H),
Dabs = abs(Dt),
Habs >= 2*Dabs.
set_kop(Kop, H, Dt, Kop) :-
Habs = abs(H),
Dabs = abs(Dt),
Habs < 2*Dabs.
set_smin(Esttol, 0.9/(pow(Esttol, 1/5)) ) :-
Esttol < 59049.
set_smin(Esttol, 0.1) :-
Esttol >= 59049.
set_hmax(Esttol, Hmode, T, H, Sign*H1) :-
set_smax(Esttol, Hmode, S),
Habs = abs(H),
Tabs = abs(T),
H1 = max(S*Habs, 26* #zero*Tabs),
sign(H, Sign).
set_smax(Esttol, nofail, 5) :-
Esttol <= 0.0001889568.
set_smax(Esttol, nofail, 0.9/(pow(Esttol, 1/5)) ) :-
Esttol > 0.0001889568.
set_smax(Esttol, hfail, 1) :-
Esttol <= 0.0001889568.
set_smax(Esttol, hfail, S) :-
Esttol > 0.0001889568,
S = min(0.9/(pow(Esttol, 1/5)), 1).
set_euler(Dt, [Y|Yrest], [Yp|Yprest], [NewY|NewYrest]) :-
NewY = Y + Dt*Yp,
set_euler(Dt, Yrest, Yprest, NewYrest).
set_euler(_, [], [], []).
/*
* set_tout(T, Tend, Incr, Tout)
* Tout is the next output point, T the current value, Tend the final point
* and Incr the set increment.
*
* If there is a user-supplied predicate user_tout, then it is used.
* Otherwise, we add Incr to T and proceed.
* Note that Incr may be negative.
*/
set_tout(T, Tend, Incr, Tout) :-
user_tout(T, Tend, Incr, Tout),
!.
set_tout(T, Tend, Incr, Tend) :-
Abs = abs(Tend - (T + Incr)),
Abs < Incr.
set_tout(T, Tend, Incr, T + Incr) :-
Abs = abs(Tend - (T + Incr)),
Abs >= Incr.
/*
* fehl(Y, T, Yp, H, Sum, Abserr, Relerr, Errest, Errind)
* Predicate to perform the evaluation of Fehlberg formulae
*
* Sum is the new estimate for Y
* Abserr is the absolute error.
* Relerr is the relative error.
* Errest is the Fehlberg estimate of the error.
* Errind indicates whether Abs is too small or not.
* If so, Errind is set to "vanished". Otherwise, it is "okay".
*/
fehl(Y, T, Yp, H, S, Abs, Rel, Err, Ind) :-
Ch1 = H/4,
Ch2 = H*3/32,
Ch3 = H/2197,
Ch4 = H/4104,
Ch5 = H/20520,
Ch6 = H/7618050,
fehl1(Y, T, Yp, H, ch(Ch1, Ch2, Ch3, Ch4, Ch5, Ch6),
S, Abs, Rel, Err, Ind).
fehl1(Y, T, Yp, H, ch(Ch1, Ch2, Ch3, Ch4, Ch5, Ch6), S, Abs, Rel, Err, Ind) :-
% calculate each of the Fi .....
set_f1(Y, Yp, Ch1, P1), set_yp(T+Ch1, P1, F1),
set_f2(Y, Yp, Ch2, F1, P2), set_yp(T+ 3*H/8, P2, F2),
set_f3(Y, Yp, Ch3, F1, F2, P3), set_yp(T + 12*H/13, P3, F3),
set_f4(Y, Yp, Ch4, F1, F2, F3, P4), set_yp(T + H, P4, F4),
set_f5(Y, Yp, Ch5, F1, F2, F3, F4, P5), set_yp(T + H/2, P5, F5),
% .. then the Y estimate ....
set_sum(Y, Yp, Ch6, F2, F3, F4, F5, S),
% .. and finally the error
set_err(Y, Yp, Abs, Rel, S, f(F2, F3, F4, F5), H, 0, Err, Ind).
set_f1([Y|Yr], [Yp|Ypr], Ch, [P|Pr]) :-
P = Y + Ch*Yp,
set_f1(Yr, Ypr, Ch, Pr).
set_f1([], [], _, []).
set_f2([Y|Yr], [Yp|Ypr], Ch, [F1|F1r], [P|Pr]) :-
P = Y + Ch*(Yp + 3*F1),
set_f2(Yr, Ypr, Ch, F1r, Pr).
set_f2([], [], _, [], []).
set_f3([Y|Yr], [Yp|Ypr], Ch, [F1|F1r], [F2|F2r], [P|Pr]) :-
P = Y + Ch*(1932*Yp + (7296*F2 - 7200*F1)),
set_f3(Yr, Ypr, Ch, F1r, F2r, Pr).
set_f3([], [], _, [], [], []).
set_f4([Y|Yr], [Yp|Ypr], Ch, [F1|F1r], [F2|F2r], [F3|F3r], [P|Pr]) :-
P = Y + Ch*((8341*Yp - 845*F3) + (29440*F2 - 32832*F1)),
set_f4(Yr, Ypr, Ch, F1r, F2r, F3r, Pr).
set_f4([], [], _, [], [], [], []).
set_f5([Y|Yr], [Yp|Ypr], Ch, [F1|F1r], [F2|F2r], [F3|F3r], [F4|F4r], [P|Pr]) :-
P = Y + Ch*((0-6080*Yp + (9295*F3 - 5643*F4)) + (41040*F1-28352*F2)),
set_f5(Yr, Ypr, Ch, F1r, F2r, F3r, F4r, Pr).
set_f5([], [], _, [], [], [], [], []).
set_sum([Y|Yr], [Yp|Ypr], Ch, [F2|F2r], [F3|F3r], [F4|F4r], [F5|F5r], [S|Sr]) :-
S = Y + Ch*((902880*Yp + (3855735*F3 - 1371249*F4)) + (3953664*F2 +
277020*F5)),
set_sum(Yr, Ypr, Ch, F2r, F3r, F4r, F5r, Sr).
set_sum([], [], _, [], [], [], [], []).
set_err([Y|Yr], Yp, Abs, Rel, [S|Sr], f(F2, F3, F4, F5), H, Oerr, Err, Ind) :-
Yabs = abs(Y),
Sabs = abs(S),
Et = Yabs + Sabs + Abs,
check_err(Et, [Y|Yr], Yp, [S|Sr], f(F2, F3, F4, F5),
Abs, Rel, H, Oerr, Err, Ind).
set_err([], [], _, Rel, [], _, H, Err, Habs*Err*(2/Rel)/752400, okay) :-
Habs = abs(H).
check_err(Et, Y, Yp, S, _, _, _, _, _, _, vanished) :-
Et <= 0.
check_err(Et, [Y|Yr], [Yp|Ypr], [S|Sr], f([F2|R2], [F3|R3], [F4|R4], [F5|R5]),
Abs, Rel, H, Oerr, Err, Ind) :-
Et > 0,
Ee = abs( (0-2090*Yp + (21970*F3 - 15048*F4)) + (22528*F2 - 27360*F5)),
Nerr = max(Oerr, Ee/Et),
set_err(Yr, Ypr, Abs, Rel, Sr, f(R2, R3, R4, R5), H, Nerr, Err, Ind).
% Low level stuff - primitives etc.
sign(V, 1) :-
V >= 0.
sign(V, 0-1) :-
V < 0.
% Magic numbers
remin(0.000000000001). % 1.0e-012 - from FM&M.
abmin(0.000000001). % 1.0e-009 - from FM&M.
max_func_eval(3000). % Also from FF&M.
eval(T, [Y1, Y2, Y3, Y4], [Y3, Y4, 0-Y1/R, 0-Y2/R]) :-
R1 = Y1*Y1 + Y2*Y2,
R = R1*pow(R1, 0.5)/((#p/4)*(#p/4)).
user_output(T, [Y1, Y2, Y3, Y4]) :-
printf("Point %10.5f : %10.5f %10.5f\n",[T,Y1,Y2]).
user_error(user(Y, T, Tend, Incr, Rer, Abs),
work(Yp, H, Nfe, Kop, Tout), max_func) :-
printf("Too hard for me - try something more accurate\n",[]).
user_error(user(Y, T, Tend, Incr, Rer, Abs),
work(Yp, H, Nfe, Kop, Tout), output_excess) :-
printf("Outputs too often - try another method\n",[]).
user_error(user(Y, T, Tend, Incr, Rer, Abs),
work(Yp, H, Nfe, Kop, Tout), step_small) :-
printf("Can''t achieve required accuracy\n",[]).
go:- Ecc = 0.25, solve([1-Ecc, 0, 0, (#p/4)*pow((1+Ecc)/(1-Ecc), 0.5)],
0, 3, 0.5, 0.000000001, 0).
% Output:
%
% Point 0.00000 : 0.75000 0.00000
% Point 0.50000 : 0.61977 0.47779
% Point 1.00000 : 0.29442 0.81218
% Point 1.50000 : -0.10518 0.95804
% Point 2.00000 : -0.49030 0.93987
% Point 2.50000 : -0.81394 0.79959
% Point 3.00000 : -1.05403 0.57571
%
% Iteration finished
% ------------------
% 439 derivative evaluations
?- printf("\n>>> Sample goal: go/0\n", []).