# Theory Approximations

theory Approximations
imports Harmonic_Numbers
section ‹Numeric approximations to Constants›

theory Approximations
imports "HOL-Analysis.Complex_Transcendental" "HOL-Analysis.Harmonic_Numbers"
begin

text ‹
In this theory, we will approximate some standard mathematical constants with high precision,
using only Isabelle's simplifier. (no oracles, code generator, etc.)

The constants we will look at are: $\pi$, $e$, $\ln 2$, and $\gamma$ (the Euler--Mascheroni
constant).
›

lemma eval_fact:
"fact 0 = 1"
"fact (Suc 0) = 1"
"fact (numeral n) = numeral n * fact (pred_numeral n)"
by (simp, simp, simp_all only: numeral_eq_Suc fact_Suc,
simp only: numeral_eq_Suc [symmetric] of_nat_numeral)

lemma sum_poly_horner_expand:
"(∑k<(numeral n::nat). f k * x^k) = f 0 + (∑k<pred_numeral n. f (k+1) * x^k) * x"
"(∑k<Suc 0. f k * x^k) = (f 0 :: 'a :: semiring_1)"
"(∑k<(0::nat). f k * x^k) = 0"
proof -
{
fix m :: nat
have "(∑k<Suc m. f k * x^k) = f 0 + (∑k=Suc 0..<Suc m. f k * x^k)"
by (subst atLeast0LessThan [symmetric], subst sum_head_upt_Suc) simp_all
also have "(∑k=Suc 0..<Suc m. f k * x^k) = (∑k<m. f (k+1) * x^k) * x"
by (subst sum_shift_bounds_Suc_ivl)
(simp add: sum_distrib_right algebra_simps atLeast0LessThan power_commutes)
finally have "(∑k<Suc m. f k * x ^ k) = f 0 + (∑k<m. f (k + 1) * x ^ k) * x" .
}
from this[of "pred_numeral n"]
show "(∑k<numeral n. f k * x^k) = f 0 + (∑k<pred_numeral n. f (k+1) * x^k) * x"
qed simp_all

lemma power_less_one:
assumes "n > 0" "x ≥ 0" "x < 1"
shows   "x ^ n < (1::'a::linordered_semidom)"
proof -
from assms consider "x > 0" | "x = 0" by force
thus ?thesis
proof cases
assume "x > 0"
with assms show ?thesis
by (cases n) (simp, hypsubst, rule power_Suc_less_one)
qed (insert assms, cases n, simp_all)
qed

lemma combine_bounds:
"x ∈ {a1..b1} ⟹ y ∈ {a2..b2} ⟹ a3 = a1 + a2 ⟹ b3 = b1 + b2 ⟹ x + y ∈ {a3..(b3::real)}"
"x ∈ {a1..b1} ⟹ y ∈ {a2..b2} ⟹ a3 = a1 - b2 ⟹ b3 = b1 - a2 ⟹ x - y ∈ {a3..(b3::real)}"
"c ≥ 0 ⟹ x ∈ {a..b} ⟹ c * x ∈ {c*a..c*b}"
by (auto simp: mult_left_mono)

lemma approx_coarsen:
"¦x - a1¦ ≤ eps1 ⟹ ¦a1 - a2¦ ≤ eps2 - eps1 ⟹ ¦x - a2¦ ≤ (eps2 :: real)"
by simp

subsection ‹Approximations of the exponential function›

lemma two_power_fact_le_fact:
assumes "n ≥ 1"
shows   "2^k * fact n ≤ (fact (n + k) :: 'a :: {semiring_char_0,linordered_semidom})"
proof (induction k)
case (Suc k)
have "2 ^ Suc k * fact n = 2 * (2 ^ k * fact n)" by (simp add: algebra_simps)
also note Suc.IH
also from assms have "of_nat 1 + of_nat 1 ≤ of_nat n + (of_nat (Suc k) :: 'a)"
by (intro add_mono) (unfold of_nat_le_iff, simp_all)
hence "2 * (fact (n + k) :: 'a) ≤ of_nat (n + Suc k) * fact (n + k)"
also have "… = fact (n + Suc k)" by simp
finally show ?case by - (simp add: mult_left_mono)
qed simp_all

text ‹
We approximate the exponential function with inputs between $0$ and $2$ by its
Taylor series expansion and bound the error term with $0$ from below and with a
geometric series from above.
›
lemma exp_approx:
assumes "n > 0" "0 ≤ x" "x < 2"
shows   "exp (x::real) - (∑k<n. x^k / fact k) ∈ {0..(2 * x^n / (2 - x)) / fact n}"
proof (unfold atLeastAtMost_iff, safe)
define approx where "approx = (∑k<n. x^k / fact k)"
have "(λk. x^k / fact k) sums exp x"
using exp_converges[of x] by (simp add: field_simps)
from sums_split_initial_segment[OF this, of n]
have sums: "(λk. x^n * (x^k / fact (n+k))) sums (exp x - approx)"

