Zulip Chat Archive

Stream: Is there code for X?

Topic: Bounds on alternating convergent series


Jeremy Tan (Jan 19 2024 at 09:35):

#9847 proves John Machin's 1706 formula for π, but that's just the formula. What he then did was exploit the Taylor series of the arctangent (Taylor was Machin's contemporary) to compute π to 100 decimal places.

Suppose we reproduced his calculation in mathlib. At some point we would be using the following well-known three-part theorem:

  1. Any alternating series where the term magnitude strictly decreases is convergent.
  2. Every partial sum ending at a negative term is a lower bound on the limiting value.
  3. Every partial sum ending at a positive term is an upper bound on the limiting value.

Do we have any of these three parts in mathlib, for general alternating series?

Mario Carneiro (Jan 19 2024 at 09:50):

we have the leibniz formula for pi, which should contain all the same components

Yury G. Kudryashov (Jan 19 2024 at 19:04):

I remember that there was a PR about alternating series. I don't remember if it landed.

Yury G. Kudryashov (Jan 19 2024 at 19:07):

We have docs#Monotone.cauchySeq_alternating_series_of_tendsto_zero and the next few lemmas for the first item.

Yury G. Kudryashov (Jan 19 2024 at 19:09):

AFAICS, we don't have the other two.

Yury G. Kudryashov (Jan 19 2024 at 19:10):

Though it's probably hidden inside the proof.

Joseph Myers (Jan 20 2024 at 01:03):

The Taylor series for arctan is hidden inside the proof; we don't have it as a separate theorem. (I've previously suggested that the Leibniz formula for pi should be deduced by combining the Taylor series for arctan with Abel's theorem, neither of which we currently have as a separate theorem in mathlib.)

Jeremy Tan (Jan 21 2024 at 23:57):

In any case I wrote #9889 which defines the complex arctangent and shows its Taylor series expansion

Jeremy Tan (Jan 22 2024 at 16:21):

@Joseph Myers is this a correct statement of Abel's theorem?

import Mathlib

open Filter Set

open scoped Classical BigOperators Topology Real

theorem abels_theorem (f :   ) (g :   )
    (hf :  l, Tendsto (fun n   i in Finset.range n, f n) atTop (𝓝 l))
    (h :  x : , |x| < 1  Tendsto (fun n   i in Finset.range n, f n * x ^ n) atTop (𝓝 (g x)))
    (l : ) (hg : Tendsto g (𝓝[<] 1) (𝓝 l)) :
    Tendsto (fun n   i in Finset.range n, f n) atTop (𝓝 l) := by
  sorry

Michael Stoll (Jan 22 2024 at 16:28):

There is also a version for functions on the complex unit disk; see Wikipedia. The convergence within the "Stolz sector" can be conveniently expressed via a nhdsWithin filter, I think.

Jeremy Tan (Jan 22 2024 at 16:48):

import Mathlib

open Filter Set

open scoped Classical BigOperators Topology Real Complex

def stolzSector (M : ) : Filter  :=
  nhdsWithin 1 {z | 1 - z < M * (1 - z)}

theorem abels_theorem {f :   } {g :   }
    (h :  z, z < 1  Tendsto (fun n   i in Finset.range n, f n * z ^ n) atTop (𝓝 (g z)))
    (hf :  l, Tendsto (fun n   i in Finset.range n, f n) atTop (𝓝 l))
    {l : } {M : } (hg : Tendsto g (stolzSector M) (𝓝 l)) (hM : 1 < M) :
    Tendsto (fun n   i in Finset.range n, f n) atTop (𝓝 l) := by
  sorry

Michael Stoll (Jan 22 2024 at 16:54):

I think this does not exactly capture the assumptions. I would perhaps write it as

import Mathlib

open Filter Set

open scoped Classical BigOperators Topology Real Complex

def stolzSector (M : ) : Filter  :=
  nhdsWithin 1 {z | 1 - z < M * (1 - z)}

theorem abels_theorem {f :   } (M : ) {l : } (h : Tendsto (fun n   i in Finset.range n, f n) atTop (𝓝 l)) :
    Tendsto (fun z  tsum (fun n  f n * z ^ n)) (stolzSector M) (𝓝 l) := by
  sorry

The assumption is that the power series converges at z = 1 (which implies that it converges absolutely on the open unit disk), and the statement is that one can swap the two limits involved.

Jeremy Tan (Jan 22 2024 at 17:18):

