Zulip Chat Archive

Stream: new members

Topic: Proofs about garbage values?


Adomas Baliuka (Oct 13 2023 at 22:04):

Functions such as log or division are defined in Mathlib on all of the reals and "garbage values" (often zero) are used where the common mathematical definition would not define such functions at all. This leads to a lot of "theorems" which aren't true in mathematics. How does one deal with that?

One way is to accept that Real.log isn't the same as the logarithm function used in mathematics, log 0 = 0 / 0 = 0 really holds and make all theorems as general as possible. Another way is to "pretend" that these functions are the same as in math and try to "never divide by zero". I think the second way is very hard to maintain (tactics would need to know this? Does that then defeat the purpose of formally verifying stuff?) and there are a lot of theorems in mathlib already which use the definitions as they are (e.g. unnecessary differentiability assumptions are omitted).

On the other hand, there seem to be theorems in Mathlib that could be generalized but weren't, perhaps due to not being "true in math".

For example, I'm reasonably sure (my recent question on this didn't receive answers) that Continuous fun x => x * Real.log x. Is that something that could go into Mathlib if proven, or is it a "bad theorem"?

Kevin Buzzard (Oct 13 2023 at 22:12):

You say "I think the second way is very hard to maintain" but 10^6+ lines of mathlib code is evidence against that claim, right?

There are certainly theorems in mathlib like (a+b)/c=a/c+b/c which are true more generally in Lean than in real maths, and your continuity statement is another such example, so I don't see why it couldn't go in mathlib.

Adomas Baliuka (Oct 14 2023 at 09:12):

So which is the way this is dealt with? Is it "neither, there's more nuance than choosing one approach"?