from assms show "(exp x - approx) ≥ 0"
by (intro sums_le[OF _ sums_zero sums]) auto

have "∀k. x^n * (x^k / fact (n+k)) ≤ (x^n / fact n) * (x / 2)^k"
proof
fix k :: nat
have "x^n * (x^k / fact (n + k)) = x^(n+k) / fact (n + k)" by (simp add: power_add)
also from assms have "… ≤ x^(n+k) / (2^k * fact n)"
by (intro divide_left_mono two_power_fact_le_fact zero_le_power) simp_all
also have "… = (x^n / fact n) * (x / 2) ^ k"
finally show "x^n * (x^k / fact (n+k)) ≤ (x^n / fact n) * (x / 2)^k" .
qed
moreover note sums
moreover {
from assms have "(λk. (x^n / fact n) * (x / 2)^k) sums ((x^n / fact n) * (1 / (1 - x / 2)))"
by (intro sums_mult geometric_sums) simp_all
also from assms have "((x^n / fact n) * (1 / (1 - x / 2))) = (2 * x^n / (2 - x)) / fact n"
by (auto simp: divide_simps)
finally have "(λk. (x^n / fact n) * (x / 2)^k) sums …" .
}
ultimately show "(exp x - approx) ≤ (2 * x^n / (2 - x)) / fact n"
by (rule sums_le)
qed

text ‹
The following variant gives a simpler error estimate for inputs between $0$ and $1$:
›
lemma exp_approx':
assumes "n > 0" "0 ≤ x" "x ≤ 1"
shows   "¦exp (x::real) - (∑k≤n. x^k / fact k)¦ ≤ x ^ n / fact n"
proof -
from assms have "x^n / (2 - x) ≤ x^n / 1" by (intro frac_le) simp_all
hence "(2 * x^n / (2 - x)) / fact n ≤ 2 * x^n / fact n"
using assms by (simp add: divide_simps)
with exp_approx[of n x] assms
have "exp (x::real) - (∑k<n. x^k / fact k) ∈ {0..2 * x^n / fact n}" by simp
moreover have "(∑k≤n. x^k / fact k) = (∑k<n. x^k / fact k) + x ^ n / fact n"
ultimately show "¦exp (x::real) - (∑k≤n. x^k / fact k)¦ ≤ x ^ n / fact n"
unfolding atLeastAtMost_iff by linarith
qed

text ‹
By adding $x^n / n!$ to the approximation (i.e. taking one more term from the
Taylor series), one can get the error bound down to $x^n / n!$.

