Zulip Chat Archive
Stream: general
Topic: finite sum puzzle
Kevin Buzzard (Mar 29 2018 at 23:53):
import tactic.ring theorem finset_sum_is_list_sum (f : ℕ → ℕ) (n : ℕ) : (finset.range n).sum f = ((list.range n).map f).sum := sorry
Kevin Buzzard (Mar 29 2018 at 23:53):
I've been thinking a lot about induction today.
Kevin Buzzard (Mar 29 2018 at 23:54):
@Mario Carneiro Is there some super-cute way of doing this already?
Kevin Buzzard (Mar 29 2018 at 23:55):
I have been trying to formalise quite an abstract approach to questions like these but for all I know this sort of thing is completely well-known. Note that a mathematician would say this proof was trivial and indeed it would be hard to explain to a mathematician why this needed a proof.
Mario Carneiro (Mar 29 2018 at 23:56):
It should be by definition, more or less
Mario Carneiro (Mar 29 2018 at 23:57):
does rfl
work?
Mario Carneiro (Mar 29 2018 at 23:58):
Also, of course that needs a proof, stop thinking that proofs that are simple by induction are trivial enough to not need a proof
Ching-Tsun Chou (Mar 29 2018 at 23:58):
Don't you need the commutativity of "+" on natural numbers?
Mario Carneiro (Mar 29 2018 at 23:58):
that's on the same lines as saying commutativity of natural numbers is trivial
Mario Carneiro (Mar 30 2018 at 00:03):
Ah, it's not quite by definition, because multiset prod is not defined in terms of list prod but instead is defined using foldl. This works:
theorem finset_sum_is_list_sum (f : ℕ → ℕ) (n : ℕ) : (finset.range n).sum f = ((list.range n).map f).sum := multiset.coe_sum _
Mario Carneiro (Mar 30 2018 at 00:05):
Here's a slightly less magical way to write it:
theorem finset_sum_is_list_sum (f : ℕ → ℕ) (n : ℕ) : (finset.range n).sum f = ((list.range n).map f).sum := show ((list.range n).map f : multiset ℕ).sum = ((list.range n).map f).sum, from multiset.coe_sum _
Kevin Buzzard (Mar 30 2018 at 00:06):
f
can map to an add_comm_monoid
Mario Carneiro (Mar 30 2018 at 00:07):
sure
Kevin Buzzard (Mar 30 2018 at 00:07):
that's what you need, I believe.
Mario Carneiro (Mar 30 2018 at 00:07):
the theorem you stated is not maximally general
Kevin Buzzard (Mar 30 2018 at 00:07):
So this one seems genuinely easier than Chris' problem?
Mario Carneiro (Mar 30 2018 at 00:08):
yes, because finset sum is defined as a multiset sum over the map
Kevin Buzzard (Mar 30 2018 at 00:15):
Here's three more trivial statements:
Kevin Buzzard (Mar 30 2018 at 00:15):
import tactic.ring universe u open nat theorem list_range_map_sum_induction {X : Type u} [has_add X] [has_zero X] {n : ℕ} (f : ℕ → X) : ((list.range (succ n)).map f).sum = ((list.range n).map f).sum + f n := sorry theorem finset_range_sum_induction {R : Type u} [add_comm_monoid R] {f : ℕ → R} {d : ℕ} : (finset.range (succ d)).sum f = (finset.range d).sum f + f d := sorry theorem finset_univ_sum_fin_induction {R : Type u} [add_comm_monoid R] {d : ℕ} {f : fin (nat.succ d) → R} : finset.univ.sum f = finset.univ.sum (λ i : fin d, f ⟨i.val,lt_trans i.is_lt $ nat.lt_succ_self d⟩) -- d or _? + f ⟨d,nat.lt_succ_self _⟩ -- is _ or d better style at the end? := sorry
Kevin Buzzard (Mar 30 2018 at 00:16):
They all say the same trivial thing in maths
Kevin Buzzard (Mar 30 2018 at 00:16):
so I am really interested in the slickest possible proofs in Lean
Kevin Buzzard (Mar 30 2018 at 00:17):
because I suspect that occasionally my students will want statements like this to just go away
Kevin Buzzard (Mar 30 2018 at 00:18):
If these get unsorried then we get the following proof of Chris' problem from yesterday:
Kevin Buzzard (Mar 30 2018 at 00:19):
theorem chris_example (n : ℕ) (f : ℕ → ℕ) (g : fin n → ℕ) (h : ∀ i : fin n, f i.1 = g i) : (finset.range n).sum f = finset.univ.sum g := begin induction n with d Hd, refl, -- base case trivial -- for the inductive step it's handy to have notation for the restriction of g, let gres : fin d → ℕ := λ i,g ⟨i.val,lt_trans i.is_lt $ nat.lt_succ_self d⟩, -- goal now of form "first kind of sum to succ d equals second kind" rw finset_range_sum_induction, rw finset_univ_sum_fin_induction, -- goal now "first sum to d + f d = second sum to d + g d" rw [(Hd gres (λ i, h ⟨i.val,_⟩))], -- first sum equals second sum rw h ⟨d,_⟩, -- f d = g d -- so done end
Kevin Buzzard (Mar 30 2018 at 00:19):
with not a pmap
in sight
Kevin Buzzard (Mar 30 2018 at 00:21):
I would argue that this was a "natural" proof which hides away the abstraction.
Kevin Buzzard (Mar 30 2018 at 00:23):
@Chris Hughes Here's what another proof of your fin n question might look like.
Mario Carneiro (Mar 30 2018 at 00:23):
By the way, here's a(nother) proof of chris's theorem:
example (n : ℕ) (f : ℕ → ℕ) (g : fin n → ℕ) (h : ∀ i : fin n, f i.1 = g i) : (finset.range n).sum f = finset.univ.sum g := show ((list.range n).map f : multiset ℕ).sum = (((list.range n).pmap fin.mk _).map g : multiset ℕ).sum, by rw [multiset.coe_sum, multiset.coe_sum, ← (funext h : _ = g), list.map_pmap, ← list.pmap_eq_map]
Kevin Buzzard (Mar 30 2018 at 00:26):
What I don't like about your proofs is that they seem (to me) to involve knowing about some internal implementation of things. My proof is implementation-free. The library maintainer just creates those blah_sum_induction
proofs (the three things sorried above) , and then the end user can construct proofs of Chris' theorem without having to worry about any other implementation.
Kevin Buzzard (Mar 30 2018 at 00:26):
Each of the induction laws gives rise to an abstraction which looks like this:
Mario Carneiro (Mar 30 2018 at 00:26):
I agree, chris has found a hole in the mathlib coverage here
Mario Carneiro (Mar 30 2018 at 00:27):
Your second theorem is provable by simp
, but list.range
doesn't break up nicely because the numbers are listed in increasing order
Mario Carneiro (Mar 30 2018 at 00:29):
I'm not a big fan of your statement of finset_univ_sum_fin_induction
, it's all too complicated in the theorem statement
Kevin Buzzard (Mar 30 2018 at 00:29):
@[reducible] definition sum_map_range {R : Type u} [add_comm_monoid R] (addend : ℕ → R) : ℕ → R | zero := (0 : R) | (succ n) := sum_map_range n + addend n theorem list_range_map_sum_abstraction {R : Type} [add_comm_monoid R] (f : ℕ → R) (n : ℕ) : ((list.range n).map f).sum = sum_map_range f n := sorry theorem finset_range_sum_abstraction {R : Type u} [add_comm_monoid R] (f : ℕ → R) (n : ℕ) : (finset.range n).sum f = sum_map_range f n := sorry theorem finset_univ_sum_fin_abstraction {R : Type u} [add_comm_monoid R] (f : ℕ → R) (n : ℕ) : finset.univ.sum (λ i : fin n, f(i.val)) = sum_map_range f n := sorry
Kevin Buzzard (Mar 30 2018 at 00:29):
yes, the fin one stinks.
Kevin Buzzard (Mar 30 2018 at 00:30):
If you use lists or finsets then f is a function on N, but to do Chris' problem you had to use a function on fin n
Mario Carneiro (Mar 30 2018 at 00:30):
I think there are functions for raising fin n
to fin (n+1)
. Alternatively, you could use fin2
, which has a natural inductive construction instead of being a subtype of nat
Mario Carneiro (Mar 30 2018 at 00:31):
fin2
is not really developed much, but it is defined in dioph.lean
Kevin Buzzard (Mar 30 2018 at 00:32):
I specifically wanted to design functions which gave me the biggest chance of being covered for all variants of the following question:"this Lean statement is trivially true in maths because it says , so how do you prove it in Lean?"
Kevin Buzzard (Mar 30 2018 at 00:33):
I feel like I have convinced myself that for any way of representing the set in Lean
Kevin Buzzard (Mar 30 2018 at 00:33):
there is an induction principle and an abstraction principle.
Kevin Buzzard (Mar 30 2018 at 00:33):
Do I have the right names for these things?
Kevin Buzzard (Mar 30 2018 at 00:35):
As you can see, the examples I have attempted to work out are list.range n
, fin n
and finset.range n
Kevin Buzzard (Mar 30 2018 at 00:36):
Sounds like I need to do fin2 n
Kevin Buzzard (Mar 30 2018 at 00:36):
I should say that I have not proved the induction hypotheses in all cases yet.
Mario Carneiro (Mar 30 2018 at 00:36):
It's mostly used for technical reasons; you can also define recursion principles on fin n
with the same structure as fin2
Mario Carneiro (Mar 30 2018 at 00:37):
but I think that fz
and fs
are the right way to think about induction on fin n
Kevin Buzzard (Mar 30 2018 at 00:37):
But as a mathematician I would find a proof of these sorts of thing rather distasteful (they're all "obvious by induction")
Kevin Buzzard (Mar 30 2018 at 00:38):
so I feel like they should be hidden from view.
Mario Carneiro (Mar 30 2018 at 00:38):
It's always messy when the function is only partially defined, so that you can't even talk about it out of domain
Mario Carneiro (Mar 30 2018 at 00:38):
that's what makes list.pmap
necessary, and also what makes it a pain to work with
Mario Carneiro (Mar 30 2018 at 00:39):
What makes Chris's problem hard is the usage of g : fin n -> N
Mario Carneiro (Mar 30 2018 at 00:39):
same for your induction principle, fin (succ n) -> N
is even worse
Mario Carneiro (Mar 30 2018 at 00:40):
I get the sense that you want to put everything in the form map_sum_range
of something, but that doesn't work for partial functions
Kevin Buzzard (Mar 30 2018 at 00:45):
Here's a 4th one
Kevin Buzzard (Mar 30 2018 at 00:45):
theorem multiset_sum_map_range_induction {X : Type u} [add_comm_monoid X] {n : ℕ} (f : ℕ → X) : ((multiset.range (succ n)).map f).sum = ((multiset.range n).map f).sum + f n := sorry
Kevin Buzzard (Mar 30 2018 at 00:46):
Corresponding to = multiset.range n
Kevin Buzzard (Mar 30 2018 at 00:46):
Each model gives you a new induction principle
Kevin Buzzard (Mar 30 2018 at 00:47):
which I think mathlib could offer with a standard name
Kevin Buzzard (Mar 30 2018 at 00:48):
There are only a finite number of ways that a mathematician can say this trivial thing, and it would be nice if we could just pull a proof out of a hat for each one.
Kevin Buzzard (Mar 30 2018 at 00:48):
Could I even write a tactic which proves all these things? Just in some stupid way -- it just tries all the proofs and chooses the one that works.
Kevin Buzzard (Mar 30 2018 at 00:49):
aah, simp
works on that one.
Mario Carneiro (Mar 30 2018 at 00:50):
I support the definition of your map_sum_range
, which I and I think chris called series
; it's on my to do list
Mario Carneiro (Mar 30 2018 at 00:50):
That would make your multiset_sum_map_range_induction
theorem, which I might otherwise call multiset.range_succ_map_sum
, just series_succ
Mario Carneiro (Mar 30 2018 at 00:51):
It's not called induction
because it's not an induction principle
Mario Carneiro (Mar 30 2018 at 00:53):
As I've said, I think it will alleviate many of the issues you are having with these sums. If you urgently need it, why not try writing it yourself? Don't worry about connecting it to finset.range
, just prove everything directly by induction. Then we can relate it to the other ways to talk about finite sums
Kevin Buzzard (Mar 30 2018 at 00:54):
simp
does the multiset and finset variants, but not the list variant.
Kevin Buzzard (Mar 30 2018 at 00:55):
Can you tell me exactly what you are suggesting I prove? I am interested in getting this done ASAP and I have some time now, term finished.
Kevin Buzzard (Mar 30 2018 at 00:56):
I think your sketches above should enable me to prove everything
Kevin Buzzard (Mar 30 2018 at 00:57):
Sorry about the induction name. I thought carefully about the abstract syntax of the names but I seem to have used the wrong term.
Kevin Buzzard (Mar 30 2018 at 01:03):
I love dioph.lean
Kevin Buzzard (Mar 30 2018 at 01:03):
(D∃4 $ D∃5 $ D∃6 $ D∃7 $ D∃8 $ D&7 D* D&7 D- (D&5 D* D&5 D- D.1) D* D&8 D* D&8 D= D.1 D∧ D&4 D* D&4 D- (D&5 D* D&5 D- D.1) D* D&3 D* D&3 D= D.1 D∧ D&2 D* D&2 D- (D&0 D* D&0 D- D.1) D* D&1 D* D&1 D= D.1 D∧
Kevin Buzzard (Mar 30 2018 at 01:03):
who could fail to love that bit
Kevin Buzzard (Mar 30 2018 at 01:04):
whatever is going on in that file
Mario Carneiro (Mar 30 2018 at 01:07):
That's the closest lean comes to a domain specific language right now
Kevin Buzzard (Mar 30 2018 at 01:09):
So there is vector
and vector3
. Is there a vector2
? I can't find it. (fin2
is used to build vector3
)
Kevin Buzzard (Mar 30 2018 at 01:10):
I have a Masters student working on Matiesevich's theorem for their project, so I showed them dioph.lean
. They know no Lean. I'm not sure they found it very helpful.
Mario Carneiro (Mar 30 2018 at 01:11):
There was once a vector2, but I deleted it because it wasn't needed. vector2
was inductively defined by
inductive vector2 (A : Type u) : nat -> Type u | nil : vector2 0 | cons (n) : A -> vector2 n -> vector2 (succ n)
Mario Carneiro (Mar 30 2018 at 01:12):
From the names, you can guess that vector
, vector2
and vector3
are all isomorphic
Mario Carneiro (Mar 30 2018 at 01:13):
I think you can find it in the file history of dioph.lean
Kevin Buzzard (Mar 30 2018 at 01:55):
I can't believe it. I feel like I have learnt something new about induction today, and I have been teaching it for 20 years.
Kenny Lau (Mar 30 2018 at 01:56):
what is it?
Kenny Lau (Mar 30 2018 at 01:56):
well there's always more to learn :P
Kevin Buzzard (Mar 30 2018 at 01:56):
yes but I'm usually trying to look nearer the top
Kevin Buzzard (Mar 30 2018 at 01:59):
There is one abstract principle of induction g := lam n, sum_to_n f
Kevin Buzzard (Mar 30 2018 at 02:00):
which you can prove assuming a hypothesis of the form \forall n, g (succ n) = g n + f n
Kevin Buzzard (Mar 30 2018 at 02:01):
but the problem is that there are several ways to encode sum_to_n
in Lean
Kevin Buzzard (Mar 30 2018 at 02:01):
e.g. the "pure" way via an inductive type
Kevin Buzzard (Mar 30 2018 at 02:02):
or ways which create an auxiliary type along the way, like sum_to_n f = ((list.range n).map f).sum
which at some point builds a list and then sums over it
Kevin Buzzard (Mar 30 2018 at 02:06):
and for each of these design decisions about how you're going to sum this series (e.g. a design decision that some other program has forced upon you) you are given a definition of sum_to_n : (ℕ → R) → (ℕ → R)
Kevin Buzzard (Mar 30 2018 at 02:07):
(e.g. λ f, λ n ((list.range n).map f).sum
)
Kevin Buzzard (Mar 30 2018 at 02:08):
it's now your job to prove ∀ n, sum_to_n (succ n) = sum_to_n n + f n
Kevin Buzzard (Mar 30 2018 at 02:12):
And that's quite annoying because list doesn't deconstruct like that.
Kenny Lau (Mar 30 2018 at 02:24):
@Kevin Buzzard do you have a list of goals?
Kevin Buzzard (Mar 30 2018 at 02:25):
I'm writing a blog post about it. This has been a most enjoyable day.
Kenny Lau (Mar 30 2018 at 02:34):
it's now your job to prove
∀ n, sum_to_n (succ n) = sum_to_n n + f n
done
Kenny Lau (Mar 30 2018 at 02:34):
theorem sum_to_n.succ : sum_to_n (n+1) = sum_to_n n + f n := begin dsimp [sum_to_n], rw [list.range_concat], rw [list.map_append], rw [list.sum_append], rw [list.map_singleton], rw [list.sum_cons], rw [list.sum_nil], rw [add_zero] end
Kenny Lau (Mar 30 2018 at 02:42):
version 2
Kenny Lau (Mar 30 2018 at 02:42):
theorem sum_to_n.succ : sum_to_n (n+1) = sum_to_n n + f n := by simp [sum_to_n, list.range_concat]
Kevin Buzzard (Mar 30 2018 at 05:24):
https://wordpress.com/post/xenaproject.wordpress.com/1344
Kenny Lau (Mar 30 2018 at 05:29):
@Kevin Buzzard did you sleep?
Kevin Buzzard (Mar 30 2018 at 05:30):
Meh https://xenaproject.wordpress.com/2018/03/30/proofs-by-induction/ is better
Kevin Buzzard (Mar 30 2018 at 05:30):
@Chris Hughes or @Kenny Lau Feel free to leave comments if you have definitions for sum_to_n
Andrew Ashworth (Mar 30 2018 at 05:30):
but, unfortunately, because I fear that in practice people really might occasionally find themselves in a situation where they need a new kind of proof by induction
writing new recursion principles is common and expected, from what i've seen out there
Kenny Lau (Mar 30 2018 at 05:32):
“using the fucking ring tactic”
Andrew Ashworth (Mar 30 2018 at 05:34):
you can see some of this in mathlib, where new elimination lemmas are defined fairly frequently
Andrew Ashworth (Mar 30 2018 at 05:34):
if we go back to the nats, strong induction is commonplace yet requires a proof
Kevin Buzzard (Mar 30 2018 at 05:37):
In lean web editor link, tactic.ring
is not available so I had to prove it the old skool way! Thank you @Mario Carneiro for tactic.ring
. No, it appears I didn't go to sleep and now the sun is up. Crap. I'm behaving like a kid.
Kenny Lau (Mar 30 2018 at 05:38):
lol
Kenny Lau (Mar 30 2018 at 05:38):
i am shocked
Kevin Buzzard (Mar 30 2018 at 05:38):
writing new recursion principles is common and expected, from what i've seen out there
I realised that Chris could solve his problem with good recursion principles.
Kevin Buzzard (Mar 30 2018 at 05:39):
I don't have to be up at 7am though, because the kids have finished school now.
Kenny Lau (Mar 30 2018 at 05:39):
so the philosophy is to always write eliminators for the things you create?
Kevin Buzzard (Mar 30 2018 at 05:39):
If I don't get my act together I'll be up at 7am anyway.
Kevin Buzzard (Mar 30 2018 at 05:39):
I guess that might be what I am saying.
Andrew Ashworth (Mar 30 2018 at 05:39):
also as a matter of style, you could give names to your function variables as opposed to always lambda-ing them
Kevin Buzzard (Mar 30 2018 at 05:39):
Or some version of this.
Kenny Lau (Mar 30 2018 at 05:39):
i am still shocked :stuck_out_tongue:
Andrew Ashworth (Mar 30 2018 at 05:40):
for example def square : ℕ → ℕ := λ i, i ^ 2
could be def square n : nat := n ^ 2
Kenny Lau (Mar 30 2018 at 05:40):
fin has no eliminator though
Kenny Lau (Mar 30 2018 at 05:40):
hence the problem
Kevin Buzzard (Mar 30 2018 at 05:40):
yes, my approach with fin was not much fun.
Kevin Buzzard (Mar 30 2018 at 05:41):
But I need to sleep.
Kenny Lau (Mar 30 2018 at 05:41):
@Andrew Ashworth I think he decided he does not care about styles
Kevin Buzzard (Mar 30 2018 at 05:41):
I just wrote it in the way that appealed to me most.
Kevin Buzzard (Mar 30 2018 at 05:41):
I have also written three solutions to the problems.
Andrew Ashworth (Mar 30 2018 at 05:41):
when you're a professor, you can write your homework exercises however you like, haha
Kevin Buzzard (Mar 30 2018 at 05:42):
One of them is just "axiom axiom constant" etc etc, and it's very cool, you can still do the last part :-)
Kenny Lau (Mar 30 2018 at 05:42):
constant name : false
Kenny Lau (Mar 30 2018 at 05:42):
theorem RH : sorry := false.elim name
Kevin Buzzard (Mar 30 2018 at 05:42):
I might be a professor but I am still very much a learner at this game. I'd be happy for any more stylistic comments.
Andrew Ashworth (Mar 30 2018 at 05:44):
it's only really meaningful in large, complicated definitions
Andrew Ashworth (Mar 30 2018 at 05:44):
of which square does not count
Andrew Ashworth (Mar 30 2018 at 05:44):
i will continue to file nitpicking issues if i see them, though
Andrew Ashworth (Mar 30 2018 at 06:01):
actually later on you give summand a nice name so i take back everything i wrote, haha
Kevin Buzzard (Mar 30 2018 at 06:02):
In fact my initial draft had an error in which I only spotted when I tried to prove that my genuine summing function was equal the one I defined with constants and axioms
Chris Hughes (Mar 30 2018 at 10:08):
@Kevin Buzzard I did define sums and sums between nats here. https://github.com/dorhinj/lean/blob/master/sum_between_nats.lean The proofs aren't mathlib ready and I don't think series is a particularly good name. I proved various basic properties as well.
Last updated: Dec 20 2023 at 11:08 UTC