Your formulation is begging the question with regards to replacing the ad-hoc proof in Data.Real.Pi.Leibniz. The central theorem there is precisely your hypothesis h

Jeremy Tan (Jan 22 2024 at 17:19):

Abel's theorem is intended to give you l, not ask l from you at the start

Jeremy Tan (Jan 22 2024 at 17:20):

Perhaps you swapped the hypothesis and statement?

Michael Stoll (Jan 22 2024 at 18:22):

I think what I wrote is a faithful translation of the statement as given on the Wikipedia page.
To use it, you only need the existence of l (I don't think we have an analogue of Summable (yet?) in this context; otherwise one could write the hypothesis in terms of that). You can then define g as the value of the power series (on the open unit disk), and then the theorem tells you that its limit (within a Stolz sector) is the same as the value of the power series at 1 (which was l). I.e., with the right notions, we could write

theorem abels_theorem {f :   } (M : ) (h : Summable' f) :
    Tendsto (fun z  tsum (fun n  f n * z ^ n)) (stolzSector M) (𝓝 (tsum' f))

where Summable' says that the series with terms f n converges (conditionally) and tsum' f is the value of the series.

Michael Stoll (Jan 22 2024 at 18:25):

And so, if you know that Tendsto g (stolzSector M) (𝓝 l), you can conclude that tsum' f = l.

Michael Stoll (Jan 22 2024 at 18:47):

(Note that a hypothesis (h : ∃ x : T, P x) is completely equivalent to (x : T) (h : P x), and g can be eliminated from your statement, since it can be defined in terms of f.)

Jeremy Tan (Jan 23 2024 at 02:40):

Michael Stoll said:

I.e., with the right notions, we could write

theorem abels_theorem {f :   } (M : ) (h : Summable' f) :
    Tendsto (fun z  tsum (fun n  f n * z ^ n)) (stolzSector M) (𝓝 (tsum' f))

where Summable' says that the series with terms f n converges (conditionally) and tsum' f is the value of the series.

theorem abels_theorem {f :   } (h : CauchySeq fun n =>  i in range n, f i) (M : ) :
    Tendsto (fun z  ∑' n, f n * z ^ n) (stolzSector M)
      (𝓝 (cauchySeq_tendsto_of_complete h).choose) := by
  sorry

Jeremy Tan (Jan 23 2024 at 02:42):

Since is complete, the CauchySeq hypothesis is equivalent to conditional convergence

Jeremy Tan (Jan 23 2024 at 12:11):

Apparently I need to show this theorem first

import Mathlib.Analysis.SpecialFunctions.Trigonometric.ArctanDeriv
import Mathlib.Analysis.SpecialFunctions.Pow.Asymptotics

open Filter Finset Complex
open scoped BigOperators Topology

lemma yyz {f :   } {l : } (h : Tendsto (fun n   i in range n, f n) atTop (𝓝 l))
    {z : } (hz : z < 1) : Summable (fun n  f n * z ^ n) := by
  sorry

Michael Stoll (Jan 23 2024 at 13:49):

I think the way to get that is to first show that f is bounded (from h) and then use comparison with the geometric series.

Yury G. Kudryashov (Jan 23 2024 at 14:35):

You can use docs#Filter.Tendsto.isBigO_one, multiply it by docs#Asymptotics.isBigO_refl, then use docs#summable_of_isBigO_nat

Yury G. Kudryashov (Jan 23 2024 at 14:36):

You may want to add IsBigO.smul_summable to the library.

Jeremy Tan (Jan 23 2024 at 16:53):

import Mathlib.Analysis.Complex.Basic
import Mathlib.Analysis.SpecificLimits.Normed

-- https://planetmath.org/proofofabelslimittheorem

open Filter Finset Complex
open scoped BigOperators Topology

def stolzSector (M : ) : Filter  :=
  nhdsWithin 1 {z | 1 - z  M * (1 - z)}

variable {f :   } {l : } (h : Tendsto (fun n   i in range n, f i) atTop (𝓝 l))

/-- The term norms of any convergent series are bounded by some universal constant. -/
lemma norm_bounded_of_tendsto :  C,  n, f n  C := by
  obtain b, _, key, _⟩⟩ := cauchySeq_iff_le_tendsto_0.mp h.cauchySeq
  refine' b 0, fun n  _
  replace key := key n (n + 1) 0 (zero_le _) (zero_le _)
  rwa [dist_partial_sum'] at key

/-- The power series associated with any convergent series is _absolutely_ convergent
in the open unit disc. -/
lemma summable_mul_geometric_of_tendsto {z : } (hz : z < 1) :
    Summable (fun n  f n * z ^ n) := by
  obtain C, hC := norm_bounded_of_tendsto h
  refine' Summable.of_norm_bounded (fun n  C * z ^ n)
    ((summable_geometric_of_abs_lt_1 (by simpa)).mul_left C) (fun n  _)
  simp_rw [norm_mul, norm_pow]
  gcongr
  exact hC n

Michael Stoll (Jan 23 2024 at 17:04):

It would be nice to also have the version for general radius of convergence (which can either be reduced to your special case or proved directly). I.e., convergence of the power series at some w implies absolute convergence at all z such that ‖z‖ < ‖w‖.

Jeremy Tan (Jan 23 2024 at 18:31):

import Mathlib.Analysis.Complex.Basic
import Mathlib.Analysis.SpecificLimits.Normed

open Finset Complex
open scoped BigOperators

/-- The power series associated with any convergent series is _absolutely_ convergent
in the open unit disc. -/
lemma summable_power_of_cauchy_series {f :   }
    (h : CauchySeq fun n   k in range n, f k) {z : } (hz : z < 1) :
    Summable fun n  f n * z ^ n := by
  obtain C, hC := norm_bounded_of_cauchy_series h
  rw [summable_iff_cauchySeq_finset]
  refine' cauchySeq_finset_of_geometric_bound (C := C) hz (fun n  _)
  simp_rw [norm_mul, norm_pow]
  gcongr
  exact hC n

/-- If a power series converges at `w`, it converges _absolutely_ for all `z` of lesser norm. -/
lemma summable_power_of_norm_lt {f :   } {w : }
    (h : CauchySeq fun n   i in range n, f i * w ^ i) {z : } (hz : z < w) :
    Summable fun n  f n * z ^ n := by
  cases' (norm_nonneg w).eq_or_gt with hw hw
  · exact absurd ((norm_nonneg z).trans_lt (hw  hz)) (lt_irrefl 0)
  have := @summable_power_of_cauchy_series _ h (z / w) (by rwa [norm_div, div_lt_one hw])
  rw [norm_eq_abs, AbsoluteValue.pos_iff] at hw
  simp_rw [mul_assoc,  mul_pow, mul_div_cancel' _ hw] at this
  exact this

Jeremy Tan (Jan 24 2024 at 03:33):

The above two lemmas are now at #9955

Michael Stoll (Jan 25 2024 at 09:13):

This looks good to me. Maybe someone better versed in the naming conventions can have a look at the lemma names? There is docs#cauchy_series_of_le_geometric, so I would think the "cauchy_series" part is OK. We have docs#PowerSeries, so I would think that "powerSeries" instead of just "power" would be more appropriate in the names (even though the lemmas do not talk about terms of type PowerSeries).

Do we have the radius of convergence of a (univariate) power series and API for it?
@loogle PowerSeries, HasSum

loogle (Jan 25 2024 at 09:13):

:shrug: nothing found

Michael Stoll (Jan 25 2024 at 09:14):

@loogle PowerSeries, Summable

loogle (Jan 25 2024 at 09:14):

:shrug: nothing found

Michael Stoll (Jan 25 2024 at 09:14):

Maybe not :frown:

Jeremy Tan (Jan 25 2024 at 09:28):

I should have proven Abel's limit theorem as you stated it at branch#abellimit

Jeremy Tan (Jan 25 2024 at 09:29):

(haven't made a PR for it because I want that to be #10000)

Michael Stoll (Jan 25 2024 at 10:17):

I wonder whether there might not be a more elegant proof using filters instead of epsilons...

Jeremy Tan (Jan 26 2024 at 10:06):

Probably not @Michael Stoll. Anyway, #10000 it is

Jeremy Tan (Jan 27 2024 at 04:57):

I've extended #10000 to cover the real case

Jeremy Tan (Jan 27 2024 at 05:09):

But surely there's a simpler way to prove this sub-theorem?

have m : (𝓝 l).map (() :   )  (𝓝 l) := by
    intro s hs
    rw [Filter.mem_map']
    rw [Metric.mem_nhds_iff] at hs 
    obtain ε, εpos, ⟩⟩ := hs
    use ε, εpos
    intro x hx
    rw [Set.subset_def] at 
    replace  :=  x
    rw [Metric.mem_ball, dist_eq_norm] at  hx
    norm_cast at 
    rw [Complex.norm_real] at 
    simp [ hx]

Michael Stoll (Jan 27 2024 at 09:33):

Combine docs#Complex.ofRealCLM, docs#ContinuousLinearMap.continuous and docs#Continuous.tendsto ? (This does not give exactly what you want, but is maybe close to what you actually need).
(I have some other commitments now and cannot think about that in more detail.)

Jeremy Tan (Jan 27 2024 at 12:12):

Well now that you mentioned the CLM, it's a two-liner

have m := ofRealCLM.continuous.tendsto l
  rw [show ofRealCLM = ofReal' by rfl, tendsto_iff_comap,  map_le_iff_le_comap] at m

Jeremy Tan (Jan 27 2024 at 13:29):

There's still https://github.com/leanprover-community/mathlib4/pull/10000/files#diff-35646e039986d92194e3a69478b379c60384711e9bb937bee97d74e1dcb73439R73 though

Michael Stoll (Jan 27 2024 at 18:39):

I've suggested a shorter proof of nhdsWithin_lt_le_nhdsWithin_stolzSet.

Yury G. Kudryashov (Jan 28 2024 at 01:21):

Jeremy Tan said:

Well now that you mentioned the CLM, it's a two-liner

have m := ofRealCLM.continuous.tendsto l
  rw [show ofRealCLM = ofReal' by rfl, tendsto_iff_comap,  map_le_iff_le_comap] at m

In fact, Tendsto is defeq to map _ ≤ _, so it's just ofRealCLM.continuous.tendsto

Jeremy Tan (Jan 30 2024 at 05:25):

I finally proved the thing I originally wanted to prove at #10120

Jeremy Tan (Feb 14 2024 at 07:15):

@Michael Stoll I have absolutely no idea how to address @Sébastien Gouëzel's comment on #10000, regarding turning the Stolz set filter into a cone filter. Please help

Jeremy Tan (Feb 14 2024 at 07:17):

Apparently I have to import Euclidean geometry in order to show that every cone filter lies below some Stolz set filter

Jeremy Tan (Feb 14 2024 at 07:17):

(which would be importing too much)

Michael Stoll (Feb 14 2024 at 13:26):

@Jeremy Tan I'm at a conference until Friday and won't have much time before the weekend, but I'll try to have a look then.

Jeremy Tan (Feb 15 2024 at 02:03):

Well I actually did prove the cone version... using a ton of Euclidean geometry

Jeremy Tan (Feb 18 2024 at 00:37):

@Michael Stoll ummm...

Michael Stoll (Feb 18 2024 at 20:09):

Here is an alternative approach:

import Mathlib.Analysis.Complex.Basic

section StolzSet

-- This (or a more general version with real exponent `0 ≤ r ≤ 1`) seems to be missing in Mathlib.
lemma Real.sqrt_one_add_le {x : } (hx : -1  x) : sqrt (1 + x)  1 + x / 2 := by
  refine sqrt_le_iff.mpr by linarith, ?_
  calc 1 + x
    _  1 + x + (x / 2) ^ 2 := le_add_of_nonneg_right <| sq_nonneg _
    _ = (1 + x / 2) ^ 2 := by ring

-- An ugly technical lemma
open Real in
private lemma Complex.stolzCone'_subset_StolzSet_aux (s : ) :
     M ε, 0 < M  0 < ε   x y, 0 < x  x < ε  |y| < s * x 
      sqrt (x ^ 2 + y ^ 2) < M * (1 - sqrt ((1 - x) ^ 2 + y ^ 2)) := by
  refine 2 * sqrt (1 + s ^ 2) + 1, 1 / (1 + s ^ 2), by positivity, by positivity,
    fun x y hx₀ hx₁ hy  ?_
  have H : sqrt ((1 - x) ^ 2 + y ^ 2)  1 - x / 2
  · calc sqrt ((1 - x) ^ 2 + y ^ 2)
      _  sqrt ((1 - x) ^ 2 + (s * x) ^ 2) := sqrt_le_sqrt <| by rw [ _root_.sq_abs y]; gcongr
      _ = sqrt (1 - 2 * x + (1 + s ^ 2) * x * x) := by congr 1; ring
      _  sqrt (1 - 2 * x + (1 + s ^ 2) * (1 / (1 + s ^ 2)) * x) := sqrt_le_sqrt <| by gcongr
      _ = sqrt (1 - x) := by congr 1; field_simp; ring
      _  1 - x / 2 := by
        simp_rw [sub_eq_add_neg,  neg_div]
        refine sqrt_one_add_le <| neg_le_neg_iff.mpr (hx₁.trans_le ?_).le
        rw [div_le_one (by positivity)]
        exact le_add_of_nonneg_right <| sq_nonneg s
  calc sqrt (x ^ 2 + y ^ 2)
    _  sqrt (x ^ 2 + (s * x) ^ 2) := sqrt_le_sqrt <| by rw [ _root_.sq_abs y]; gcongr
    _ = sqrt ((1 + s ^ 2) * x ^ 2) := by congr; ring
    _ = sqrt (1 + s ^ 2) * x := by rw [sqrt_mul' _ (sq_nonneg x), sqrt_sq hx₀.le]
    _ = 2 * sqrt (1 + s ^ 2) * (x / 2) := by ring
    _ < (2 * sqrt (1 + s ^ 2) + 1) * (x / 2) := by gcongr; exact lt_add_one _
    _  _ := by gcongr; exact le_sub_comm.mpr H

namespace Complex

-- This seems to be missing in Mathlib.
lemma abs_eq_sqrt_sq_add_sq (z : ) : abs z = Real.sqrt (z.re ^ 2 + z.im ^ 2) := by
  rw [abs_apply, normSq_apply, sq, sq]

/-- The Stolz set for a given `M`, roughly teardrop-shaped with the tip at 1 but tending to the
open unit disc as `M` tends to infinity. -/
def stolzSet (M : ) : Set  := {z | z < 1  1 - z < M * (1 - z)}

-- /-- The cone around 1 with angle `2θ`. -/
-- def stolzCone (θ : ℝ) : Set ℂ := {z | ‖1 - z‖ ≠ 0 ∧ |arg (1 - z)| < θ}

/-- The cone to the left of `1` with angle `2 θ` such that `tan θ = s`. -/
def stolzCone' (s : ) : Set  := {z | |z.im| < s * (1 - z.re)}

lemma stolzCone'_subset_StolzSet_aux' {s : } (hs : 0 < s) :
     M ε, 0 < M  0 < ε  {z :  | 1 - ε < z.re}  stolzCone' s  stolzSet M := by
  peel stolzCone'_subset_StolzSet_aux s with M ε hM  H
  rintro z hzl, hzr
  rw [Set.mem_setOf_eq, sub_lt_comm,  one_re,  sub_re] at hzl
  rw [stolzCone', Set.mem_setOf_eq,  one_re,  sub_re] at hzr
  replace H :=
    H (1 - z).re z.im ((mul_pos_iff_of_pos_left hs).mp <| (abs_nonneg z.im).trans_lt hzr) hzl hzr
  have h : z.im ^ 2 = (1 - z).im ^ 2
  · simp only [sub_im, one_im, zero_sub, even_two, neg_sq]
  rw [h,  abs_eq_sqrt_sq_add_sq,  norm_eq_abs,  h, sub_re, one_re, sub_sub_cancel,
     abs_eq_sqrt_sq_add_sq,  norm_eq_abs] at H
  exact sub_pos.mp <| (mul_pos_iff_of_pos_left hM).mp <| (norm_nonneg _).trans_lt H, H

open Topology

lemma  nhdsWithin_stolzCone'_le_nhdsWithin_stolzSet {s : } (hs : 0 < s) :
     M, 𝓝[stolzCone' s] 1  𝓝[stolzSet M] 1 := by
  obtain M, ε, _, , H := stolzCone'_subset_StolzSet_aux' hs
  use M
  rw [nhdsWithin_le_iff, mem_nhdsWithin]
  refine ⟨{w | 1 - ε < w.re}, isOpen_lt continuous_const continuous_re, ?_, H
  simp only [Set.mem_setOf_eq, one_re, sub_lt_self_iff, ]

Michael Stoll (Feb 19 2024 at 09:18):

@Jeremy Tan :up:

Jeremy Tan (Feb 26 2024 at 05:37):

@Michael Stoll done!

Jeremy Tan (Feb 28 2024 at 02:19):

Is there anyone else available to review #10000?

Jeremy Tan (Mar 02 2024 at 07:27):

Joseph Myers said:

The Taylor series for arctan is hidden inside the proof; we don't have it as a separate theorem. (I've previously suggested that the Leibniz formula for pi should be deduced by combining the Taylor series for arctan with Abel's theorem, neither of which we currently have as a separate theorem in mathlib.)

#11098

Jeremy Tan (Mar 03 2024 at 18:44):

And #11098 is merged. I have now merged everything I initially wanted to merge


Last updated: May 02 2025 at 03:31 UTC