(* port of taylor functions, taylor interval *)
(*
The first part of the file implements basic operations on type taylor_interval.
Then a type tfunction is defined that represents a twice continuously
differentiable function of six variables. It can be evaluated, which
is the taylor_interval data associated with it.
Sometimes a tfunction f is used to represent an inequality f < 0.
(See recurse.hl.
*)
module Taylor = struct
open Interval;;
open Univariate;;
open Line_interval;;
(* general utilities *)
let m8_sum =
let ( + ) = iadd in
fun dd1 dd2 ->
let r8_sum (x,y) = table (fun i -> mth x i + mth y i) in
map r8_sum (zip dd1 dd2);;
let center_form(x,z) =
let ( + ) , ( - ), ( / ) = up(); upadd,upsub,updiv in
let y = table (fun i -> if (mth x i=mth z i) then mth x i else (mth x i + mth z i)/ 2.0) in
let w = table (fun i -> max (mth z i - mth y i) (mth y i - mth x i)) in
let _ = (minl w >= 0.0) or failwith "centerform" in
(y,w);;
(* start with taylor interval operations *)
let make_taylor_interval (l1,w1,dd1) = {l = l1; w = w1; dd=dd1;};;
let ti_add (ti1,ti2) =
let _ = (ti1.w = ti2.w) or failwith ("width mismatch in ti") in
make_taylor_interval( ladd ti1.l ti2.l,ti1.w, m8_sum ti1.dd ti2.dd);;
let ti_scale (ti,t) =
make_taylor_interval( smul ti.l t,ti.w, table2 (fun i j -> imul (mth2 ti.dd i j) t));;
let taylor_error ti =
let ( + ), ( * ) , ( / )= up(); upadd, upmul, updiv in
let dot_abs_row r = List.fold_left2 (fun a b c -> a + b * iabs c) 0.0 ti.w r in
let dots = map dot_abs_row (ti.dd) in
(List.fold_left2 (fun a b c -> a + b * c) 0.0 ti.w dots) / 2.0;;
(* (end_itlist ( + ) p) / 2.0 ;; *)
let upper_bound ti =
let e = taylor_error ti in
let ( + ), ( * ) = up(); upadd, upmul in
let t = ti.l.f.hi + e in
t + List.fold_left2 (fun a b c -> a + b * iabs c) 0.0 ti.w ti.l.df;;
let lower_bound ti =
let e = taylor_error ti in
let ( + ), ( * ),(- ) = down(); downadd,downmul,downsub in
let t = ti.l.f.lo - e in
t + List.fold_left2 (fun a b c -> a + ( ~-. b) * iabs c) 0.0 ti.w ti.l.df;;
let upper_partial ti i =
let ( + ), ( * ) = up(); upadd,upmul in
let err = List.fold_left2 (fun a b c -> a + b*(max c.hi (~-. (c.lo))))
0.0 ti.w (mth ti.dd i) in
err + Interval.sup ( mth ti.l.df i);;
let lower_partial ti i =
let ( + ), ( * ), ( - ) = down();downadd,downmul,downsub in
let err = List.fold_left2 (fun a b c -> a + b * min c.lo (~-. (c.hi)))
0.0 ti.w (mth ti.dd i) in
Interval.inf ( mth ti.l.df i) + err;;
let ti_mul (ti1,ti2) =
let _ = (ti1.w = ti2.w) or failwith ("ti_mul: width mismatch in ti") in
let line = lmul ti1.l ti2.l in
let f1_int =
let lo, hi = lower_bound ti1, upper_bound ti1 in mk_interval (lo, hi) in
let f2_int =
let lo, hi = lower_bound ti2, upper_bound ti2 in mk_interval (lo, hi) in
let d1_ints = table (fun i -> mk_interval (lower_partial ti1 i, upper_partial ti1 i)) in
let d2_ints = table (fun i -> mk_interval (lower_partial ti2 i, upper_partial ti2 i)) in
let dd = table2 (fun i j ->
let ( + ), ( * ) = iadd, imul in
mth2 ti1.dd i j * f2_int + mth d1_ints i * mth d2_ints j +
mth d1_ints j * mth d2_ints i + f1_int * mth2 ti2.dd i j) in
make_taylor_interval(line, ti1.w, dd);;
(* primitive A *)
type primitiveA = {
f_df : int -> float list -> float list -> interval;
hfn : float list -> line;
second : float list -> float list -> interval list list;
};;
let make_primitiveA (f,h1,s1) = {f_df = f; hfn = h1; second = s1; };;
let unitA =
let zero2 = table2 (fun i j -> zero) in
make_primitiveA (
(fun i x z -> if i = 0 then one else zero),
(fun y -> line_unit),
(fun x z -> zero2)
);;
let evalf4A pA w x y z =
make_taylor_interval(
pA.hfn y,
w,
pA.second x z
);;
let line_estimateA pA y = pA.hfn y;;
(* primitive U *)
type primitiveU = {
slot: int;
uv: univariate;
};;
let mk_primitiveU s1 uv1 =
let _ = (s1 < 8) or failwith (Printf.sprintf "slot %d" s1) in
{ slot = s1; uv = uv1; };;
let line_estimateU p y =
let y0 = mth y p.slot in
let t = mk_interval(y0,y0) in
let d = table (fun i -> if (i=p.slot) then eval p.uv t 1 else zero) in
mk_line ( eval p.uv t 0, d );;
let evalf4U =
let row0 = table (fun i -> zero) in
fun p w x y z ->
let t = mk_interval(mth x p.slot,mth z p.slot) in
let row_slot = table (fun i -> if (i=p.slot) then eval p.uv t 2 else zero) in
let dd = table (fun i -> if (i=p.slot) then row_slot else row0) in
make_taylor_interval(
line_estimateU p y,
w,
dd
);;
type tfunction =
| Prim_a of primitiveA
| Uni of primitiveU
| Plus of tfunction * tfunction
| Product of tfunction * tfunction
| Scale of tfunction * interval
| Uni_compose of univariate * tfunction
| Composite of tfunction * (* F(g1,g2,g3,g4,g5,g6,g7,g8) *)
tfunction *tfunction *tfunction *
tfunction *tfunction *tfunction *
tfunction *tfunction;;
let unit = Prim_a unitA;;
let x1 = Uni (mk_primitiveU 0 ux1);;
let x2 = Uni (mk_primitiveU 1 ux1);;
let x3 = Uni (mk_primitiveU 2 ux1);;
let x4 = Uni (mk_primitiveU 3 ux1);;
let x5 = Uni (mk_primitiveU 4 ux1);;
let x6 = Uni (mk_primitiveU 5 ux1);;
let x1x2 =
let tab2 = table2 (fun i j -> if (i+j=1) then one else zero) in
Prim_a (make_primitiveA(
(fun i x z ->
let x1 = mk_interval (mth x 0, mth z 0) in
let x2 = mk_interval (mth x 1, mth z 1) in
if i = 0 then imul x1 x2
else if i = 1 then x2
else if i = 2 then x1
else zero),
(fun y ->
let u1 = mth y 0 in
let u2 = mth y 1 in
let x1 = mk_interval(u1,u1) in
let x2 = mk_interval(u2,u2) in
mk_line(
imul x1 x2,
table (fun i -> if i=0 then x2 else if i=1 then x1 else zero)
)),
(fun x z -> tab2)));;
let tf_product tf1 tf2 = Composite(x1x2,tf1,tf2,unit,unit,unit,unit,unit,unit);;
(* This is one of the most difficult functions in the interval code.
It uses the chain rule to compute the second partial derivatives with
respect to x(i) x(j), of a function composition
F(x1,...,x6) = f(g1(x1,...x6),g2(x1,...x6),...,g6(x1,...x6)).
(F i j) = sum {k m} (f k m) (gk i) (gm j) + sum {r} (f r) (gr i j).
Fast performance of this function is very important, especially
when many of the functions g* are constant.
There is a bit of imperative programming here, in computing the sums.
Note that ( + ) and ( * ) have different types in various subsections.
*)
let eval_composite =
let rest = () in
let sparse_table h f = filter h (List.flatten (table2 f)) in
fun hdr p1 p2 p3 p4 p5 p6 p7 p8 w ->
let p = [p1;p2;p3;p4;p5;p6;p7;p8] in
(* wide and narrow ranges of p *)
let (aw,bw) = map (lower_bound) p, map (upper_bound) p in
let (a,b) = map (fun p -> p.l.f.lo) p, map (fun p -> p.l.f.hi) p in
(* wide and narrow widths from a to b *)
let (u,wu,wf) =
let ( + ),( - ),( / ) = up();upadd,upsub,updiv in
let u = table (fun i -> (mth a i + mth b i) / 2.0) in
let wu = table (fun i -> max (mth bw i - mth u i) (mth u i - mth aw i)) in
let wf = table (fun i -> max (mth b i - mth u i) (mth u i - mth a i)) in
(u,wu,wf) in
let (fu:taylor_interval) = hdr wu aw u bw in
let fpy =
let t = make_taylor_interval(fu.l,wf,fu.dd) in
mk_line (
mk_interval(lower_bound t, upper_bound t),
table (fun i -> mk_interval(lower_partial t i,upper_partial t i)) ) in
(* use chain rule imperatively to compute narrow first derivative *)
let df_tmp = Array.create 8 zero in
let ( + ) = iadd in
let ( * ) = imul in
let _ = for j=0 to 7 do
let dfj = mth fpy.df j in
if is_zero dfj then rest
else for i=0 to 7 do
let r = mth (mth p j).l.df i in
if (is_zero r) then rest else df_tmp.(i) <- df_tmp.(i) + dfj * r;
done;
done in
let lin = mk_line ( fpy.f, Array.to_list df_tmp ) in
(* second derivative init *)
let fW_partial = table (fun i -> mk_interval(lower_partial fu i,upper_partial fu i)) in
let pW_partial = sparse_table (fun (_,_,z) ->not (is_zero z))
(fun k i -> (k,i,(mk_interval(lower_partial (mth p k) i,upper_partial (mth p k) i)))) in
(* chain rule 4-nested loop!, but flattened with sparse table *)
let dcw = Array.make_matrix 8 8 zero in
let _ = for i=0 to 7 do for j=0 to 7 do for k=0 to 7 do
if (is_zero (mth2 (mth p k).dd i j)) then rest
else dcw.(i).(j) <- dcw.(i).(j) + mth fW_partial k * mth2 ((mth p k).dd) i j ;
done; done; done in
let len = List.length pW_partial in
let _ = for ki = 0 to len-1 do
let (k,i,rki) = List.nth pW_partial ki in
for mj=0 to len-1 do
let (m,j,rmj) = List.nth pW_partial mj in
(* Report.report (Printf.sprintf "k i m j rki rmj fuddkm = %d %d %d %d %f %f %f" k i m j rki.lo rmj.lo (mth2 fu.dd k m).lo); *)
dcw.(i).(j) <- dcw.(i).(j) + mth2 fu.dd k m * rki * rmj; (* innermost loop *)
done; done in
let dcw_list = map Array.to_list (Array.to_list dcw) in
make_taylor_interval(lin,w,dcw_list);;
let rec evalf4 tf w x y z = match tf with
| Prim_a p -> evalf4A p w x y z
| Uni p -> evalf4U p w x y z
| Plus (tf1,tf2) -> ti_add(evalf4 tf1 w x y z, evalf4 tf2 w x y z)
| Product (tf1,tf2) -> ti_mul(evalf4 tf1 w x y z, evalf4 tf2 w x y z)
| Composite(h,g1,g2,g3,g4,g5,g6,g7,g8) ->
let [p1;p2;p3;p4;p5;p6;p7;p8] = map (fun t-> evalf4 t w x y z) [g1;g2;g3;g4;g5;g6;g7;g8] in
eval_composite (evalf4 h) p1 p2 p3 p4 p5 p6 p7 p8 w
| Scale (tf,t) -> ti_scale ((evalf4 tf w x y z),t)
| Uni_compose (uf,tf) ->
let ti = evalf4 tf w x y z in
let fy = ti.l.f in
let u_fy = uf.u fy in
let du_fy = uf.du fy in
let line =
let ( * ) = imul in
mk_line (u_fy, table (fun i -> du_fy * mth ti.l.df i)) in
let fx = mk_interval (lower_bound ti, upper_bound ti) in
let dfx = table (fun i -> mk_interval (lower_partial ti i, upper_partial ti i)) in
let du_fx = uf.du fx in
let ddu_fx = uf.ddu fx in
let dd = table2 (fun i j ->
let ( + ), ( * ) = iadd, imul in
(ddu_fx * mth dfx j) * mth dfx i + du_fx * mth2 ti.dd j i) in
make_taylor_interval(line, w, dd);;
(* evalf4 (Composite(Uni (mk_primitiveU 0 uf),tf,unit,unit,unit,unit,unit,unit,unit)) w x y z;; *)
let evalf tf x z =
let (y,w) = center_form (x,z) in
evalf4 tf w x y z;;
(* Evaluates a function (i = 0) and its first derivatives (i = 1, 2, ...) at the given interval *)
let rec evalf0 tf i x z = match tf with
| Prim_a p -> p.f_df i x z
| Uni p ->
let int = mk_interval (mth x p.slot, mth z p.slot) in
if i = 0 then eval p.uv int 0
else if i = p.slot + 1 then eval p.uv int 1
else zero
| Plus (tf1, tf2) -> iadd (evalf0 tf1 i x z) (evalf0 tf2 i x z)
| Product (tf1, tf2) ->
let itf1, itf2 = evalf0 tf1 0 x z, evalf0 tf2 0 x z in
if i = 0 then imul itf1 itf2
else
let i_df1, i_df2 = evalf0 tf1 i x z, evalf0 tf2 i x z in
iadd (imul i_df1 itf2) (imul itf1 i_df2)
| Scale (tf, t) -> imul (evalf0 tf i x z) t
| Uni_compose (uf, tf) ->
let itf = evalf0 tf 0 x z in
if i = 0 then eval uf itf 0
else
let i_df = evalf0 tf i x z in
imul (eval uf itf 1) i_df
| Composite (h,g1,g2,g3,g4,g5,g6,g7,g8) ->
let gs = [g1;g2;g3;g4;g5;g6;g7;g8] in
let ps = map (fun t -> let int = evalf0 t 0 x z in int.lo, int.hi) gs in
let x', z' = unzip ps in
if i = 0 then evalf0 h 0 x' z'
else
let dhs = table (fun j -> evalf0 h (j + 1) x' z') in
let dgs = map (fun t -> evalf0 t i x z) gs in
let ( + ), ( * ) = iadd, imul in
itlist2 (fun a b c -> a * b + c) dhs dgs zero;;
(*
let line_estimate_composite =
let ( + ) = iadd in
let ( * ) = imul in
fun h p1 p2 p3 p4 p5 p6 p7 p8 ->
let p = [p1;p2;p3;p4;p5;p6;p7;p8] in
let (a,b) = map (fun p -> p.f.lo) p, map (fun p -> p.f.hi) p in
let fN = evalf h a b in
let fN_partial = table (fun i -> mk_interval(lower_partial fN i,upper_partial fN i)) in
let pN_partial =table2(fun i j-> (mth (mth p i).df j)) in
let cN_partial2 = table2 (fun i j -> mth fN_partial j * mth2 pN_partial j i) in
let cN_partial = map (end_itlist ( + )) cN_partial2 in
mk_line ( fN.l.f, cN_partial );;
let rec line_estimate tf y = match tf with
| Prim_a p -> line_estimateA p y
| Uni p -> line_estimateU p y
| Plus (p,q) -> ladd (line_estimate p y) (line_estimate q y)
| Scale (p,t) -> smul (line_estimate p y) t
| Uni_compose (uf,tf) ->
line_estimate (Composite(Uni { slot=0; uv=uf; },tf,unit,unit,unit,unit,unit,unit,unit)) y
| Composite(h,g1,g2,g3,g4,g5,g6,g7,g8) ->
let [p1;p2;p3;p4;p5;p6;p7;p8] = map (fun t-> line_estimate t y) [g1;g2;g3;g4;g5;g6;g7;g8] in
line_estimate_composite h p1 p2 p3 p4 p5 p6 p7 p8;;
*)
end;;