This means that the number of accurate binary digits produced by the approximation is
asymptotically equal to $(n \log n - n) / \log 2$ by Stirling's formula.
›
lemma exp_approx'':
assumes "n > 0" "0 ≤ x" "x ≤ 1"
shows   "¦exp (x::real) - (∑k≤n. x^k / fact k)¦ ≤ 1 / fact n"
proof -
from assms have "¦exp x - (∑k≤n. x ^ k / fact k)¦ ≤ x ^ n / fact n"
by (rule exp_approx')
also from assms have "… ≤ 1 / fact n" by (simp add: divide_simps power_le_one)
finally show ?thesis .
qed

text ‹
We now define an approximation function for Euler's constant $e$.
›

definition euler_approx :: "nat ⇒ real" where
"euler_approx n = (∑k≤n. inverse (fact k))"

definition euler_approx_aux :: "nat ⇒ nat" where
"euler_approx_aux n = (∑k≤n. ∏{k + 1..n})"

lemma exp_1_approx:
"n > 0 ⟹ ¦exp (1::real) - euler_approx n¦ ≤ 1 / fact n"
using exp_approx''[of n 1] by (simp add: euler_approx_def divide_simps)

text ‹
The following allows us to compute the numerator and the denominator of the result
separately, which greatly reduces the amount of rational number arithmetic that we
have to do.
›
lemma euler_approx_altdef [code]:
"euler_approx n = real (euler_approx_aux n) / real (fact n)"
proof -
have "real (∑k≤n. ∏{k+1..n}) = (∑k≤n. ∏i=k+1..n. real i)" by simp
also have "… / fact n = (∑k≤n. 1 / (fact n / (∏i=k+1..n. real i)))"
also have "… = (∑k≤n. 1 / fact k)"
proof (intro sum.cong refl)
fix k assume k: "k ∈ {..n}"
have "fact n = (∏i=1..n. real i)" by (simp add: fact_prod)
also from k have "{1..n} = {1..k} ∪ {k+1..n}" by auto
also have "prod real … / (∏i=k+1..n. real i) = (∏i=1..k. real i)"
by (subst nonzero_divide_eq_eq, simp, subst prod.union_disjoint [symmetric]) auto
also have "… = fact k" by (simp add: fact_prod)
finally show "1 / (fact n / prod real {k + 1..n}) = 1 / fact k" by simp
qed
also have "… = euler_approx n" by (simp add: euler_approx_def field_simps)
finally show ?thesis by (simp add: euler_approx_aux_def)
qed

lemma euler_approx_aux_Suc:
"euler_approx_aux (Suc m) = 1 + Suc m * euler_approx_aux m"
unfolding euler_approx_aux_def
by (subst sum_distrib_left) (simp add: atLeastAtMostSuc_conv)

lemma eval_euler_approx_aux:
"euler_approx_aux 0 = 1"
"euler_approx_aux 1 = 2"
"euler_approx_aux (Suc 0) = 2"
"euler_approx_aux (numeral n) = 1 + numeral n * euler_approx_aux (pred_numeral n)" (is "?th")
proof -
have A: "euler_approx_aux (Suc m) = 1 + Suc m * euler_approx_aux m" for m :: nat
unfolding euler_approx_aux_def
by (subst sum_distrib_left) (simp add: atLeastAtMostSuc_conv)
show ?th by (subst numeral_eq_Suc, subst A, subst numeral_eq_Suc [symmetric]) simp

lemma euler_approx_aux_code [code]:
"euler_approx_aux n = (if n = 0 then 1 else 1 + n * euler_approx_aux (n - 1))"
by (cases n) (simp_all add: eval_euler_approx_aux euler_approx_aux_Suc)

lemmas eval_euler_approx = euler_approx_altdef eval_euler_approx_aux

text ‹Approximations of $e$ to 60 decimals / 128 and 64 bits:›

lemma euler_60_decimals:
"¦exp 1 - 2.718281828459045235360287471352662497757247093699959574966968¦
≤ inverse (10^60::real)"
by (rule approx_coarsen, rule exp_1_approx[of 48])

lemma euler_128:
"¦exp 1 - 924983374546220337150911035843336795079 / 2 ^ 128¦ ≤ inverse (2 ^ 128 :: real)"
by (rule approx_coarsen[OF euler_60_decimals]) simp_all

lemma euler_64:
"¦exp 1 - 50143449209799256683 / 2 ^ 64¦ ≤ inverse (2 ^ 64 :: real)"
by (rule approx_coarsen[OF euler_128]) simp_all

text ‹
An approximation of $e$ to 60 decimals. This is about as far as we can go with the
simplifier with this kind of setup; the exported code of the code generator, on the other
hand, can easily approximate $e$ to 1000 decimals and verify that approximation within
fractions of a second.
›

(* (Uncommented because we don't want to use the code generator;
don't forget to import Code\_Target\_Numeral)) *)
(*
lemma "¦exp 1 - 2.7182818284590452353602874713526624977572470936999595749669676277240766303535475945713821785251664274274663919320030599218174135966290435729003342952605956307381323286279434907632338298807531952510190115738341879307021540891499348841675092447614606680822648001684774118537423454424371075390777449920695517027618386062613313845830007520449338265602976067371132007093287091274437470472306969772093101416928368190255151086574637721112523897844250569536967707854499699679468644549059879316368892300987931277361782154249992295763514822082698951936680331825288693984964651058209392398294887933203625094431173012381970684161403970198376793206832823764648042953118023287825098194558153017567173613320698112509961818815930416903515988885193458072738667385894228792284998920868058257492796104841984443634632449684875602336248270419786232090021609902353043699418491463140934317381436405462531520961836908887070167683964243781405927145635490613031072085103837505101157477041718986106873969655212671546889570350354021¦
≤ inverse (10^1000::real)"
by (rule approx_coarsen, rule exp_1_approx[of 450], simp) eval
*)

subsection ‹Approximation of $\ln 2$›

text ‹
The following three auxiliary constants allow us to force the simplifier to
evaluate intermediate results, simulating call-by-value.
›

definition "ln_approx_aux3 x' e n y d ⟷
¦(2 * y) * (∑k<n. inverse (real (2*k+1)) * (y^2)^k) + d - x'¦ ≤ e - d"
definition "ln_approx_aux2 x' e n y ⟷
ln_approx_aux3 x' e n y (y^(2*n+1) / (1 - y^2) / real (2*n+1))"
definition "ln_approx_aux1 x' e n x ⟷
ln_approx_aux2 x' e n ((x - 1) / (x + 1))"

lemma ln_approx_abs'':
fixes x :: real and n :: nat
defines "y ≡ (x-1)/(x+1)"
defines "approx ≡ (∑k<n. 2*y^(2*k+1) / of_nat (2*k+1))"
defines "d ≡ y^(2*n+1) / (1 - y^2) / of_nat (2*n+1)"
assumes x: "x > 1"
assumes A: "ln_approx_aux1 x' e n x"
shows   "¦ln x - x'¦ ≤ e"
proof (rule approx_coarsen[OF ln_approx_abs[OF x, of n]], goal_cases)
case 1
from A have "¦2 * y * (∑k<n. inverse (real (2 * k + 1)) * y⇧2 ^ k) + d - x'¦ ≤ e - d"
by (simp only: ln_approx_aux3_def ln_approx_aux2_def ln_approx_aux1_def
y_def [symmetric] d_def [symmetric])
also have "2 * y * (∑k<n. inverse (real (2 * k + 1)) * y⇧2 ^ k) =
(∑k<n. 2 * y^(2*k+1) / (real (2 * k + 1)))"
by (subst sum_distrib_left, simp, subst power_mult)
finally show ?case by (simp only: d_def y_def approx_def)
qed

text ‹
We unfold the above three constants successively and then compute the
sum using a Horner scheme.
›
lemma ln_2_40_decimals:
"¦ln 2 - 0.6931471805599453094172321214581765680755¦
≤ inverse (10^40 :: real)"
apply (rule ln_approx_abs''[where n = 40], simp)
apply (simp add: ln_approx_aux2_def power2_eq_square power_divide)
done

lemma ln_2_128:
"¦ln 2 - 235865763225513294137944142764154484399 / 2 ^ 128¦ ≤ inverse (2 ^ 128 :: real)"
by (rule approx_coarsen[OF ln_2_40_decimals]) simp_all

lemma ln_2_64:
"¦ln 2 - 12786308645202655660 / 2 ^ 64¦ ≤ inverse (2 ^ 64 :: real)"
by (rule approx_coarsen[OF ln_2_128]) simp_all

subsection ‹Approximation of the Euler--Mascheroni constant›

text ‹
Unfortunatly, the best approximation we have formalised for the Euler--Mascheroni
constant converges only quadratically. This is too slow to compute more than a
few decimals, but we can get almost 4 decimals / 14 binary digits this way,
›
lemma euler_mascheroni_approx:
defines "approx ≡ 0.577257 :: real" and "e ≡ 0.000063 :: real"
shows   "abs (euler_mascheroni - approx :: real) < e"
(is "abs (_ - ?approx) < ?e")
proof -
define l :: real
where "l = 47388813395531028639296492901910937/82101866951584879688289000000000000"
define u :: real
where "u = 142196984054132045946501548559032969 / 246305600854754639064867000000000000"
have impI: "P ⟶ Q" if Q for P Q using that by blast
have hsum_63: "harm 63 = (310559566510213034489743057 / 65681493561267903750631200 :: real)"
from harm_Suc[of 63] have hsum_64: "harm 64 =
623171679694215690971693339 / (131362987122535807501262400::real)"
by (subst (asm) hsum_63) simp
have "ln (64::real) = real (6::nat) * ln 2" by (subst ln_realpow[symmetric]) simp_all
hence "ln (real_of_nat (Suc 63)) ∈ {4.158883083293<..<4.158883083367}" using ln_2_64
by (simp add: abs_real_def split: if_split_asm)
from euler_mascheroni_bounds'[OF _ this]
have "(euler_mascheroni :: real) ∈ {l<..<u}"
by (simp add: hsum_63 del: greaterThanLessThan_iff) (simp only: l_def u_def)
also have "… ⊆ {approx - e<..<approx + e}"
by (subst greaterThanLessThan_subseteq_greaterThanLessThan, rule impI)
(simp add: approx_def e_def u_def l_def)
finally show ?thesis by (simp add: abs_real_def)
qed

subsection ‹Approximation of pi›

subsubsection ‹Approximating the arctangent›

text‹
The arctangent can be used to approximate pi. Fortunately, its Taylor series expansion
converges exponentially for small values, so we can get $\Theta(n)$ digits of precision
with $n$ summands of the expansion.
›

definition arctan_approx where
"arctan_approx n x = x * (∑k<n. (-(x^2))^k / real (2*k+1))"

lemma arctan_series':
assumes "¦x¦ ≤ 1"
shows "(λk. (-1)^k * (1 / real (k*2+1) * x ^ (k*2+1))) sums arctan x"
using summable_arctan_series[OF assms] arctan_series[OF assms] by (simp add: sums_iff)

lemma arctan_approx:
assumes x: "0 ≤ x" "x < 1" and n: "even n"
shows   "arctan x - arctan_approx n x ∈ {0..x^(2*n+1) / (1-x^4)}"
proof -
define c where "c k = 1 / (1+(4*real k + 2*real n)) - x⇧2 / (3+(4*real k + 2*real n))" for k
from assms have "(λk. (-1) ^ k * (1 / real (k * 2 + 1) * x^(k*2+1))) sums arctan x"
using arctan_series' by simp
also have "(λk. (-1) ^ k * (1 / real (k * 2 + 1) * x^(k*2+1))) =
(λk. x * ((- (x^2))^k / real (2*k+1)))"
by (simp add: power2_eq_square power_mult power_mult_distrib mult_ac power_minus')
finally have "(λk. x * ((- x⇧2) ^ k / real (2 * k + 1))) sums arctan x" .
from sums_split_initial_segment[OF this, of n]
have "(λi. x * ((- x⇧2) ^ (i + n) / real (2 * (i + n) + 1))) sums
(arctan x - arctan_approx n x)"
from sums_group[OF this, of 2] assms
have sums: "(λk. x * (x⇧2)^n * (x^4)^k * c k) sums (arctan x - arctan_approx n x)"

from assms have "0 ≤ arctan x - arctan_approx n x"
by (intro sums_le[OF _ sums_zero sums] allI mult_nonneg_nonneg)
(auto intro!: frac_le power_le_one simp: c_def)
moreover {
from assms have "c k ≤ 1 - 0" for k unfolding c_def
by (intro diff_mono divide_nonneg_nonneg add_nonneg_nonneg) auto
with assms have "x * x⇧2 ^ n * (x ^ 4) ^ k * c k ≤ x * x⇧2 ^ n * (x ^ 4) ^ k * 1" for k
by (intro mult_left_mono mult_right_mono mult_nonneg_nonneg) simp_all
with assms have "arctan x - arctan_approx n x ≤ x * (x⇧2)^n * (1 / (1 - x^4))"
by (intro sums_le[OF _ sums sums_mult[OF geometric_sums]] allI mult_left_mono)
(auto simp: power_less_one)
also have "x * (x^2)^n = x^(2*n+1)" by (simp add: power_mult power_add)
finally have "arctan x - arctan_approx n x ≤ x^(2*n+1) / (1 - x^4)" by simp
}
ultimately show ?thesis by simp
qed

lemma arctan_approx_def': "arctan_approx n (1/x) =
(∑k<n. inverse (real (2 * k + 1) * (- x⇧2) ^ k)) / x"
proof -
have "(-1)^k / b = 1 / ((-1)^k * b)" for k :: nat and b :: real
by (cases "even k") auto
thus ?thesis by (simp add: arctan_approx_def  field_simps power_minus')
qed

lemma expand_arctan_approx:
"(∑k<(numeral n::nat). inverse (f k) * inverse (x ^ k)) =
inverse (f 0) + (∑k<pred_numeral n. inverse (f (k+1)) * inverse (x^k)) / x"
"(∑k<Suc 0. inverse (f k) * inverse (x^k)) = inverse (f 0 :: 'a :: field)"
"(∑k<(0::nat). inverse (f k) * inverse (x^k)) = 0"
proof -
{
fix m :: nat
have "(∑k<Suc m. inverse (f k * x^k)) =
inverse (f 0) + (∑k=Suc 0..<Suc m. inverse (f k * x^k))"
by (subst atLeast0LessThan [symmetric], subst sum_head_upt_Suc) simp_all
also have "(∑k=Suc 0..<Suc m. inverse (f k * x^k)) = (∑k<m. inverse (f (k+1) * x^k)) / x"
by (subst sum_shift_bounds_Suc_ivl)
atLeast0LessThan power_commutes)
finally have "(∑k<Suc m. inverse (f k) * inverse (x ^ k)) =
inverse (f 0) + (∑k<m. inverse (f (k + 1)) * inverse (x ^ k)) / x" by simp
}
from this[of "pred_numeral n"]
show "(∑k<numeral n. inverse (f k) * inverse (x^k)) =
inverse (f 0) + (∑k<pred_numeral n. inverse (f (k+1)) * inverse (x^k)) / x"
qed simp_all

lemma arctan_diff_small:
assumes "¦x*y::real¦ < 1"
shows   "arctan x - arctan y = arctan ((x - y) / (1 + x * y))"
proof -
have "arctan x - arctan y = arctan x + arctan (-y)" by (simp add: arctan_minus)
also from assms have "… = arctan ((x - y) / (1 + x * y))" by (subst arctan_add_small) simp_all
finally show ?thesis .
qed

subsubsection ‹Machin-like formulae for pi›

text ‹
We first define a small proof method that can prove Machin-like formulae for @{term "pi"}
automatically. Unfortunately, this takes far too much time for larger formulae because
the numbers involved become too large.
›

definition "MACHIN_TAG a b ≡ a * (b::real)"

lemma numeral_horner_MACHIN_TAG:
"MACHIN_TAG Numeral1 x = x"
"MACHIN_TAG (numeral (Num.Bit0 (Num.Bit0 n))) x =
MACHIN_TAG 2 (MACHIN_TAG (numeral (Num.Bit0 n)) x)"
"MACHIN_TAG (numeral (Num.Bit0 (Num.Bit1 n))) x =
MACHIN_TAG 2 (MACHIN_TAG (numeral (Num.Bit1 n)) x)"
"MACHIN_TAG (numeral (Num.Bit1 n)) x =
MACHIN_TAG 2 (MACHIN_TAG (numeral n) x) + x"
unfolding numeral_Bit0 numeral_Bit1 ring_distribs one_add_one[symmetric] MACHIN_TAG_def

lemma tag_machin: "a * arctan b = MACHIN_TAG a (arctan b)" by (simp add: MACHIN_TAG_def)

lemma arctan_double': "¦a::real¦ < 1 ⟹ MACHIN_TAG 2 (arctan a) = arctan (2 * a / (1 - a*a))"
unfolding MACHIN_TAG_def by (simp add: arctan_double power2_eq_square)

ML ‹
fun machin_term_conv ctxt ct =
let
in
case Thm.term_of ct of
Const (@{const_name MACHIN_TAG}, _) $_$
(Const (@{const_name "Transcendental.arctan"}, _) $_) => Simplifier.rewrite ctxt' ct | Const (@{const_name MACHIN_TAG}, _)$ _ $(Const (@{const_name "Groups.plus"}, _)$
(Const (@{const_name "Transcendental.arctan"}, _) $_)$
(Const (@{const_name "Transcendental.arctan"}, _) $_)) => Simplifier.rewrite ctxt' ct | _ => raise CTERM ("machin_conv", [ct]) end fun machin_tac ctxt = let val conv = Conv.top_conv (Conv.try_conv o machin_term_conv) ctxt in SELECT_GOAL ( Local_Defs.unfold_tac ctxt @{thms tag_machin[THEN eq_reflection] numeral_horner_MACHIN_TAG[THEN eq_reflection]} THEN REPEAT (CHANGED (HEADGOAL (CONVERSION conv)))) THEN' Simplifier.simp_tac (ctxt addsimps @{thms arctan_add_small arctan_diff_small}) end › method_setup machin = ‹Scan.succeed (SIMPLE_METHOD' o machin_tac)› text ‹ We can now prove the standard'' Machin formula, which was already proven manually in Isabelle, automatically. }› lemma "pi / 4 = (4::real) * arctan (1 / 5) - arctan (1 / 239)" by machin text ‹ We can also prove the following more complicated formula: › lemma machin': "pi/4 = (12::real) * arctan (1/18) + 8 * arctan (1/57) - 5 * arctan (1/239)" by machin subsubsection ‹Simple approximation of pi› text ‹ We can use the simple Machin formula and the Taylor series expansion of the arctangent to approximate pi. For a given even natural number$n$, we expand @{term "arctan (1/5)"} to$3n$summands and @{term "arctan (1/239)"} to$n$summands. This gives us at least$13n-2$bits of precision. › definition "pi_approx n = 16 * arctan_approx (3*n) (1/5) - 4 * arctan_approx n (1/239)" lemma pi_approx: fixes n :: nat assumes n: "even n" and "n > 0" shows "¦pi - pi_approx n¦ ≤ inverse (2^(13*n - 2))" proof - from n have n': "even (3*n)" by simp ― ‹We apply the Machin formula› from machin have "pi = 16 * arctan (1/5) - 4 * arctan (1/239::real)" by simp ― ‹Taylor series expansion of the arctangent› also from arctan_approx[OF _ _ n', of "1/5"] arctan_approx[OF _ _ n, of "1/239"] have "… - pi_approx n ∈ {-4*((1/239)^(2*n+1) / (1-(1/239)^4))..16*(1/5)^(6*n+1) / (1-(1/5)^4)}" by (simp add: pi_approx_def) ― ‹Coarsening the bounds to make them a bit nicer› also have "-4*((1/239::real)^(2*n+1) / (1-(1/239)^4)) = -((13651919 / 815702160) / 57121^n)" by (simp add: power_mult power2_eq_square) (simp add: field_simps) also have "16*(1/5)^(6*n+1) / (1-(1/5::real)^4) = (125/39) / 15625^n" by (simp add: power_mult power2_eq_square) (simp add: field_simps) also have "{-((13651919 / 815702160) / 57121^n) .. (125 / 39) / 15625^n} ⊆ {- (4 / 2^(13*n)) .. 4 / (2^(13*n)::real)}" by (subst atLeastatMost_subset_iff, intro disjI2 conjI le_imp_neg_le) (rule frac_le; simp add: power_mult power_mono)+ finally have "abs (pi - pi_approx n) ≤ 4 / 2^(13*n)" by auto also from ‹n > 0› have "4 / 2^(13*n) = 1 / (2^(13*n - 2) :: real)" by (cases n) (simp_all add: power_add) finally show ?thesis by (simp add: divide_inverse) qed lemma pi_approx': fixes n :: nat assumes n: "even n" and "n > 0" and "k ≤ 13*n - 2" shows "¦pi - pi_approx n¦ ≤ inverse (2^k)" using assms(3) by (intro order.trans[OF pi_approx[OF assms(1,2)]]) (simp_all add: field_simps) text ‹We can now approximate pi to 22 decimals within a fraction of a second.› lemma pi_approx_75: "abs (pi - 3.1415926535897932384626 :: real) ≤ inverse (10^22)" proof - define a :: real where "a = 8295936325956147794769600190539918304 / 2626685325478320010006427764892578125" define b :: real where "b = 8428294561696506782041394632 / 503593538783547230635598424135" ― ‹The introduction of this constant prevents the simplifier from applying solvers that we don't want. We want it to simply evaluate the terms to rational constants.}› define eq :: "real ⇒ real ⇒ bool" where "eq = (=)" ― ‹Splitting the computation into several steps has the advantage that simplification can be done in parallel› have "abs (pi - pi_approx 6) ≤ inverse (2^76)" by (rule pi_approx') simp_all also have "pi_approx 6 = 16 * arctan_approx (3 * 6) (1 / 5) - 4 * arctan_approx 6 (1 / 239)" unfolding pi_approx_def by simp also have [unfolded eq_def]: "eq (16 * arctan_approx (3 * 6) (1 / 5)) a" by (simp add: arctan_approx_def' power2_eq_square, simp add: expand_arctan_approx, unfold a_def eq_def, rule refl) also have [unfolded eq_def]: "eq (4 * arctan_approx 6 (1 / 239::real)) b" by (simp add: arctan_approx_def' power2_eq_square, simp add: expand_arctan_approx, unfold b_def eq_def, rule refl) also have [unfolded eq_def]: "eq (a - b) (171331331860120333586637094112743033554946184594977368554649608 / 54536456744112171868276045488779391002026386559009552001953125)" by (unfold a_def b_def, simp, unfold eq_def, rule refl) finally show ?thesis by (rule approx_coarsen) simp qed text ‹ The previous estimate of pi in this file was based on approximating the root of the$\sin(\pi/6)$in the interval$[0;4]$using the Taylor series expansion of the sine to verify that it is between two given bounds. This was much slower and much less precise. We can easily recover this coarser estimate from the newer, precise estimate: › lemma pi_approx_32: "¦pi - 13493037705/4294967296 :: real¦ ≤ inverse(2 ^ 32)" by (rule approx_coarsen[OF pi_approx_75]) simp subsection ‹A more complicated approximation of pi› text ‹ There are more complicated Machin-like formulae that have more terms with larger denominators. Although they have more terms, each term requires fewer summands of the Taylor series for the same precision, since it is evaluated closer to$0\$.

Using a good formula, one can therefore obtain the same precision with fewer operations.
The big formulae used for computations of pi in practice are too complicated for us to
prove here, but we can use the three-term Machin-like formula @{thm machin'}.
›

definition "pi_approx2 n = 48 * arctan_approx (6*n) (1/18::real) +
32 * arctan_approx (4*n) (1/57) - 20 * arctan_approx (3*n) (1/239)"

lemma pi_approx2:
fixes n :: nat assumes n: "even n" and "n > 0"
shows   "¦pi - pi_approx2 n¦ ≤ inverse (2^(46*n - 1))"
proof -
from n have n': "even (6*n)" "even (4*n)" "even (3*n)" by simp_all
from machin' have "pi = 48 * arctan (1/18) + 32 * arctan (1/57) - 20 * arctan (1/239::real)"
by simp
hence "pi - pi_approx2 n = 48 * (arctan (1/18) - arctan_approx (6*n) (1/18)) +
32 * (arctan (1/57) - arctan_approx (4*n) (1/57)) -
20 * (arctan (1/239) - arctan_approx (3*n) (1/239))"
also have "… ∈ {-((20/239/(1-(1/239)^4)) * (1/239)^(6*n))..
(48/18 / (1-(1/18)^4))*(1/18)^(12*n) + (32/57/(1-(1/57)^4)) * (1/57)^(8*n)}"
(is "_ ∈ {-?l..?u1 + ?u2}")
apply ((rule combine_bounds(1,2))+; (rule combine_bounds(3); (rule arctan_approx)?)?)
done
also {
have "?l ≤ (1/8) * (1/2)^(46*n)"
unfolding power_mult by (intro mult_mono power_mono) (simp_all add: divide_simps)
also have "… ≤ (1/2) ^ (46 * n - 1)"
finally have "?l ≤ (1/2) ^ (46 * n - 1)" .
moreover {
have "?u1 + ?u2 ≤ 4 * (1/2)^(48*n) + 1 * (1/2)^(46*n)"
also from ‹n > 0› have "4 * (1/2::real)^(48*n) ≤ (1/2)^(46*n)"
also from ‹n > 0› have "(1/2::real) ^ (46 * n) + 1 * (1 / 2) ^ (46 * n) = (1/2) ^ (46 * n - 1)"
finally have "?u1 + ?u2 ≤ (1/2) ^ (46 * n - 1)" by - simp
}
ultimately have "{-?l..?u1 + ?u2} ⊆ {-((1/2)^(46*n-1))..(1/2)^(46*n-1)}"
by (subst atLeastatMost_subset_iff) simp_all
}
finally have "¦pi - pi_approx2 n¦ ≤ ((1/2) ^ (46 * n - 1))" by auto
thus ?thesis by (simp add: divide_simps)
qed

lemma pi_approx2':
fixes n :: nat assumes n: "even n" and "n > 0" and "k ≤ 46*n - 1"
shows   "¦pi - pi_approx2 n¦ ≤ inverse (2^k)"
using assms(3) by (intro order.trans[OF pi_approx2[OF assms(1,2)]]) (simp_all add: field_simps)

text ‹
We can now approximate pi to 54 decimals using this formula. The computations are much
slower now; this is mostly because we use arbitrary-precision rational numbers, whose
numerators and demoninators get very large. Using dyadic floating point numbers would be
much more economical.
›
lemma pi_approx_54_decimals:
"abs (pi - 3.141592653589793238462643383279502884197169399375105821 :: real) ≤ inverse (10^54)"
(is "abs (pi - ?pi') ≤ _")
proof -
define a :: real
where "a = 2829469759662002867886529831139137601191652261996513014734415222704732791803 /
1062141879292765061960538947347721564047051545995266466660439319087625011200"
define b :: real
where "b = 13355545553549848714922837267299490903143206628621657811747118592 /
23792006023392488526789546722992491355941103837356113731091180925"
define c :: real
where "c = 28274063397213534906669125255762067746830085389618481175335056 /
337877029279505250241149903214554249587517250716358486542628059"
let ?pi'' = "3882327391761098513316067116522233897127356523627918964967729040413954225768920394233198626889767468122598417405434625348404038165437924058179155035564590497837027530349 /
1235783190199688165469648572769847552336447197542738425378629633275352407743112409829873464564018488572820294102599160968781449606552922108667790799771278860366957772800"
define eq :: "real ⇒ real ⇒ bool" where "eq = (=)"

have "abs (pi - pi_approx2 4) ≤ inverse (2^183)" by (rule pi_approx2') simp_all
also have "pi_approx2 4 = 48 * arctan_approx 24 (1 / 18) +
32 * arctan_approx 16 (1 / 57) -
20 * arctan_approx 12 (1 / 239)"
unfolding pi_approx2_def by simp
also have [unfolded eq_def]: "eq (48 * arctan_approx 24 (1 / 18)) a"
simp add: expand_arctan_approx, unfold a_def eq_def, rule refl)
also have [unfolded eq_def]: "eq (32 * arctan_approx 16 (1 / 57::real)) b"
simp add: expand_arctan_approx, unfold b_def eq_def, rule refl)
also have [unfolded eq_def]: "eq (20 * arctan_approx 12 (1 / 239::real)) c"
simp add: expand_arctan_approx, unfold c_def eq_def, rule refl)
also have [unfolded eq_def]:
"eq (a + b) (34326487387865555303797183505809267914709125998469664969258315922216638779011304447624792548723974104030355722677 /
10642967245546718617684989689985787964158885991018703366677373121531695267093031090059801733340658960857196134400)"
by (unfold a_def b_def c_def, simp, unfold eq_def, rule refl)
also have [unfolded eq_def]: "eq (… - c) ?pi''"
by (unfold a_def b_def c_def, simp, unfold eq_def, rule refl)
― ‹This is incredibly slow because the numerators and denominators are huge.›
finally show ?thesis by (rule approx_coarsen) simp
qed

text ‹A 128 bit approximation of pi:›
lemma pi_approx_128:
"abs (pi - 1069028584064966747859680373161870783301 / 2^128) ≤ inverse (2^128)"
by (rule approx_coarsen[OF pi_approx_54_decimals]) simp

text ‹A 64 bit approximation of pi:›
lemma pi_approx_64:
"abs (pi - 57952155664616982739 / 2^64 :: real) ≤ inverse (2^64)"
by (rule approx_coarsen[OF pi_approx_54_decimals]) simp

text ‹
Again, going much farther with the simplifier takes a long time, but the code generator
can handle even two thousand decimal digits in under 20 seconds.
›

end