Zulip Chat Archive

Stream: maths

Topic: Lebesgue-Vitali for Riemann integrability


Xavier Roblot (Apr 24 2024 at 10:39):

We have the fact that a continous function on a rectangular box has a Riemann integral, see MeasureTheory.ContinuousOn.hasBoxIntegral. However, by a theorem of Lebesgue-Vitali, see Wikipedia, to have a Riemann integral, it is enough that is continuous almost everywhere. What would be the easiest way to prove this in Mathlib? The proof on Wikipedia uses oscillation and I do not think we have that.

Xavier Roblot (Apr 24 2024 at 10:43):

PS. I need this result for the connection between points counting in ℤ-lattices and volume: Let s be a bounded, measurable subset of ℝ^d with zero-measure frontier. Let L be a ℤ-lattice in ℝ^d and denote by N(c) denote the number of points in s ∩ c⁻¹ • L, then N(c) / c^d → volume s / covolume L. The above result is the only missing ingredient.

Riccardo Brasca (Apr 29 2024 at 13:38):

Bumping this since we probably really need it for a project in number theory.

Riccardo Brasca (Apr 29 2024 at 13:38):

Maybe @Sébastien Gouëzel knows a strategy?

James Sundstrom (May 01 2024 at 02:35):

Correct me if I'm wrong, but the fact that oscillation isn't in Mathlib doesn't seem like a major impediment. I don't think we need much more about it than this:

import Mathlib.Analysis.BoxIntegral.Basic
import Mathlib.Order.Filter.Basic

open BoxIntegral Topology EMetric

variable {ι : Type u} {E : Type v} [Fintype ι] [NormedAddCommGroup E]

/-- The oscillation of `f` at `x`. -/
noncomputable def oscillation (f : (ι  )  E) (x : ι  ) : ENNReal :=
   S  (𝓝 x).map f, diam S

