Zulip Chat Archive

Stream: general

Topic: Proving identities from Table of Integrals, Series, and Prod


Congzhou Sha (Sep 19 2022 at 22:19):

This is definitely going to be a massive undertaking, but I've started proving identities from the book by Gradshteyn and Ryzhik (https://github.com/mikesha2/identitylib). So far, a lot of it is algebraic hell...

Moritz Doll (Sep 19 2022 at 22:27):

This is great. One way I tried to convince analysts that formalization is useful (this is quite a hard task) is saying that having formalization of these huge libraries of special functions identities and estimates in a digitalized form would be so much better than what we have now (also considering that these things tend to have mistakes and no proofs). For the really interesting parts I think we still lack the complex analysis tools, but the more fundamental things would be great to have in mathlib.

Eric Wieser (Sep 19 2022 at 22:29):

I'd be very surprised if this lemma about derivatives combining additively is not in mathlib as something like docs#has_deriv_at.add

Congzhou Sha (Sep 19 2022 at 22:46):

It's absolutely in there, and I've been trying to learn more theorems that are already in mathlib, but philosophically, it shouldn't matter, right? As long as I see "goal accomplished"

Scott Morrison (Sep 20 2022 at 02:04):

Here's the argument that it does matter. Beyond just being formally checked, we want mathematical results to be discoverable, usable, and maintainable. All of these problems getting easier the tighter the integration with mathlib. It's very important for usability that libraries can be imported simultaneously with mathlib (duplication is not actually an issue here). I would argue that a repository becomes much more maintainable if it adheres to the same standards (documentation, proof style, lemma naming, etc) as mathlib --- first because there are lots of people familiar with those standards who can contribute, and secondly because it significantly lowers the barrier to porting critical parts of an external library into mathlib if it becomes necessary (either for mathlib's own purposes, or maintenance).

I think the "perfect" approach to formalizing a large set of results (assuming they are significant enough and on topic enough to warrant living in mathlib, but that certainly applies here) is to PR them all directly into mathlib, using doc-strings to label the theorems as coming from some named collection. (e.g. see how we link to the Stacks project in many places).

It is obviously more work, but there are significant payoffs: the discipline of going through the mathlib PR process results in much better code at the end of the day (plus you learn "mathlib style", making it easier to collaborate with others), and of course you get to hope that your contributions are maintained longer, and discovered by more people.

On the other hand, perfect is the enemy of good, and it's completely fine to work off in your own repository, duplication mathlib results as you see fit. :-)

Congzhou Sha (Sep 22 2022 at 15:02):

I'm actually pretty concerned about the transition from Lean 3 to Lean 4 as well (especially for mathlib). Also since this is a hobby project for me, I think that it makes sense for me to work with a stable mathlib build, so for now I think I won't commit directly the mathlib. At some point in the future when I think I have a pretty good grasp on mathlib and I've proven a substantial number of identities, I will work on streamlining my results with theorems from mathlib and submitting PRs, which should be a far easier task than the initial writing of the theorems.

As a small update, it took me about a week to get through the first 4 identities, and I'm hoping my speed will improve as I do more. I think I might need to formalize some umbral calculus to make some of the combinatoric identities easier: image.png

Scott Morrison (Sep 23 2022 at 00:58):

Another take on this is that there is typically a bit of a learning curve in making mathlib PRs, and that you will likely find that you are substantially rewriting material during the PR process. Making a few small PRs early in the process may save effort overall, as you'll be able to do more of your development in mathlib style/quality on the first pass.

Kevin Buzzard (Sep 23 2022 at 10:43):

@Congzhou Sha Here's a golfed proof of the first lemma:

  @[simp] theorem arithmetic_progression_0_111 (n : ) (a r : ) :
     (k : ) in finset.range n, (a + k * r) = n * (a + (n - 1) * r / 2) :=
  begin
    induction n with n ih,
    { simp, },
    { rw [finset.sum_range_succ, ih],
      simp, ring,
    },
  end

You don't need to do all this rw right_distribstuff -- we have tools like ring for that.

A comment on the @[simp] attribute: simp will never fire on a lemma like

@[simp] theorem geometric_progression_0_112 (n : ) (a q : ) (h : q  1) :
     (k : ) in finset.range n, a * q ^ k = a * (q ^ n - 1) / (q - 1) :=

because the simplifier will not be able to find a proof of the hypothesis q \ne 1.

Yaël Dillies (Sep 23 2022 at 10:45):

Technically speaking, simp could prove q ≠ 1, but only in very few cases.

Kevin Buzzard (Sep 23 2022 at 10:47):

  lemma k_squared_geometric_progression_algebra (x : ) (n : ) :
    -x ^ (n + 2) * n * n +
        (2 * x ^ (n + 1) * n * n +
          (-x * x * x * x ^ n * n * n + 3 * x * x * x ^ n * n * n -
              3 * x * x ^ n * n * n)) =
      2 * x ^ (n + 2) * n * n +
        (-x ^ (n + 1) * n * n - x ^ (n + 3) * n * n) :=
  begin
    ring_exp,
  end

Eric Rodriguez (Sep 23 2022 at 10:49):

you can always pass them in, though, Kevin:

import data.real.basic

open_locale big_operators

@[simp] theorem geometric_progression_0_112 (n : ) (a q : ) (h : q  1) :
     (k : ) in finset.range n, a * q ^ k = a * (q ^ n - 1) / (q - 1) := sorry

example (n : ) (a q t : ) :  (k : ) in finset.range n, a * q ^ k = t :=
begin
  simp [show q  1, from sorry],
  -- ⊢ a * (q ^ n - 1) / (q - 1) = t
end

I've definitely seen simp lemmas in this way (and indeed, docs#nat.succ_succ_ne_one is a simp lemma too)

Kevin Buzzard (Sep 23 2022 at 10:50):

  theorem sum_integers_0_121_7 (n : ) :
     (k : ) in finset.range (n + 1), (k : ) ^ 7 = n ^ 2 * (n + 1) ^ 2 * (3 * n ^ 4 + 6 * n ^ 3 - n ^ 2 - 4 * n + 2) / 24 :=
  begin
    induction n with n ih,
    { simp, },
    { rw [finset.sum_range_succ, ih],
      simp, ring,
    },
  end

This pattern can do all this stuff. Did you know that there's a general theorem for d'th powers involving Bernoulli polynomials? We might even have it in mathlib.

Kevin Buzzard (Sep 23 2022 at 10:52):

  theorem convert_proof_real_to_nat (n : ) (f g :   ) :
     (k : ) in finset.range (n + 1), ((f k) : ) = (g n) 
     (k : ) in finset.range (n + 1), f k = g n :=
  begin
    intro h,
    exact_mod_cast h,
  end

Kevin Buzzard (Sep 23 2022 at 10:54):

    @[simp] lemma pure_geometric_progression (n : ) (q : ) (h : q  1) :
     (k : ) in finset.range n, q ^ k = (q ^ n - 1) / (q - 1) :=
  begin
    simpa using geometric_progression_0_112 n 1 q h,
  end

simpa using this is simp at this, exact this

Kevin Buzzard (Sep 23 2022 at 10:59):

    lemma deriv_of_geometric_rv (n : ) (x : ) (h : x  1):
    ((n + 1) * x ^ n * (x - 1) - (x ^ (n + 1) - 1)) / (x - 1) ^ 2 =
     (k : ) in finset.range n, (k + 1) * x ^ k :=
  begin
    exact (deriv_of_geometric n x h).symm,
  end

Dot notation is super-cool! The type of deriv_of_geometric n x h is eq _ _ so (deriv_of_geometric n x h).symm means eq.symm deriv_of_geometric n x h which you can use instead of the symmetry tactic.

Kevin Buzzard (Sep 23 2022 at 11:07):

lemma deriv_of_geometric (n : ) (x : ) (h : x  1):
   (k : ) in finset.range n, (k + 1) * x ^ k =
  ((n + 1) * x ^ n * (x - 1) - (x ^ (n + 1) - 1)) / (x - 1) ^ 2 :=
begin
  -- proof which avoids analysis:
  -- First clear denominators:
  have foo : (x - 1)  0 := by { intro h2, apply h, linarith},
  field_simp [foo],
  -- now just use induction on n
  induction n with d hd,
  -- and it's just algebra
  { simp, },
  { rw [finset.sum_range_succ, add_mul, hd],
    simp, ring_exp, },
end

You can't need analysis to prove this because it's true in any field. The algebra proof is far less painful than the analytic route you took (the algebra route completely involves the long deriv_except_at_one)

Congzhou Sha (Sep 23 2022 at 14:45):

Kevin Buzzard said:

lemma deriv_of_geometric (n : ) (x : ) (h : x  1):
   (k : ) in finset.range n, (k + 1) * x ^ k =
  ((n + 1) * x ^ n * (x - 1) - (x ^ (n + 1) - 1)) / (x - 1) ^ 2 :=
begin
  -- proof which avoids analysis:
  -- First clear denominators:
  have foo : (x - 1)  0 := by { intro h2, apply h, linarith},
  field_simp [foo],
  -- now just use induction on n
  induction n with d hd,
  -- and it's just algebra
  { simp, },
  { rw [finset.sum_range_succ, add_mul, hd],
    simp, ring_exp, },
end

You can't need analysis to prove this because it's true in any field. The algebra proof is far less painful than the analytic route you took (the algebra route completely involves the long deriv_except_at_one)

Yeah, it ‘s interesting how the easiest way to come up with the correct algebraic expression is to do the analysis route by pencil and paper, but then formalizing is easier with induction.

Thanks for all the suggestions, I’ll go through and streamline everything! There were definitely cases where ring wasn’t able to solve the goal (cancelling terms like (1 - x) ^ 2 / (1 - x) ^ 3) and I had to manually cancel some terms, but I see you use ring_exp for some of that

Kevin Buzzard (Sep 23 2022 at 17:15):

You just need to know the tricks (which you learn by trying stuff and then looking at other people's solutions, which is why I showed you some of my solutions). ring can't deal with division because division isn't part of the structure of a ring. But there are tactics like field_simp which are specifically designed for clearing denominators (division is part of the structure of a field).


Last updated: Dec 20 2023 at 11:08 UTC