You seem to be implying that there is something about this that needs to be maintained manually (presumably, that theorem statements don't stray too far from what's true in standard math) but my example would be OK despite not being true in normal math? I don't quite understand. Are there "evil theorems" that typecheck but aren't "true" in some sense relevant to Mathlib and would be bad to include (I'm not talking about "theorems that break tactics" or "trivial theorems that just clutter the code")? Is it true to say that a theorem that's already in Mathlib should always be as general as possible (have the weakest possible assumptions)?

Ruben Van de Velde (Oct 14 2023 at 10:02):

Have you read the Xena blog post about division by zero?

Adomas Baliuka (Oct 14 2023 at 10:07):

Ruben Van de Velde said:

Have you read the Xena blog post about division by zero?

Yes, the way I understand it it has the same tension between "mathematicians never divide by zero" (I would think this implies that my continuity statement is "bad") and "division as defined in Lean is not the same as in Math" (which I would think means my continuity statement is OK)

Ruben Van de Velde (Oct 14 2023 at 10:13):

Basically yes, I'd say you want to expect that lean definitions can be somewhat more general than you'd usually make them in mathematics. I'm not sure I'd talk about theorems that are false in mathematics: they're theorems whose statement is meaningless in most "informal" mathematics

Riccardo Brasca (Oct 14 2023 at 10:18):

If you really want you can prove "false" theorems, for example: there exist a rational x such that 1 / x = 2 / x.

Riccardo Brasca (Oct 14 2023 at 10:18):

I mean, theorems that a mathematician would say are false.

Eric Rodriguez (Oct 14 2023 at 10:19):

I think it's actually in the spirit of mathlib to prove statements that are generally true given the garbage value. Often the garbage value is chosen so that some good amount of theorems are true in this case (e.g. why do we have (2 : Nat) - 4 = 0 instead of 37?) and some of the beauty of this is creating really nice api that requires slightly less than usual, as it saves keystrokes, assumptions and pain. (The tsub API is a good example of this).

Eric Rodriguez (Oct 14 2023 at 10:22):

These sometimes orchestrate in a nice way for advanced maths, too; in flt-regular Riccardo has proved a nice formula for the discriminant of Q[zeta_p^n] that unifies all cases (even p^k = 1, 2) whilst the "usual" way the formula is presented is with cases. We've often found that the cardinality of infinite groups being set to zero can be really helpful due to divisibility orderings also having zero as the top element.

Adomas Baliuka (Dec 01 2023 at 23:30):

I proved

lemma continuous_log_mul_rpow {r : } (hr : 0 < r)
: Continuous (fun (x : )  log x * x ^ r)

I'm sure this is about 10x longer and more ugly than what someone familiar with Lean/Mathlib would have written, but here it is:

/-
Copyright (c) 2023 Adomas Baliuka. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Authors: Adomas Baliuka
-/
import Mathlib.Analysis.SpecialFunctions.Pow.Asymptotics
import Mathlib.Analysis.SpecialFunctions.Pow.Continuity

open Classical
open Filter
open Set
open Topology

/-!
# Assymptotics of `x^r * log x` near zero

In Mathlib, the Real natural logarithm is defined as `log |⬝|` (and zero at zero).
Real powers are defined such that
    · For `x > 0`, `x ^ y = exp (y log x)`.
    · For `x < 0`, `x ^ y = exp (y log x) cos (π y)`

Together, this implies that for `r > 0` the funciton `x ↦ x^r * log x` is continuous over ℝ.
For `x > 0`, this is proved in `tendsto_log_mul_rpow_nhds_zero`
(see `Mathlib.Analysis.SpecialFunctions.Pow.Asymptotics`)
This file generalizes to arbitrary `x : ℝ`.

The main result is `continuous_log_mul_rpow`.
Also provided are two generalizations of `tendsto_log_mul_rpow_nhds_zero`:
`tendsto_log_mul_rpow_nhds_zero'` (replaces `𝓝[>]` by `𝓝[≥]`)
`tendsto_log_mul_rpow_nhds_zero''` (replaces `ndhdsWithin` by `nhds`)
Probably only the second, most general should be kept? TODO

## Tags

limit, log, logarithm, xlogx, asymptotics, asymptotic, real, continuous, zero

-/

namespace AssymptoticsXPowlogX

section SetStuff  ------------------------------------------------------------------------

@[simp] lemma intersect_Ici_x_singleton {x : } : Ici (x : )  {x} = {x} := by simp

lemma inters_subset_of_subset {s t U : Set α} (h : s  t)
    : U  s  t := by
    intro x hx
    simp_all only [mem_inter_iff]
    apply h
    simp_all only

lemma Ioi_subset_iff_Iio_subset_neg {b : Set } : Ioi 0  b  Iio 0  -b := by
    have subset_neg (A B : Set ) : A  B  -A  -B := Iff.symm neg_subset_neg
    convert subset_neg ?_ b
    simp

lemma Iio_zero_subset_iff_Ioi_subset_neg {b : Set }
: Iio 0  b  Ioi 0  -b := by
    have subset_neg (A B : Set ) : A  B  -A  -B := Iff.symm neg_subset_neg
    convert subset_neg ?_ b
    simp

end SetStuff

section TendstoHelpers -------------------------------------------------------------------

lemma helper_tendsto_within_ge0 (f :   ) (h: Tendsto f (𝓝[>] 0) (𝓝 0))
: Tendsto f (𝓝[Ici 0  {0}] 0 ) (𝓝 0) := by
    have : Ici (0 : )  {0} = Ioi 0 := by
        ext x
        constructor
        rintro hl, hr
        exact Ne.lt_of_le' hr hl
        intro
        simp_all
        constructor <;> linarith
    rw [this]
    exact h

lemma tendsto_of_union_tendstoWithin' {f :   } {l : Filter } {s1 s2 U : Set } {x : }
    (h_union : s1  s2 = U)
    (hl : Tendsto f (𝓝[s1] x) l)
    (hr : Tendsto f (𝓝[s2] x) l)
    : Tendsto f (𝓝[U] x) l := by
    let S := (s2 \ s1)
    have Ssubs2 : U  S  s2 := by
        have := diff_subset s2 s1
        simp [S]
        exact inters_subset_of_subset this
    have Scomplsubs1 : U  S  s1 := by
        simp only [S] at *
        intro x hx
        rw [h_union] at *
        simp_all only [compl_sdiff, mem_inter_iff, mem_union]
        have hx1 := hx.1
        have hx2 := hx.2
        cases hx1
        · simp_all only
        · apply hx2
          assumption
    have hl : Tendsto f (𝓝[U  S] x) l := by
        apply tendsto_nhdsWithin_mono_left Scomplsubs1 hl
    have hr : Tendsto f (𝓝[U  S] x) l := by
        apply tendsto_nhdsWithin_mono_left Ssubs2 hr
    have := Tendsto.piecewise_nhdsWithin hr hl
    simp at this
    exact this

lemma tendsto_of_union_tendstoWithin {f :   } {l : Filter } {s1 s2 : Set } {x : }
    (h_union : s1  s2 = univ)
    (hl : Tendsto f (𝓝[s1] x) l)
    (hr : Tendsto f (𝓝[s2] x) l)
    : Tendsto f (𝓝 x) l := by
    convert tendsto_of_union_tendstoWithin' h_union hl hr
    exact (nhdsWithin_univ x).symm

lemma union_Ici_Iic_univ {x : } : Ici x  Iic x = univ := by
    ext a
    simp only [mem_union, mem_Ici, mem_Iic, mem_univ, iff_true]
    exact le_total x a

lemma tendsto_ge_of_tendsto_gt_funvalue {f :   }
    (tenF : Tendsto f (𝓝[>] 0) (𝓝 0))
    (tenG : f 0 = 0)
: Tendsto f (𝓝[] 0) (𝓝 0) := by
    have h1 := helper_tendsto_within_ge0 f tenF
    have h2 : Tendsto f (𝓝[Ici 0  {0}] 0) (𝓝 0) := by
        simp
        exact EventuallyEq.tendsto tenG
    have := Tendsto.piecewise_nhdsWithin h2 h1
    simp at this
    exact this

lemma tendsto_zero_of_abs_tendsto_zero {f :   } {l : Filter }
    (tenF : Tendsto (fun x  |f x|) l (𝓝 0))
    : Tendsto f l (𝓝 0) := by
    exact (tendsto_zero_iff_abs_tendsto_zero f).mpr tenF

lemma zero_le_abs_fun (f :   )  :
   0  fun x => |f x| := by
   intro x
   simp

lemma tendsto_zero_of_abs_le_tendsto_zero {g :   } {b : Filter }
    (f :   )
    (gLeF : |g|  f)
    (tenF : Tendsto f b (𝓝 0))
    : Tendsto g b (𝓝 0) := by
    apply tendsto_zero_of_abs_tendsto_zero
    have hg : Tendsto (fun (_ : )  (0 : )) b (𝓝 0) := tendsto_const_nhds
    refine tendsto_of_tendsto_of_tendsto_of_le_of_le hg tenF ?_ gLeF
    refine le_of_sub_nonneg ?tenF.hgf.a
    have : ((fun x => |g x|) - fun x => 0) = (fun x => |g x|) := by
        ext
        simp
    rw [this]
    exact zero_le_abs_fun fun x => g x

lemma preim_neg_nhdsWithin_zero {U : Set }
    : Neg.neg ⁻¹' U  𝓝[>] 0  U  𝓝[<] 0 := by
        simp_all
        constructor <;> intro h
        · simp [nhdsWithin] at *
          simp [Inf.inf] at *
          rcases h with a, ha, b, hb1, hb2⟩⟩⟩
          use -a
          constructor
          · exact neg_mem_nhds_zero  ha
          · use -b
            constructor
            · exact (Ioi_subset_iff_Iio_subset_neg).mp hb1
            · exact neg_eq_iff_eq_neg.mp hb2
        · simp [nhdsWithin] at *
          simp [Inf.inf] at *
          rcases h with a, ha, b, hb1, hb2⟩⟩⟩
          use -a
          constructor
          · exact neg_mem_nhds_zero  ha
          · use -b
            constructor
            · exact (Iio_zero_subset_iff_Ioi_subset_neg).mp hb1
            · simp_all only [inter_neg]

lemma tendsto_nhdsWithin_of_tendsto_nhdsWithin_neg
   {f :   }
   {l : Filter }
   : Tendsto f (𝓝[<] 0) l
    Tendsto (fun x  f (-x)) (𝓝[>] 0) l := by
    have : (fun (x : )  f (-x)) = f  Neg.neg := by ext; simp
    rw [this]
    constructor <;> intro h
    · apply Tendsto.comp h
      convert tendsto_neg_nhdsWithin_Ioi
      simp; simp; exact TopologicalRing.toContinuousNeg
    · intro U hU
      simp only [Tendsto, LE.le] at h
      simp_all
      have := h hU
      have preim_comp : f  Neg.neg ⁻¹' U = Neg.neg ⁻¹' (f ⁻¹' U) := by rfl
      rw [preim_comp] at this
      exact preim_neg_nhdsWithin_zero.mp this

lemma zero_mem_iff_zero_mem_neg (U : Set )
    : 0  U  0  -U := by
    simp_all only [Set.mem_neg, neg_zero]

end TendstoHelpers

section XLogX --------------------------------------------------------------------------

open Real

lemma tendsto_rpow_neg {r : } (hr : 0 < r)
 : Tendsto (fun x => log x * (-x) ^ r) (𝓝[>] 0) (𝓝 0) := by
    apply tendsto_zero_of_abs_tendsto_zero
    simp_rw [abs_mul]
    apply tendsto_zero_of_abs_le_tendsto_zero (fun x  |log x| * |x| ^ r)
    intro x
    simp
    have : abs (fun x => |log x| * |(-x) ^ r|) x = |log x| * |(-x) ^ r| := by
        have abs_fun_applied (f:   ) (x : ) : abs f x = |f x| := rfl
        rw [abs_fun_applied (fun x => |log x| * |(-x) ^ r|) x]
        simp
        rw [abs_mul]
        exact abs_nonneg (log x * (-x) ^ r)
    rw [this]
    · gcongr
      rw [show |x| = |-x| by simp]
      apply Real.abs_rpow_le_abs_rpow
    · convert (tendsto_log_mul_rpow_nhds_zero hr).norm.congr' ?_
      · simp
      · filter_upwards [self_mem_nhdsWithin] with x hx
        simp_rw [norm_mul, norm_rpow_of_nonneg (mem_Ioi.mp hx).le, norm_eq_abs]

/-- This is a direct generalization of `tendsto_log_mul_rpow_nhds_zero`
(replaces `>` by `≥`) -/
lemma tendsto_log_mul_rpow_nhds_zero' {r : } (hr : 0 < r) :
    Tendsto (fun x => log x * x ^ r) (𝓝[] 0) (𝓝 0) := by
        apply tendsto_ge_of_tendsto_gt_funvalue
        apply tendsto_log_mul_rpow_nhds_zero
        exact hr
        norm_num

/-- This is a generalization of `tendsto_log_mul_rpow_nhds_zero`
(replaces `ndhdsWithin` by `nhds`) -/
lemma tendsto_log_mul_rpow_nhds_zero'' {r : } (hr : 0 < r) :
    Tendsto (fun x => log x * x ^ r) (𝓝 0) (𝓝 0) := by
        have union_tozero_fromzero : Iio (0 : )  Ici 0 = univ := by simp
        refine tendsto_of_union_tendstoWithin union_tozero_fromzero ?_ ?_
        · apply tendsto_nhdsWithin_of_tendsto_nhdsWithin_neg.mpr
          convert tendsto_rpow_neg hr using 1
          simp
        exact tendsto_log_mul_rpow_nhds_zero' hr

lemma continuousAt_zero_log_mul_rpow {r : } (hr : 0 < r)
: ContinuousAt (fun (x : )  log x * x ^ r) 0 := by
    apply continuousAt_of_tendsto_nhds (y:=0)
    exact tendsto_log_mul_rpow_nhds_zero'' hr

/--
In Mathlib, the Real natural logarithm is defined as `log |⬝|` (and zero at zero).
Real powers are defined such that
    · For `x > 0`, `x ^ y = exp (y log x)`.
    · For `x < 0`, `x ^ y = exp (y log x) cos (π y)`

Together, this implies that for `r > 0` the funciton `x ↦ x^r * log x` is continuous over ℝ.
-/
lemma continuous_log_mul_rpow {r : } (hr : 0 < r)
: Continuous (fun (x : )  log x * x ^ r) := by
    apply continuous_iff_continuousAt.mpr
    intro x
    by_cases h : x = 0
    · rw [h]
      exact continuousAt_zero_log_mul_rpow hr
    · apply ContinuousAt.mul
      apply ContinuousAt.log
      apply continuousAt_id
      apply h
      refine continuousAt_rpow_const x r ?_
      left; assumption

end XLogX

Yongyi Chen (Dec 02 2023 at 02:12):

Good practice for me! I got it down to 43 lines, not quite the 10x reduction you were looking for but here you go.

import Mathlib.Analysis.SpecialFunctions.Pow.Continuity

open Filter Topology Real

lemma neg_nhds_right_eq_nhds_left : (𝓝[>] (0 : )).map Neg.neg = 𝓝[<] 0 := by
  rw [nhdsWithin, Filter.map_inf neg_injective, map_principal, Set.image_neg_Ioi, neg_zero]
  congr 1
  exact nhds_zero_symm' 

lemma neg_nhds_left_eq_nhds_right : (𝓝[<] (0 : )).map Neg.neg = 𝓝[>] 0 := by
  rw [nhdsWithin, Filter.map_inf neg_injective, map_principal, Set.image_neg_Iio, neg_zero]
  congr 1
  exact nhds_zero_symm' 

lemma tendsto_nhdsWithin_iff_neg {f :   } : (𝓝[<] 0).Tendsto f (𝓝 a)  (𝓝[>] 0).Tendsto (fun x => f (-x)) (𝓝 a) := by
  constructor <;> intro h
  . exact h.comp (le_of_eq neg_nhds_right_eq_nhds_left)
  . suffices Tendsto (f  Neg.neg  Neg.neg) (𝓝[<] 0) (𝓝 a) by
      simpa only [neg_involutive, Function.Involutive.comp_self, Function.comp.right_id] using this
    exact h.comp (le_of_eq neg_nhds_left_eq_nhds_right)

lemma continuous_log_mul_rpow {r : } (hr : 0 < r) : Continuous (fun (x : ) => log x * x ^ r) := by
  rw [continuous_iff_continuousAt]
  intro x
  by_cases h : x = 0
  . subst h
    rw [continuousAt_iff_continuous_left'_right', ContinuousWithinAt, ContinuousWithinAt]
    norm_num
    constructor
    . simp only [tendsto_nhdsWithin_iff_neg, log_neg_eq_log]
      have bound :  x > 0, log x * (-x) ^ r  |log x| * |x ^ r| := by
        intro x xpos
        simp [norm_mul, norm_eq_abs, gt_iff_lt, abs_pos, ne_eq, log_eq_zero, abs_of_pos (rpow_pos_of_pos xpos r)]
        gcongr
        nth_rw 2 [ abs_of_pos xpos]
        rw [ abs_neg x]
        exact abs_rpow_le_abs_rpow _ _
      apply squeeze_zero_norm' (eventually_nhdsWithin_of_forall bound)
      simp only [ norm_eq_abs,  norm_mul,  tendsto_zero_iff_norm_tendsto_zero]
      exact tendsto_log_mul_rpow_nhds_zero hr
    . exact tendsto_log_mul_rpow_nhds_zero hr
  . push_neg at h
    exact Real.continuousAt_log h |>.mul (Real.continuousAt_rpow_const _ _ (Or.inl h))

Jireh Loreaux (Dec 02 2023 at 08:29):

I played with this a bit, but not too much:

import Mathlib.Analysis.SpecialFunctions.Pow.Asymptotics

open Filter Topology Real Asymptotics Bornology

lemma isLittleO_log_rpow_cobounded {r : } (hr : 0 < r) :
    log =o[cobounded ] fun x => x ^ r := by
  rw [Real.cobounded_eq, isLittleO_sup]
  have := calc
    log =o[atTop] (· ^ r) := isLittleO_log_rpow_atTop hr
    _   =ᶠ[atTop] (‖·‖ ^ r) := by
      filter_upwards [Ici_mem_atTop 0] with x (hx : 0  x) using by rw [norm_of_nonneg hx]
  refine ?_, this
  convert this.comp_tendsto tendsto_neg_atBot_atTop using 1
  all_goals apply funext; simp

lemma norm_rpow_le {x y : } : x ^ y  x ^ y := by
  obtain (hx|hx) := lt_or_le x 0
  · rw [rpow_def_of_neg hx, rpow_def_of_pos (norm_pos_iff.mpr hx.ne)]
    simpa only [norm_mul, norm_eq_abs, abs_exp, log_abs, mul_le_iff_le_one_right (exp_pos _)]
      using abs_cos_le_one (y * π)
  · exact (norm_rpow_of_nonneg hx).le

theorem Real.tendsto_log_cobounded : Tendsto log (cobounded ) atTop := by
  rw [Real.cobounded_eq, tendsto_sup]
  refine ?_, tendsto_log_atTop
  convert Real.tendsto_log_atTop.comp tendsto_neg_atBot_atTop
  funext
  simp [Function.comp_apply]

Adomas Baliuka (Dec 02 2023 at 09:52):

@Jireh Loreaux that's not "garbage values" though, is it? I'm a bit surprised (assuming what I just learned about Bornology was right) this wasn't in Mathlib yet. (Also, the Bornology pages would definitely benefit from more docs. A lot of people don't learn about bornology, same as filters. How about adding "used e.g. to talk about limits towards infinity" to cobounded docs?)

Kevin Buzzard (Dec 02 2023 at 09:55):

Please feel free to make docstring PRs! If you see something in mathlib which could be better, you can just make it better and people are very receptive to this approach.

Jireh Loreaux (Dec 02 2023 at 09:57):

You can use it to get the version you want with a bit more work. Note that docs#isLittleO_log_rpow_atTop is the first step toward proving the existing log mul rpow result.

Jireh Loreaux (Dec 02 2023 at 09:59):

You're welcome to PR anything I wrote if you feel like it.


Last updated: Dec 20 2023 at 11:08 UTC