/-- The oscillation of `f` at `x` is 0 whenever `f` is continuous at `x`. -/
lemma oscillation_zero_of_continuousAt (f : (ι  )  E) (x : ι  ) (hf : ContinuousAt f x) :
    oscillation f x = 0 := by
  apply le_antisymm (ENNReal.le_of_forall_pos_le_add ?_) (zero_le _)
  intro ε  _
  rw [zero_add]
  have : ball (f x) (ε / 2)  (𝓝 x).map f := hf <| ball_mem_nhds _ (by simp [ne_of_gt ])
  apply (biInf_le diam this).trans (le_of_le_of_eq diam_ball ?_)
  apply (ENNReal.mul_div_cancel' ?_ ?_) <;> norm_num

-- Used in the proof of Lebesgue-Vitali
-- See proof at https://en.wikipedia.org/wiki/Riemann_integral#Integrability
def X (f : (ι  )  E) (I : Box ι) (ε : ENNReal) :=
  { x | oscillation f x  ε }  (Box.Icc I)

lemma isCompact_X (f : (ι  )  E) (I : Box ι) (ε : ENNReal) : IsCompact (X f I ε) := by
  apply I.isCompact_Icc.of_isClosed_subset (IsClosed.inter ?_ I.isCompact_Icc.isClosed) (by simp)
  rw [isClosed_iff_clusterPt]
  intro a ha
  rw [Set.mem_setOf_eq]
  apply le_iInf
  intro S
  apply le_iInf
  intro hS
  obtain U, hU, U_open, aU := _root_.mem_nhds_iff.1 hS
  apply le_trans ?_ (diam_mono (Set.image_subset_iff.2 hU))
  rw [clusterPt_iff] at ha
  obtain b, bU, hb := ha (U_open.mem_nhds aU) (Filter.mem_principal_self _)
  apply (Set.mem_setOf.1 hb).trans (biInf_le diam ?_)
  exact (𝓝 b).mem_of_superset (U_open.mem_nhds bU) (Set.subset_preimage_image f U)

Of course, that's not to imply that formalizing the rest of the proof would be easy...

Xavier Roblot (May 01 2024 at 14:30):

Thanks a lot! That could be very useful. At the moment, I am still considering which proof is better suited for formalization. I have found a suspiciously short proof in some French course notes that I am still trying to parse

James Sundstrom (May 17 2024 at 16:29):

Did you still need this? The code could probably use some cleanup, but it works.

import Mathlib.Analysis.BoxIntegral.Basic

open BoxIntegral Topology EMetric BigOperators

variable {ι : Type} [Fintype ι] {E : Type} [NormedAddCommGroup E] [NormedSpace  E] {I J : Box ι}

local notation "ℝⁿ" => ι  

/-- The oscillation of `f` at `x`. -/
noncomputable def oscillation (f : ℝⁿ  E) (x : ℝⁿ) : ENNReal :=
   S  (𝓝 x).map f, diam S

/-- The oscillation of `f` at `x` is 0 whenever `f` is continuous at `x`. -/
theorem oscillation_zero_of_continuousAt {f : ℝⁿ  E} {x : ℝⁿ} (hf : ContinuousAt f x) :
    oscillation f x = 0 := by
  refine le_antisymm (ENNReal.le_of_forall_pos_le_add fun ε  _  ?_) (zero_le _)
  rw [zero_add]
  have : ball (f x) (ε / 2)  (𝓝 x).map f := hf <| ball_mem_nhds _ (by simp [ne_of_gt ])
  refine (biInf_le diam this).trans (le_of_le_of_eq diam_ball ?_)
  refine (ENNReal.mul_div_cancel' ?_ ?_) <;> norm_num

/-- If `oscillation f x < ε` at every `x` in a compact set `K`, then there exists `δ > 0` such
that the oscillation of `f` on `Metric.ball x δ` is less than `ε` for every `x` in `K`. -/
theorem uniform_oscillation_of_compact {K : Set ℝⁿ} (comp : IsCompact K) (f : ℝⁿ  E)
    (ε : ENNReal) (hK :  x  K, oscillation f x < ε) :
     δ > 0,  x  K, diam (f '' (Metric.ball x δ))  ε := by
  let S := fun r  { x : ℝⁿ |  (a : ), (a > r  diam (f '' (Metric.ball x a))  ε) }
  have S_open :  r > 0, IsOpen (S r) := by
    intro r _
    rw [isOpen_iff_nhds]
    rintro x a, ar, ha t ht
    rw [Metric.mem_nhds_iff]
    use (a - r) / 2, by simp [ar]
    intro y hy
    apply ht
    use a - (a - r) / 2, by linarith
    refine le_trans (diam_mono (Set.image_mono fun z hz  ?_)) ha
    rw [Metric.mem_ball] at *
    linarith [dist_triangle z y x]
  have S_cover : K   r > 0, S r := by
    intro x hx
    have : oscillation f x < ε := hK x hx
    simp only [oscillation, Filter.mem_map, iInf_lt_iff] at this
    obtain n, hn₁, hn₂ := this
    obtain r, r0, hr := Metric.mem_nhds_iff.1 hn₁
    use (S (r / 2)), r / 2, by simp [r0]⟩, r, div_two_lt_of_pos r0
    exact le_trans (diam_mono (Set.image_subset_iff.2 hr)) (le_of_lt hn₂)
  have S_antitone :  (r₁ r₂ : ), r₁  r₂  S r₂  S r₁ :=
    fun r₁ r₂ hr x a, ar₂, ha  a, lt_of_le_of_lt hr ar₂, ha
  have :  r > 0, K  S r := by
    obtain T, Tb, Tfin, hT := comp.elim_finite_subcover_image S_open S_cover
    by_cases T_nonempty : T.Nonempty
    · use Tfin.isWF.min T_nonempty, Tb (Tfin.isWF.min_mem T_nonempty)
      intro x hx
      obtain r, hr := Set.mem_iUnion.1 (hT hx)
      simp only [Set.mem_iUnion, exists_prop] at hr
      exact (S_antitone _ r (Set.IsWF.min_le Tfin.isWF T_nonempty hr.1)) hr.2
    · rw [Set.not_nonempty_iff_eq_empty] at T_nonempty
      use 1, one_pos, subset_trans hT (by simp [T_nonempty])
  obtain δ, δ0,  := this
  use δ, δ0
  intro x xK
  obtain a, δa, ha :=  xK
  exact le_trans (diam_mono (Set.image_mono (Metric.ball_subset_ball (le_of_lt δa)))) ha


open MeasureTheory Prepartition Set Classical

/-- A function that is bounded and a.e. continuous on a box `I` is integrable on `I`. -/
theorem integrable_of_bounded_and_ae_continuous (l : IntegrationParams) [CompleteSpace E]
    {I : Box ι} {f : ℝⁿ  E} (hb :  C : ,  x  Box.Icc I, f x  C) (μ : Measure ℝⁿ)
    [IsLocallyFiniteMeasure μ] (hc : ∀ᵐ x μ, ContinuousAt f x) :
    Integrable I l f μ.toBoxAdditive.toSMul := by
  refine' integrable_iff_cauchy_basis.2 fun ε ε0  _
  rcases exists_pos_mul_lt ε0 (2 * μ.toBoxAdditive I) with ε₁, ε₁0, hε₁
  rcases hb with C, hC
  by_cases C0 : C  0; swap
  · obtain x, hx := BoxIntegral.Box.nonempty_coe I
    exact False.elim <| C0 <| le_trans (norm_nonneg (f x)) <| hC x (Box.coe_subset_Icc hx)
  rcases exists_pos_mul_lt ε0 (4 * C) with ε₂, ε₂0, hε₂
  have ε₂0': ENNReal.ofReal ε₂  0 := fun h  not_le_of_gt ε₂0 (ENNReal.ofReal_eq_zero.1 h)
  let D := { x  Box.Icc I | ¬ ContinuousAt f x }
  have μD : μ D = 0 := by
    obtain v, v_ae, hv := Filter.eventually_iff_exists_mem.1 hc
    exact eq_of_le_of_not_lt (le_of_le_of_eq (μ.mono <| fun x hx xv  hx.2 (hv x xv))
                                (mem_ae_iff.1 v_ae)) ENNReal.not_lt_zero
  obtain U, UD, Uopen, hU := Set.exists_isOpen_lt_add D (show μ D   by simp [μD]) ε₂0'
  rw [μD, zero_add] at hU
  have comp : IsCompact (Box.Icc I \ U) :=
    I.isCompact_Icc.of_isClosed_subset (I.isCompact_Icc.isClosed.sdiff Uopen) (Set.diff_subset _ U)
  have :  x  (Box.Icc I \ U), oscillation f x < (ENNReal.ofReal ε₁) := by
    intro x hx
    suffices oscillation f x = 0 by rw [this]; exact ENNReal.ofReal_pos.2 ε₁0
    apply oscillation_zero_of_continuousAt
    simpa [D, hx.1] using hx.2  (fun a  UD a)
  obtain r, r0, hr := uniform_oscillation_of_compact comp f (ENNReal.ofReal ε₁) this
  refine' fun _ _  r / 2, half_pos r0⟩, fun _ _ _  rfl, fun c₁ c₂ π₁ π₂ h₁ h₁p h₂ h₂p  _
  simp only [dist_eq_norm, integralSum_sub_partitions _ _ h₁p h₂p, BoxAdditiveMap.toSMul_apply,
     smul_sub]
  set B := (π₁.toPrepartition  π₂.toPrepartition).boxes
  let p : Box ι  Prop := fun J  (J.toSet  U)
  rw [ Finset.sum_sdiff (Finset.filter_subset p B),  add_halves ε]
  have μI_lt_top := lt_of_le_of_lt (μ.mono I.coe_subset_Icc) I.isCompact_Icc.measure_lt_top
  have μJ_ne_top :  J  B, μ J   := fun J hJ  lt_top_iff_ne_top.1 <| lt_of_le_of_lt
                      (μ.mono (Prepartition.le_of_mem' _ J hJ)) μI_lt_top
  have union :  S  B,  J  S, J.toSet  I.toSet :=
    fun S hS  iUnion_subset_iff.2 (fun J  iUnion_subset_iff.2 fun hJ  le_of_mem' _ J (hS hJ))
  apply le_trans (norm_add_le _ _) (add_le_add ?_ ?_)
  · have :  J  B \ B.filter p, μ.toBoxAdditive J 
      (f ((π₁.infPrepartition π₂.toPrepartition).tag J) -
      f ((π₂.infPrepartition π₁.toPrepartition).tag J))  μ.toBoxAdditive J * ε₁ := by
      intro J hJ
      rw [norm_smul, μ.toBoxAdditive_apply, Real.norm_of_nonneg ENNReal.toReal_nonneg]
      refine mul_le_mul_of_nonneg_left ?_ ENNReal.toReal_nonneg
      have :  x  J, x  U := by
        rw [Finset.mem_sdiff, Finset.mem_filter, not_and] at hJ
        simpa only [p, Set.not_subset] using hJ.2 hJ.1
      obtain x, xJ, xnU := this
      have hx : x  Box.Icc I \ U :=
        Box.coe_subset_Icc <| (le_of_mem' _ J (Finset.mem_sdiff.1 hJ).1) xJ, xnU
      have JB : J  B := (Finset.mem_sdiff.1 hJ).1
      have hy : (π₁.infPrepartition π₂.toPrepartition).tag J  Metric.ball x r :=
        Metric.closedBall_subset_ball (div_two_lt_of_pos r0) (Metric.mem_closedBall_comm.1 <|
            h₁.isSubordinate.infPrepartition π₂.toPrepartition J JB (Box.coe_subset_Icc xJ))
      have hz : (π₂.infPrepartition π₁.toPrepartition).tag J  Metric.ball x r := by
        refine Metric.closedBall_subset_ball (div_two_lt_of_pos r0) (Metric.mem_closedBall_comm.1 <|
            h₂.isSubordinate.infPrepartition π₁.toPrepartition J ?_ (Box.coe_subset_Icc xJ))
        rwa [BoxIntegral.TaggedPrepartition.mem_infPrepartition_comm]
      simpa only [edist_le_ofReal (le_of_lt ε₁0), dist_eq_norm, (Finset.mem_sdiff.1 hJ).1] using
        le_trans (edist_le_diam_of_mem (mem_image_of_mem f hy) (mem_image_of_mem f hz)) (hr x hx)
    refine (norm_sum_le _ _).trans <| (Finset.sum_le_sum this).trans ?_
    rw [ Finset.sum_mul]
    trans μ.toBoxAdditive I * ε₁; swap
    · rw [le_div_iff' two_pos,  mul_assoc]
      exact le_of_lt hε₁
    · simp_rw [mul_le_mul_right ε₁0, Measure.toBoxAdditive_apply]
      refine le_trans ?_ <| ENNReal.toReal_mono (lt_top_iff_ne_top.1 μI_lt_top) <| μ.mono <|
          union _ (Finset.sdiff_subset B (B.filter p))
      rw [ ENNReal.toReal_sum,  Finset.tsum_subtype]; swap
      · exact fun J hJ  μJ_ne_top J (Finset.mem_sdiff.1 hJ).1
      apply (ENNReal.toReal_mono <| ne_of_lt <| lt_of_le_of_lt (μ.mono <|
        union _ (Finset.sdiff_subset B (B.filter p))) μI_lt_top) <| le_of_eq (Eq.symm ?_)
      refine measure_biUnion (Finset.countable_toSet _) ?_ (fun J _  J.measurableSet_coe)
      intro J hJ J' hJ' hJJ'
      exact pairwiseDisjoint _ (Finset.mem_sdiff.1 hJ).1 (Finset.mem_sdiff.1 hJ').1 hJJ'
  · have :  J  B.filter p, μ.toBoxAdditive J  (f ((π₁.infPrepartition π₂.toPrepartition).tag J) -
        f ((π₂.infPrepartition π₁.toPrepartition).tag J))  μ.toBoxAdditive J * (2 * C) := by
      intro J _
      rw [norm_smul, μ.toBoxAdditive_apply, Real.norm_of_nonneg ENNReal.toReal_nonneg, two_mul]
      apply mul_le_mul_of_nonneg_left (le_trans (norm_sub_le _ _) (add_le_add ?_ ?_)) (by simp) <;>
        exact hC _ (TaggedPrepartition.tag_mem_Icc _ J)
    apply le_trans (norm_sum_le_of_le _ this)
    simp_rw [ Finset.sum_mul, Measure.toBoxAdditive_apply]
    rw [ ENNReal.toReal_sum (fun J hJ  μJ_ne_top J (B.filter_subset p hJ))]
    have : ( a in B.filter p, μ a).toReal  ε₂ := by
      rw [ ENNReal.toReal_ofReal (le_of_lt ε₂0)]
      refine ENNReal.toReal_mono ENNReal.ofReal_ne_top ( le_of_lt <| lt_of_le_of_lt ?_ hU)
      trans μ ( J  B.filter p, J)
      · apply le_of_eq
        rw [ Finset.tsum_subtype]
        apply (measure_biUnion (B.filter p).countable_toSet ?_ (fun J _  J.measurableSet_coe)).symm
        intro J hJ J' hJ' hJJ'
        exact pairwiseDisjoint _ (B.filter_subset p hJ) (B.filter_subset p hJ') hJJ'
      · apply μ.mono
        simp_rw [iUnion_subset_iff]
        exact fun J hJ  (Finset.mem_filter.1 hJ).2
    apply le_trans <| mul_le_mul_of_nonneg_right this <| (mul_nonneg_iff_of_pos_left two_pos).2 C0
    linarith

Xavier Roblot (May 17 2024 at 16:33):

Yes, I still need this. In fact, I was slowly working on it but I was quite far from it. Thank you very much! Do you have plan to PR that?

James Sundstrom (May 17 2024 at 17:08):

I can give it a shot. I've never actually contributed anything before, so I'll have to figure out what I'm doing there.

Riccardo Brasca (May 17 2024 at 17:10):

Yes yes, PRit!! Don't worry if it's in the final form

Kevin Buzzard (May 17 2024 at 17:11):

@James Sundstrom I sent you an invitation to the github userid in your profile. Maybe just start by adding it to Mathlib.Analysis.BoxIntegral.Basic? You should make a PR from a branch (so clone the project locally, make a branch, push and then PR) rather than a fork. That way we get CI running on it. And I third the opinion that you should PR it :-)

Xavier Roblot (May 17 2024 at 19:38):

In any case, I can help you with the PR if you want

James Sundstrom (May 18 2024 at 22:08):

@Kevin Buzzard @Xavier Roblot Thanks! I made the PR.

Kevin Buzzard (May 18 2024 at 23:45):

If it's ready for review, can you label it awaiting-review @James Sundstrom

James Sundstrom (May 19 2024 at 00:25):

Oh, I stupidly assumed that would be the default state. Thanks again.

Kevin Buzzard (May 19 2024 at 00:33):

OK it's now on the #queue :-)

Xavier Roblot (May 19 2024 at 06:02):

Great! I’ll try to have a look later today


Last updated: May 02 2025 at 03:31 UTC