Accuracy of Log-Sum-Exp-based computations
This example proves some results about accuracy on the computation of functions combining summations, logarithm and exponential. It is presented in details in the paper Formally Verified Roundoff Error Bounds on LogSumExp-based Computations.
Authors: Claude Marché / Paul Bonnot
Topics: Floating-Point Computations / Ghost code / Non-linear Arithmetic
Tools: Why3
See also: Accuracy of Log-Sum-Exp
see also the index (by topic, by tool, by reference, by year)
download ZIP archive
The following provides the WhyML code where all the definitions and theorems are stated. See after all that code for the prover results on each verification conditions.
module RealHelpers use real.Real use real.ExpLog use real.Abs let ghost exp_bound_1 (x:real) requires { 0.0 <= x <= 1.0 } ensures { exp x - 2.0 * x <= 1.0 } = () (* proof is done using Rocq + Interval *)
helper lemma: exp x <= 1 + 2x for 0 <= x <= 1
let ghost log_1_minus_x (x:real) requires { 0.0 <= x < 1.0 } ensures { log (1.0 + x) <= - log (1.0 - x) } = assert { (1.0 + x) * (1.0 - x) = 1.0 - x * x <= 1.0 }; assert { log ((1.0 + x) * (1.0 - x)) <= log 1.0 }
helper lemma: log(1+x) <= - log(1-x)
end module RealSummations use int.Int use real.RealInfix use real.Abs use real.FromInt use real.Sum function f_abs (f:int -> real) : int -> real = (fun i -> abs (f i)) let rec ghost sum_pos (f: int -> real) (a b:int) variant { b - a } requires { a <= b } requires { forall i. a <= i < b -> 0. <=. f i } ensures { 0. <=. sum f a b } = if a < b then sum_pos f a (b - 1) let rec ghost sum_bounds (min max:real) (f:int -> real) (a b:int) requires { a <= b /\ forall i. a <= i < b -> min <=. f i <=. max } variant { b - a } ensures { from_int (b-a) *. min <=. sum f a b <=. from_int (b-a) *. max } = if (a < b) then sum_bounds min max f a (b - 1)
Lemma: bounds on summations
let rec ghost sum_abs (f: int -> real) (a b: int) variant { b - a } requires { a <= b } ensures { abs (sum f a b) <=. sum (f_abs f) a b } = if a < b then sum_abs f a (b - 1)
Lemma: summations of absolute values
let rec ghost sum_strictly_pos (f: int -> real) (a b:int) variant { b - a } requires { a < b } requires { forall i. a <= i < b -> 0. <. f i } ensures { 0. <. sum f a b } = if a < b-1 then sum_strictly_pos f a (b - 1)
Lemma: summation of positive values is positive
(* Lemma: summation of values with errors *) let rec ghost sum_accuracy (rel_err abs_err:real) (f g:int -> real) (a b:int) requires { a <= b } requires { forall i. a <= i < b -> abs (g i -. f i) <=. abs (f i) *. rel_err +. abs_err } variant { b - a } ensures { abs (sum g a b -. sum f a b) <=. rel_err *. sum (f_abs f) a b +. abs_err *. from_int (b-a) } = if (a < b) then begin sum_accuracy rel_err abs_err f g a (b - 1); assert { sum (f_abs f) a (b - 1) +. abs (f (b-1)) = sum (f_abs f) a b } end end module FloatSummations use int.Int use ufloat.UFloat use real.RealInfix use real.Abs function f_to_real (a:int -> u) : int -> real = fun i -> to_real (a i) function f_abs_to_real (a:int -> u) : int -> real = fun i -> abs (to_real (a i)) let rec function u_sum (a: int -> u) (n: int) : u variant { n } = if n <= 0 then uzero else uadd (u_sum a (n-1)) (a (n-1))
u_sum a n is the summation of unbounded floats a 0, a 1,..,
a (n-1)
use real.FromInt use real.Sum use RealSummations let rec ghost u_sum_accuracy (a:int -> u) (n:int) requires { 0 <= n } variant { n } ensures { abs (to_real (u_sum a n) -. sum (f_to_real a) 0 n) <=. from_int (n-1) *. eps *. sum (f_abs_to_real a) 0 n } = if n <= 0 then (* base case, trivial *) return (); if n = 1 then begin assert { u_sum a n = a 0 }; return () end; (* induction *) u_sum_accuracy a (n-1); (* recursive call to obtain induction hypothesis *) let exact_sum = sum (f_to_real a) 0 (n-1) in (* sum in real numbers *) let abs_sum = sum (f_abs_to_real a) 0 (n-1) in (* sum of absolute values *) begin ensures { abs exact_sum <=. abs_sum } sum_abs (f_to_real a) 0 (n-1); end; let psum : u = u_sum a (n-1) in (* the previous sum, in udouble *) let res : u = uadd psum (a (n-1)) in (* the final sum, in udouble *) let s : real = to_real psum in (* the previous sum, in real *) let anm1 = to_real (a (n-1)) in (* the last element as a real *) let delta = to_real res -. (s +. anm1) in (* the difference to bound *) begin (* additional property (5) *) ensures { abs delta <=. eps *. (abs_sum +. from_int (n-1) *. abs anm1) } if abs anm1 <=. eps *. abs_sum then (* case 1 of the proof, trivial *) () else begin (* case 2 of the proof *) (* we know eps *. abs_sum <. abs anm1 *) assert { abs (s +. anm1) <=. abs (s -. exact_sum) +. abs abs_sum +. abs anm1 }; assert { from_int (n-2) *. (eps *. abs_sum) <=. from_int (n-2) *. abs anm1 } end end; assert { abs (to_real (u_sum a n) -. sum (f_to_real a) 0 n) <=. abs delta +. abs (s +. anm1 -. sum (f_to_real a) 0 n) }
Accuracy theorem for summations
let ghost u_sum_constant_bounds (max:real) (a:int -> u) (n:int) requires { 0 <= n } requires { 0. <=. max } requires { forall i. 0 <= i < n -> abs (to_real (a i)) <=. max } ensures { abs (to_real (u_sum a n)) <=. (max *. from_int n) *. (1. +. eps *. from_int (n-1)) } = sum_bounds (-. max) max (f_to_real a) 0 n; (* real sum is between -(n-m)*max and (n-m)*max *) sum_bounds 0. max (f_abs_to_real a) 0 n; (* real sum of abs values is between 0 and (n-m)*max *) u_sum_accuracy a n; (* call the known theorem on sum accuracy *) assert { n > 0 -> (max *. from_int n) *. (-.1. -. eps *. from_int (n-1)) <=. to_real (u_sum a n) <=. (max *. from_int n) *. (1. +. eps *. from_int (n-1)) by abs (to_real (u_sum a n) -. sum (f_to_real a) 0 n) <=. (eps *. from_int (n-1)) *. (from_int n *. max) so abs (to_real (u_sum a n) -. sum (f_to_real a) 0 n) <=. max *. from_int n *. (eps *. from_int (n-1)) }; assert { abs (to_real (u_sum a n)) <=. (max *. from_int n) *. (1. +. eps *. from_int (n-1)) }
Corollary: bounds on sums
let ghost u_sum_accuracy_combine (rel_err abs_err : real) (f : int -> real) (f': int -> u) (n:int) requires { forall i. 0 <= i < n -> abs ((f_to_real f') i -. f i) <=. abs (f i) *. rel_err +. abs_err } requires { 0. <=. rel_err } requires { 0. <=. abs_err } requires { 0 <= n } ensures { abs (to_real (u_sum f' n) -. sum f 0 n) <=. (sum (f_abs f) 0 n) *. (rel_err +. (eps *. from_int (n-1) *. (1. +. rel_err))) +. abs_err *. ((1. +. eps *. from_int (n-1)) *. from_int n) } = u_sum_accuracy f' n; sum_accuracy rel_err abs_err f (f_to_real f') 0 n; begin ensures { eps *. from_int (n-1) *. sum (f_abs_to_real f') 0 n <=. eps *. from_int (n-1) *. (sum (f_abs f) 0 n *. (1. +. rel_err) +. from_int n *. abs_err) } begin ensures { sum (f_abs_to_real f') 0 n <=. sum (f_abs f) 0 n *. (1. +. rel_err) +. from_int n *. abs_err } assert { f_abs (f_abs f) = f_abs f }; sum_accuracy rel_err abs_err (f_abs f) (f_abs_to_real f') 0 n; end end end module UExpLog use real.RealInfix use real.Abs use real.ExpLog use ufloat.UFloat constant exp_max_value :real axiom exp_max_value_spec: 0.0 <. exp_max_value <=. 708.0 constant exp_error:real axiom exp_error_bound : 0.0 <. exp_error <=. 0.5 val function u_exp (x:u) : u axiom u_exp_spec: forall x:u. abs(to_real x) <=. exp_max_value -> abs (to_real (u_exp x) -. exp (to_real x)) <=. exp (to_real x) *. exp_error constant log_max_value : real axiom log_max_value_spec: 1.0 <=. log_max_value constant log_error:real axiom log_error_bound : 0.0 <. log_error <=. 1.0 val function u_log (x:u) : u axiom u_log_spec: forall x:u. 0. <. to_real x <=. log_max_value -> abs (to_real (u_log x) -. log (to_real x)) <=. abs (log (to_real x)) *. log_error constant log2_max_value : real axiom log2_max_value_spec: 1.0 <=. log_max_value constant log2_error:real axiom log2_error_bound : 0.0 <. log2_error <=. 1.0 val function u_log2 (x:u) : u axiom u_log2_spec: forall x:u. 0. <. to_real x <=. log2_max_value -> abs (to_real (u_log2 x) -. log2 (to_real x)) <=. abs (log2 (to_real x)) *. log2_error end module LSE use int.Int use real.RealInfix use real.Abs use real.ExpLog use real.Sum use real.FromInt use ufloat.UFloat use RealSummations use FloatSummations use UExpLog use RealHelpers as RealHelpers let ghost log_combine_err (x x_approx a b :real) requires { 0.0 <. x } requires { b <. x *. (1.0 -. a) } requires { abs (x_approx -. x) <=. x *. a +. b } ensures { abs (log x_approx -. log x) <=. -. log(1.0 -. (a +. b /. x)) } = let err = a +. b /. x in assert { b = x *. (b /. x) }; begin ensures { log x_approx -. log x <=. -. log (1.0 -. err) } assert { log x_approx <=. log (x *. (1. +. err)) };
lemma: error propagation through log
invoke lemma on reals
RealHelpers.log_1_minus_x err; end; begin ensures { log x_approx -. log x >=. -. (-. log (1.0 -. err)) } assert { log x_approx >=. log (x *. (1. -. err)) }; end function f_exp (a:int -> u) : int -> real = fun i -> exp ((f_to_real a) i)
[f_exp a i = exp (a i)]
function f_uexp (a: int -> u) : int -> u = fun i -> u_exp (a i)
[f_uexp a i = exp (a i)] in unbounded floats
function lse_exact (f : int -> u) (n : int) : real = log (sum (f_exp f) 0 n)
The LSE function itself, exact computation [lse(a,n) = log (exp a(0) + ... + exp a(n-1))]
function u_lse [@inline:trivial] (a: int -> u) (size:int) : u = u_log (u_sum (f_uexp a) size) let ghost lse_accuracy (a:int -> u) (n:int) (a_max:real) requires { 1 <= n } requires { from_int (n-1) *. eps <=. 0x1p-2 } requires { forall i. 0 <= i < n -> -. exp_max_value <=. to_real (a i) <=. a_max } requires { a_max <=. exp_max_value } requires { exp a_max *. (1.0 +. exp_error) *. from_int n *. (1.0 +. eps *. from_int (n-1)) <=. log_max_value } ensures { let err = exp_error +. eps *. from_int (n-1) *. (1.0 +. exp_error) in abs (to_real (u_lse a n) -. lse_exact a n) <=. log_error *. abs (lse_exact a n) -. log (1.0 -. err) *. (1.0 +. log_error) } = let s : u = u_sum (f_uexp a) n in let sum_exps : real = sum (f_exp a) 0 n in begin ensures { abs ((to_real s) -. sum_exps) <=. sum_exps *. (exp_error +. eps *. from_int (n-1) *. (1.0 +. exp_error)) } assert { forall i. 0 <= i < n -> abs (to_real (u_exp (a i)) -. exp (to_real (a i))) <=. abs(exp(to_real (a i))) *. exp_error }; u_sum_accuracy_combine exp_error 0.0 (f_exp a) (f_uexp a) n; assert { f_abs (f_exp a) = f_exp a } end; begin (* required domain for calling u_log *) ensures { 0.0 <. to_real s <=. log_max_value } begin ensures { sum_exps >. 0.0 } sum_strictly_pos (f_exp a) 0 n; end; assert { forall i. 0 <= i < n -> 0.0 <=. to_real (u_exp (a i)) <=. exp a_max *. (1.0 +. exp_error) by abs (to_real (u_exp (a i)) -. exp (to_real (a i))) <=. exp (to_real (a i)) *. exp_error so to_real (u_exp (a i)) <=. exp (to_real (a i)) *. (1.0 +. exp_error) }; u_sum_constant_bounds (exp a_max *. (1.0 +. exp_error)) (f_uexp a) n; end; let r : u = u_log s in assert { r = u_lse a n }; let err : real = exp_error +. eps *. from_int (n-1) *. (1.0 +. exp_error) in assert { err <. 1.0 }; begin ensures { abs (log (to_real s) -. log sum_exps) <=. -. log (1.0 -. err) } log_combine_err sum_exps (to_real s) err 0.0; end; assert { (log_error +. 1.0) *. (abs (log (to_real s) -. log sum_exps)) <=. -. log (1.0 -. err) *. (log_error +. 1.0) by (log_error +. 1.0 >=. 0.0) }; assert { abs (to_real r -. lse_exact a n) <=. abs (to_real r -. log (to_real s)) +. abs (log (to_real s) -. log sum_exps) <=. (log_error +. 1.0) *. (abs (log (to_real s) -. log sum_exps)) +. log_error *. abs (lse_exact a n) <=. log_error *. abs (lse_exact a n) -. log (1.0 -. err) *. (log_error +. 1.0) }
First main theorem: LSE accuracy
end module SLSE use real.RealInfix use real.Abs use real.ExpLog use ufloat.UFloat use UExpLog let function sqr (x:real) : real = x *. x let ghost sqr_ge (x y:real) : unit requires { abs x <=. y } ensures { sqr x <=. sqr y } = () let ghost sqr_non_neg (x:real) : unit ensures { 0.0 <=. sqr x } = () let function g (x y rho:u) : real = let x_rho_y = to_real x +. to_real rho -. to_real y in exp (-. sqr x_rho_y /. 2.0) let ghost g_bounds (x y rho:u) ensures { 0.0 <. g x y rho <=. 1.0 } = () let function u_g (x y rho:u) : u = let x_rho_y = usub (uadd x rho) y in u_exp (uminus ((udiv (umul x_rho_y x_rho_y) utwo))) constant a_eps : real = eps *. (1.0 +. 4.0 *. sqr (1.0 +. eps)) /. 2.0 predicate g_hypothesis (rho:u) (a_max:real) = sqr (2.0 *. a_max +. abs (to_real rho)) *. (0.5 +. a_eps) +. 1.5 *. eta <=. exp_max_value
Hypothesis for accuracy of g
use ufloat.HelperLemmas as HelperLemmas use ufloat.UFloatLemmas as UFloatLemmas let ghost g_accuracy (x y rho : u) (a_max : real) requires { abs (to_real x) <=. a_max } requires { abs (to_real y) <=. a_max } requires { g_hypothesis rho a_max } ensures { let t1 = abs (to_real x) +. abs (to_real y) +. abs (to_real rho) in let t2 = a_eps *. sqr t1 +. 1.5 *. eta in let error = exp_error +. (exp t2 -. 1.0) *. (1. +. exp_error) in abs (to_real (u_g x y rho) -. g x y rho) <=. error *. abs (g x y rho) } = let a = to_real x +. to_real rho -. to_real y in let u_a = usub (uadd x rho) y in let a' = abs (to_real x) +. abs (to_real rho) +. abs (to_real y) in assert { abs a <=. a' }; begin ensures { abs (to_real u_a -. a) <=. 2. *. eps *. a' } UFloatLemmas.usub_error_propagation (uadd x rho) y u_a (to_real x +. to_real rho) (abs (to_real x +. to_real rho)) eps 0.0 (to_real y) (abs (to_real y)) 0.0 0.0; end; let b = sqr a in let u_b = umul u_a u_a in let b' = sqr a' in assert { abs b <=. b' }; begin ensures { abs (to_real u_b -. b) <=. 2.0 *. a_eps *. b' +. eta } UFloatLemmas.umul_error_propagation u_a u_a u_b a a' (2. *. eps) 0.0 a a' (2. *. eps) 0.0 end; let c = b /. 2.0 in let u_c = udiv u_b utwo in let c' = b' in assert { abs c <=. 0.5 *. c' }; begin ensures { abs (to_real u_c -. c) <=. a_eps *. c' +. 1.5 *. eta } UFloatLemmas.udiv_exact_error_propagation u_b utwo u_c b b' (2.0 *. a_eps) eta end; let d = -. c in let u_d = uminus u_c in let d' = c' in assert { abs d <=. 0.5 *. d' }; begin ensures { abs (to_real u_d -. d) <=. a_eps *. d' +. 1.5 *. eta } end; begin ensures { abs (to_real u_d) <=. exp_max_value } begin ensures { d' <=. sqr (2.0 *. a_max +. abs (to_real rho)) } sqr_ge a' (2.0 *. a_max +. abs (to_real rho)) end; begin ensures { (0.5 +. a_eps) *. d' <=. (0.5 +. a_eps) *. sqr (2.0 *. a_max +. abs (to_real rho)) } HelperLemmas.mul_order_compat (0.5 +. a_eps) d' (sqr (2.0 *. a_max +. abs (to_real rho))) end; assert { abs (to_real u_d) <=. abs d +. a_eps *. d' +. 1.5 *. eta <=. (0.5 +. a_eps) *. d' +. 1.5 *. eta <=. (0.5 +. a_eps) *. sqr (2.0 *. a_max +. abs (to_real rho)) +. 1.5 *. eta <=. exp_max_value }; () end; let e = exp d in let u_e = u_exp u_d in let t2 = a_eps *. b' +. 1.5 *. eta in let error = exp_error +. (exp t2 -. 1.0) *. (1. +. exp_error) in begin ensures { abs (to_real u_e -. e) <=. error *. e } UFloatLemmas.exp_error_propagation u_e u_d d d' exp_error 0.0 a_eps (1.5 *. eta); end; assert { u_g x y rho = u_e }; assert { g x y rho = e } use int.Int use real.FromInt use real.Sum use RealSummations use FloatSummations let ghost function gi (a:int -> u) (rho : u) (i : int) : int -> real = (fun j -> g (a i) (a j) rho)
(gi a rho i) j = g (a i) (a j) rho
let ghost function h (a:int -> u) (n:int) (rho : u) : int -> real = (fun i -> log2 (sum (gi a rho i) 0 n))
(h a n rho) i = log2 (g (a i) (a 0) rho + ... g (a i) (a (n-1)) rho)
let ghost function slse (a:int -> u) (n:int) (rho:u) = sum (h a n rho) 0 n
Mathematical SLSE
let ghost function abs_slse (a:int -> u) (n:int) (rho:u) = sum (f_abs (h a n rho)) 0 n
Mathematical sum of absolute partial sums
let function u_gi (a:int -> u) (rho : u) (i : int) : int -> u = (fun j -> u_g (a i) (a j) rho) let function u_h (a:int -> u) (n:int) (rho : u) : int -> u = (fun i -> u_log2 (u_sum (u_gi a rho i) n)) let function u_slse (a:int -> u) (n:int) (rho:u) = u_sum (u_h a n rho) n function g_max_error (rho:u) (a_max:real) : real = exp_error +. (exp (a_eps *. sqr (2.0 *. a_max +. abs(to_real rho)) +. 1.5 *. eta) -. 1.0) *. (1.0 +. exp_error)
Hypothesis for accuracy of slse
predicate float_format_hypothesis (a_eps exp_max_value eta : real) = 2.0 *. a_eps *. exp_max_value +. 1.5 *. eta <=. 0x1p-6 use RealHelpers as RealHelpers let ghost gg_bounds (rho:u) (a_max:real) requires { g_hypothesis rho a_max } requires { float_format_hypothesis a_eps exp_max_value eta } ensures { 0.0 <=. g_max_error rho a_max <=. 0x9p-4 } = begin ensures { 0.0 <=. g_max_error rho a_max } sqr_non_neg (2.0 *. a_max +. abs(to_real rho)); end; begin ensures { g_max_error rho a_max <=. 0x9p-4 } begin ensures { a_eps *. sqr (2.0 *. a_max +. abs(to_real rho)) <=. 2.0 *. a_eps *. exp_max_value } assert { sqr (2.0 *. a_max +. abs (to_real rho)) *. (0.5 +. a_eps) <=. exp_max_value }; (* from g_hypothesis *) assert { sqr (2.0 *. a_max +. abs (to_real rho)) <=. 2.0 *. exp_max_value }; HelperLemmas.mul_order_compat a_eps (sqr (2.0 *. a_max +. abs (to_real rho))) (2.0 *. exp_max_value) end; begin ensures { exp (a_eps *. sqr (2.0 *. a_max +. abs(to_real rho)) +. 1.5 *. eta) <=. exp (2.0 *. a_eps *. exp_max_value +. 1.5 *. eta) } exp_increasing (a_eps *. sqr (2.0 *. a_max +. abs(to_real rho))) (2.0 *. a_eps *. exp_max_value +. 1.5 *. eta) end; begin ensures { g_max_error rho a_max <=. exp_error +. (exp (2.0 *. a_eps *. exp_max_value +. 1.5 *. eta) -. 1.0 ) *. (1.0 +. exp_error) } HelperLemmas.mul_order_compat (1.0 +. exp_error) (exp (a_eps *. sqr (2.0 *. a_max +. abs(to_real rho)) +. 1.5 *. eta) -. 1.0) (exp (2.0 *. a_eps *. exp_max_value +. 1.5 *. eta) -. 1.0) end; begin ensures { g_max_error rho a_max <=. 0.5 +. 2.0 *. (exp (2.0 *. a_eps *. exp_max_value +. 1.5 *. eta) -. 1.0 ) } end; begin ensures { exp (2.0 *. a_eps *. exp_max_value +. 1.5 *. eta) -. 1.0 <=. 0x1p-5 } (* invoke a lemma on reals *) RealHelpers.exp_bound_1 (2.0 *. a_eps *. exp_max_value +. 1.5 *. eta) end end predicate slse_hypothesis (rho:u) (a_max:real) (n:int) = let gg = g_max_error rho a_max in from_int n *. (1.0 +. (gg +. from_int (n-1) *. eps *. (1.0 +. gg))) <=. log2_max_value let ghost slse_accuracy (a:int -> u) (n:int) (rho:u) (a_max:real) requires { 1 <= n } requires { from_int (n-1) *. eps <=. 0x1p-2 } requires { forall i. 0 <= i < n -> abs (to_real (a i)) <=. a_max } requires { g_hypothesis rho a_max } requires { float_format_hypothesis a_eps exp_max_value eta } requires { slse_hypothesis rho a_max n } ensures { let gg = g_max_error rho a_max in abs (to_real (u_slse a n rho) -. slse a n rho) <=. (log2_error +. from_int (n-1) *. eps *. (1.0 +. log2_error)) *. abs_slse a n rho -. log2 (1.0 -. (gg +. from_int (n-1) *. eps *. (1.0 +. gg))) *. (1.0 +. log2_error) *. from_int n *. (1.0 +. from_int (n-1) *. eps) } = let gg = g_max_error rho a_max in begin ensures { 0.0 <=. gg <. 1.0 } gg_bounds rho a_max end; let ggg = gg +. from_int (n-1) *. eps *. (1.0 +. gg) in begin ensures { ggg <. 1.0 } assert { from_int (n-1) *. eps <=. 0x1p-2 }; (* 1/4 *) gg_bounds rho a_max; assert { 1.0 +. gg <=. 0x19p-4 }; (* 25 / 16 *) assert { from_int (n-1) *. eps *. (1.0 +. gg) <=. 0x19p-6 }; (* 25 / 64 *) assert { gg +. from_int (n-1) *. eps *. (1.0 +. gg) <=. 0x9p-4 +. 0x19p-6 } end; let lemma propagation_through_log2 (i:int) requires { 0 <= i < n } ensures { abs (to_real (u_log2 (u_sum (u_gi a rho i) n)) -. log2 (sum (gi a rho i) 0 n)) <=. log2_error *. abs (log2 (sum (gi a rho i) 0 n)) -. log2 (1.0 -. ggg) *. (1.0 +. log2_error) } = let lemma step1 (i j:int) requires { 0 <= i < n } requires { 0 <= j < n } ensures { abs (to_real (u_g (a i) (a j) rho) -. g (a i) (a j) rho) <=. gg *. g (a i) (a j) rho } = begin (* invoke Corollary 13, corollary of g_accuracy *) g_accuracy (a i) (a j) rho a_max; assert { abs (g (a i) (a j) rho) = g (a i) (a j) rho }; begin ensures { sqr (abs (to_real (a @ i)) +. abs (to_real (a @ j)) +. abs (to_real rho)) <=. sqr (2.0 *. a_max +. abs (to_real rho)) } sqr_ge (abs (to_real (a @ i)) +. abs (to_real (a @ j)) +. abs (to_real rho)) (2.0 *. a_max +. abs (to_real rho)) end; begin ensures { exp ((a_eps *. sqr ((abs (to_real (a @ i)) +. abs (to_real (a @ j))) +. abs (to_real rho))) +. (1.5 *. eta)) -. 1.0 >=. 0.0 } assert { a_eps >=. 0.0 }; ExpLog.exp_increasing 0.0 ((a_eps *. sqr ((abs (to_real (a @ i)) +. abs (to_real (a @ j))) +. abs (to_real rho))) +. (1.5 *. eta)) end; begin ensures { (exp (a_eps *. sqr ((abs (to_real (a @ i)) +. abs (to_real (a @ j))) +. abs (to_real rho)) +. (1.5 *. eta)) -. 1.0) *. (1.0 +. exp_error) <=. (exp (a_eps *. sqr (2.0 *. a_max +. abs (to_real rho)) +. (1.5 *. eta)) -. 1.0) *. (1.0 +. exp_error) } HelperLemmas.mul_order_compat a_eps (sqr ((abs (to_real (a @ i)) +. abs (to_real (a @ j))) +. abs (to_real rho)) +. (1.5 *. eta)) (sqr (2.0 *. a_max +. abs (to_real rho)) +. (1.5 *. eta)); ExpLog.exp_increasing (a_eps *. sqr ((abs (to_real (a @ i)) +. abs (to_real (a @ j))) +. abs (to_real rho)) +. (1.5 *. eta)) (a_eps *. sqr (2.0 *. a_max +. abs (to_real rho)) +. (1.5 *. eta)); assert { exp (a_eps *. sqr ((abs (to_real (a @ i)) +. abs (to_real (a @ j))) +. abs (to_real rho)) +. (1.5 *. eta)) <=. exp (a_eps *. sqr (2.0 *. a_max +. abs (to_real rho)) +. (1.5 *. eta)) }; HelperLemmas.mul_order_compat (1.0 +. exp_error) (exp (a_eps *. sqr ((abs (to_real (a @ i)) +. abs (to_real (a @ j))) +. abs (to_real rho)) +. (1.5 *. eta)) -. 1.0) (exp (a_eps *. sqr (2.0 *. a_max +. abs (to_real rho)) +. (1.5 *. eta)) -. 1.0) end end in (* invoke generalized accuracy for summations *) u_sum_accuracy_combine gg 0.0 (gi a rho i) (u_gi a rho i) n; assert { f_abs (gi a rho i) = gi a rho i }; begin ensures { sum (gi a rho i) 0 n >. 0.0 } sum_strictly_pos (gi a rho i) 0 n end; assert { abs (to_real (u_sum (u_gi a rho i) n) -. sum (gi a rho i) 0 n) <=. ggg *. sum (gi a rho i) 0 n }; (* error propagation through log2 *) begin ensures { 0.0 <. to_real (u_sum (u_gi a rho i) n) } assert { to_real (u_sum (u_gi a rho i) n) >=. (1.0 -. ggg) *. sum (gi a rho i) 0 n }; sum_strictly_pos (gi a rho i) 0 n; assert { 0.0 <. 1.0 -. ggg } end; begin ensures { to_real (u_sum (u_gi a rho i) n) <=. log2_max_value } assert { to_real (u_sum (u_gi a rho i) n) <=. (1.0 +. ggg) *. sum (gi a rho i) 0 n } ; sum_strictly_pos (gi a rho i) 0 n; begin ensures { (1.0 +. ggg) *. sum (gi a rho i) 0 n <=. (1.0 +. ggg) *. from_int n } let lemma g_bound (j:int) requires { 0 <= j < n } ensures { 0.0 <. (gi a rho i) j <=. 1.0 } = g_bounds (a i) (a j) rho in sum_bounds 0.0 1.0 (gi a rho i) 0 n; assert { sum (gi a rho i) 0 n <=. from_int n } end; begin ensures { (1.0 +. ggg) *. sum (gi a rho i) 0 n <=. log2_max_value } assert { (1.0 +. ggg) *. from_int n <=. log2_max_value by slse_hypothesis rho a_max n } end end; UFloatLemmas.log2_error_propagation_simple (u_log2 (u_sum (u_gi a rho i) n)) (u_sum (u_gi a rho i) n) (sum (gi a rho i) 0 n) log2_error ggg in (* another invocation of generalized accuracy for summations *) assert { log2 (1.0 -. ggg) <=. 0.0 }; u_sum_accuracy_combine log2_error (-. log2 (1.0 -. ggg) *. (1.0 +. log2_error)) (h a n rho) (u_h a n rho) n
Second main theorem: SLSE accuracy
end
The following tables list all the verification conditions generated and tell which prover proved which.
Why3 Proof Results for Project "log-sum-exp"
Theory "log-sum-exp.RealHelpers": fully verified
| Obligations | Alt-Ergo 2.6.2 | Coq 8.20.1 | |
| VC for exp_bound_1 | --- | 1.19 | |
| VC for log_1_minus_x | --- | --- | |
| split_vc | |||
| assertion | 0.02 | --- | |
| assertion | 0.03 | --- | |
| postcondition | 0.03 | --- | |
Theory "log-sum-exp.RealSummations": fully verified
| Obligations | Alt-Ergo 2.6.2 | CVC4 1.8 | CVC5 1.2.0 | Z3 4.12.2 |
| VC for sum_pos | 0.03 | --- | --- | --- |
| VC for sum_bounds | --- | --- | --- | 0.03 |
| VC for sum_abs | --- | 0.06 | --- | --- |
| VC for sum_strictly_pos | --- | --- | --- | 0.01 |
| VC for sum_accuracy | --- | --- | 0.46 | --- |
Theory "log-sum-exp.FloatSummations": fully verified
| Obligations | Alt-Ergo 2.6.2 | CVC4 1.8 | CVC5 1.2.0 | Z3 4.12.2 | ||
| VC for u_sum | --- | --- | 0.04 | --- | ||
| VC for u_sum_accuracy | --- | --- | --- | --- | ||
| split_vc | ||||||
| postcondition | --- | --- | 0.06 | --- | ||
| assertion | --- | 0.38 | --- | --- | ||
| postcondition | 0.06 | --- | --- | --- | ||
| variant decrease | --- | 0.03 | --- | --- | ||
| precondition | --- | --- | 0.02 | --- | ||
| precondition | --- | 0.03 | --- | --- | ||
| postcondition | --- | 1.94 | --- | --- | ||
| assertion | 0.06 | --- | --- | --- | ||
| assertion | 0.34 | --- | --- | --- | ||
| postcondition | 3.73 | --- | --- | --- | ||
| assertion | --- | --- | 0.17 | --- | ||
| postcondition | 0.66 | --- | --- | --- | ||
| VC for u_sum_constant_bounds | --- | --- | --- | --- | ||
| split_vc | ||||||
| precondition | 0.04 | --- | --- | --- | ||
| precondition | 0.07 | --- | --- | --- | ||
| precondition | --- | --- | 0.02 | --- | ||
| assertion | --- | --- | --- | --- | ||
| split_vc | ||||||
| assertion | 0.06 | --- | --- | --- | ||
| VC for u_sum_constant_bounds | --- | --- | 0.02 | --- | ||
| VC for u_sum_constant_bounds | 0.04 | --- | --- | --- | ||
| VC for u_sum_constant_bounds | --- | --- | 0.03 | --- | ||
| assertion | --- | --- | 0.11 | --- | ||
| postcondition | --- | 0.03 | --- | --- | ||
| VC for u_sum_accuracy_combine | --- | --- | --- | --- | ||
| split_vc | ||||||
| precondition | --- | --- | 0.03 | --- | ||
| precondition | --- | --- | --- | 0.02 | ||
| precondition | 0.04 | --- | --- | --- | ||
| assertion | 0.05 | --- | --- | --- | ||
| precondition | 0.06 | --- | --- | --- | ||
| precondition | 0.40 | --- | --- | --- | ||
| postcondition | 0.04 | --- | --- | --- | ||
| postcondition | 0.34 | --- | --- | --- | ||
| postcondition | --- | --- | 0.04 | --- | ||
Theory "log-sum-exp.LSE": fully verified
| Obligations | Alt-Ergo 2.6.2 | CVC4 1.8 | CVC5 1.2.0 | Z3 4.12.2 | ||
| VC for log_combine_err | --- | --- | --- | --- | ||
| split_vc | ||||||
| precondition | --- | 0.03 | --- | --- | ||
| assertion | --- | --- | --- | 0.02 | ||
| assertion | --- | 0.09 | --- | --- | ||
| precondition | 1.19 | --- | --- | --- | ||
| postcondition | 0.33 | --- | --- | --- | ||
| assertion | 0.20 | --- | --- | --- | ||
| postcondition | 0.18 | --- | --- | --- | ||
| postcondition | --- | --- | 0.03 | --- | ||
| VC for lse_accuracy | --- | --- | --- | --- | ||
| split_vc | ||||||
| assertion | 0.15 | --- | --- | --- | ||
| precondition | --- | --- | --- | --- | ||
| instantiate Assert i | ||||||
| precondition | 0.20 | --- | --- | --- | ||
| precondition | --- | --- | 0.04 | --- | ||
| precondition | --- | --- | 0.04 | --- | ||
| precondition | --- | --- | 0.04 | --- | ||
| assertion | --- | --- | 0.26 | --- | ||
| postcondition | 0.05 | --- | --- | --- | ||
| precondition | --- | --- | 0.03 | --- | ||
| precondition | --- | 0.25 | --- | --- | ||
| postcondition | --- | --- | 0.05 | --- | ||
| assertion | --- | --- | --- | --- | ||
| split_vc | ||||||
| assertion | --- | --- | 0.07 | --- | ||
| VC for lse_accuracy | --- | --- | 0.04 | --- | ||
| VC for lse_accuracy | 0.32 | --- | --- | --- | ||
| VC for lse_accuracy | 0.18 | --- | --- | --- | ||
| precondition | --- | --- | 0.06 | --- | ||
| precondition | --- | --- | 0.11 | --- | ||
| precondition | --- | --- | 0.07 | --- | ||
| postcondition | 1.80 | --- | --- | --- | ||
| assertion | --- | --- | --- | 0.02 | ||
| assertion | 0.24 | --- | --- | --- | ||
| precondition | 0.18 | --- | --- | --- | ||
| precondition | 1.37 | --- | --- | --- | ||
| precondition | 0.06 | --- | --- | --- | ||
| postcondition | 0.05 | --- | --- | --- | ||
| assertion | 1.32 | --- | --- | --- | ||
| assertion | 0.54 | --- | --- | --- | ||
| postcondition | --- | --- | --- | 0.02 | ||
Theory "log-sum-exp.SLSE": fully verified
| Obligations | Alt-Ergo 2.6.2 | CVC4 1.8 | CVC5 1.2.0 | Z3 4.12.2 | ||
| VC for sqr_ge | 0.16 | --- | --- | --- | ||
| VC for sqr_non_neg | 0.05 | --- | --- | --- | ||
| VC for g | --- | --- | 0.04 | --- | ||
| VC for g_bounds | 0.81 | --- | --- | --- | ||
| VC for u_g | --- | --- | 0.04 | --- | ||
| VC for g_accuracy | --- | --- | --- | --- | ||
| split_vc | ||||||
| assertion | --- | --- | 0.05 | --- | ||
| precondition | --- | 0.09 | --- | --- | ||
| precondition | --- | --- | 0.05 | --- | ||
| precondition | --- | --- | 0.05 | --- | ||
| precondition | --- | --- | 0.04 | --- | ||
| precondition | --- | --- | 0.07 | --- | ||
| precondition | 0.07 | --- | --- | --- | ||
| precondition | --- | --- | 0.04 | --- | ||
| precondition | --- | --- | --- | 0.02 | ||
| precondition | --- | --- | 0.07 | --- | ||
| postcondition | 0.14 | --- | --- | --- | ||
| assertion | 4.99 | --- | --- | --- | ||
| precondition | --- | --- | 0.04 | --- | ||
| precondition | --- | --- | 0.06 | --- | ||
| precondition | --- | --- | 0.07 | --- | ||
| precondition | 0.13 | --- | --- | --- | ||
| precondition | --- | --- | 0.04 | --- | ||
| precondition | --- | --- | 0.05 | --- | ||
| precondition | 0.06 | --- | --- | --- | ||
| precondition | 0.20 | --- | --- | --- | ||
| precondition | --- | --- | 0.03 | --- | ||
| postcondition | --- | --- | 0.05 | --- | ||
| precondition | --- | --- | 0.04 | --- | ||
| precondition | --- | --- | --- | 0.03 | ||
| assertion | --- | 0.10 | --- | --- | ||
| precondition | --- | --- | 0.03 | --- | ||
| precondition | --- | --- | 0.04 | --- | ||
| precondition | --- | --- | 0.16 | --- | ||
| precondition | --- | --- | 0.19 | --- | ||
| precondition | --- | --- | 0.04 | --- | ||
| precondition | --- | --- | 0.18 | --- | ||
| precondition | --- | --- | 0.17 | --- | ||
| postcondition | --- | --- | 0.08 | --- | ||
| assertion | --- | --- | 0.06 | --- | ||
| postcondition | --- | --- | 0.06 | --- | ||
| precondition | --- | --- | 0.07 | --- | ||
| postcondition | --- | --- | 0.04 | --- | ||
| precondition | --- | --- | 0.14 | --- | ||
| precondition | --- | --- | 0.05 | --- | ||
| postcondition | --- | --- | 0.05 | --- | ||
| assertion | --- | --- | --- | --- | ||
| split_vc | ||||||
| assertion | --- | --- | 0.21 | --- | ||
| assertion | --- | --- | 0.07 | --- | ||
| assertion | --- | --- | 0.05 | --- | ||
| assertion | --- | --- | 0.15 | --- | ||
| postcondition | --- | --- | 0.19 | --- | ||
| precondition | --- | --- | 0.16 | --- | ||
| precondition | --- | 0.27 | --- | --- | ||
| precondition | --- | --- | 0.09 | --- | ||
| precondition | --- | --- | 0.05 | --- | ||
| postcondition | --- | --- | 0.04 | --- | ||
| assertion | --- | --- | 0.03 | --- | ||
| assertion | --- | --- | 0.04 | --- | ||
| postcondition | 2.96 | --- | --- | --- | ||
| VC for gi | --- | 0.19 | --- | --- | ||
| VC for h | 0.04 | --- | --- | --- | ||
| VC for u_gi | 0.06 | --- | --- | --- | ||
| VC for u_h | --- | --- | --- | 0.02 | ||
| VC for gg_bounds | --- | --- | --- | --- | ||
| split_vc | ||||||
| postcondition | 4.36 | --- | --- | --- | ||
| assertion | --- | --- | 0.05 | --- | ||
| assertion | 0.38 | --- | --- | --- | ||
| precondition | --- | --- | 0.12 | --- | ||
| precondition | --- | --- | 0.05 | --- | ||
| postcondition | --- | --- | 0.05 | --- | ||
| precondition | --- | --- | 0.03 | --- | ||
| postcondition | --- | --- | --- | 0.27 | ||
| precondition | --- | --- | 0.06 | --- | ||
| precondition | --- | --- | 0.05 | --- | ||
| postcondition | --- | --- | 0.05 | --- | ||
| postcondition | 0.76 | --- | --- | --- | ||
| precondition | --- | --- | 0.13 | --- | ||
| postcondition | 0.30 | --- | --- | --- | ||
| postcondition | 0.07 | --- | --- | --- | ||
| postcondition | --- | --- | 0.04 | --- | ||
| VC for slse_accuracy | --- | --- | --- | --- | ||
| split_vc | ||||||
| precondition | --- | --- | 0.03 | --- | ||
| precondition | --- | --- | 0.04 | --- | ||
| postcondition | 0.04 | --- | --- | --- | ||
| assertion | --- | --- | --- | 0.03 | ||
| precondition | 0.04 | --- | --- | --- | ||
| precondition | --- | --- | 0.04 | --- | ||
| assertion | --- | --- | 0.03 | --- | ||
| assertion | 0.24 | --- | --- | --- | ||
| assertion | --- | --- | 0.05 | --- | ||
| postcondition | 0.04 | --- | --- | --- | ||
| precondition | 0.23 | --- | --- | --- | ||
| precondition | --- | --- | 0.33 | --- | ||
| precondition | --- | --- | 0.03 | --- | ||
| assertion | --- | --- | 0.26 | --- | ||
| precondition | --- | --- | 0.94 | --- | ||
| postcondition | --- | --- | 0.04 | --- | ||
| assertion | --- | --- | 0.19 | --- | ||
| precondition | 1.43 | --- | --- | --- | ||
| postcondition | --- | --- | 0.06 | --- | ||
| precondition | --- | --- | 0.14 | --- | ||
| precondition | --- | --- | 0.06 | --- | ||
| precondition | --- | --- | 0.05 | --- | ||
| assertion | --- | --- | 0.05 | --- | ||
| precondition | 0.08 | --- | --- | --- | ||
| precondition | --- | --- | 0.05 | --- | ||
| postcondition | --- | --- | 0.06 | --- | ||
| postcondition | 3.47 | --- | --- | --- | ||
| precondition | --- | --- | --- | --- | ||
| instantiate H2 i1,i | ||||||
| precondition | 0.46 | --- | --- | --- | ||
| precondition | --- | --- | --- | 0.02 | ||
| precondition | --- | --- | 0.04 | --- | ||
| precondition | 0.07 | --- | --- | --- | ||
| assertion | --- | --- | 0.64 | --- | ||
| precondition | 0.05 | --- | --- | --- | ||
| precondition | --- | --- | 0.54 | --- | ||
| postcondition | 0.07 | --- | --- | --- | ||
| assertion | 0.06 | --- | --- | --- | ||
| assertion | --- | --- | 0.05 | --- | ||
| precondition | 0.06 | --- | --- | --- | ||
| precondition | --- | --- | 0.72 | --- | ||
| assertion | --- | --- | 0.04 | --- | ||
| postcondition | 13.64 | --- | --- | --- | ||
| assertion | --- | --- | 0.04 | --- | ||
| precondition | --- | --- | 0.04 | --- | ||
| precondition | --- | --- | 0.62 | --- | ||
| postcondition | --- | --- | 0.25 | --- | ||
| precondition | --- | --- | 0.14 | --- | ||
| assertion | --- | --- | 0.04 | --- | ||
| postcondition | 8.16 | --- | --- | --- | ||
| assertion | --- | --- | 0.05 | --- | ||
| postcondition | --- | --- | 0.16 | --- | ||
| postcondition | --- | --- | 0.17 | --- | ||
| precondition | --- | --- | 0.04 | --- | ||
| precondition | --- | --- | 0.14 | --- | ||
| precondition | --- | --- | 0.04 | --- | ||
| precondition | 0.05 | --- | --- | --- | ||
| precondition | --- | --- | 0.08 | --- | ||
| postcondition | --- | --- | 0.04 | --- | ||
| assertion | 5.14 | --- | --- | --- | ||
| precondition | --- | --- | --- | --- | ||
| instantiate H2 i | ||||||
| precondition | 0.46 | --- | --- | --- | ||
| precondition | --- | --- | 0.04 | --- | ||
| precondition | 0.05 | --- | --- | --- | ||
| precondition | --- | --- | 0.04 | --- | ||
| postcondition | --- | --- | 0.03 | --- | ||