New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Create choose.lean #48
Conversation
Definition of choose function for naturals and basic lemmas
data/nat/choose.lean
Outdated
|
||
@[simp] | ||
lemma choose_zero_right (n : ℕ) : choose n 0 = 1 := begin | ||
cases n with n,unfold choose,unfold choose, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
here and anywhere else: move the begin
to the next line.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also think the proof would be clearer as by cases n; refl
data/nat/choose.lean
Outdated
lemma choose_succ_succ (n k : ℕ) : choose (succ n) (succ k) = choose n k + choose n (succ k) := rfl | ||
|
||
lemma choose_eq_zero_of_lt {n k : ℕ} : n < k → choose n k = 0 := begin | ||
revert k, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
instead of reverting at the beginning state your theorem different, i.e. {n : ℕ} : \Pi k, n < k → choose n k = 0
data/nat/choose.lean
Outdated
lemma choose_eq_zero_of_lt {n k : ℕ} : n < k → choose n k = 0 := begin | ||
revert k, | ||
induction n with n' hi, | ||
assume k nk,cases k,exact absurd nk dec_trivial, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
avoid writing multiple tactics in one line.
Here you use multiple tactics to proof the first induction step. Please mark it with { ... }
and indent it properly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, space after ,
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This also goes for further proofs.
Dear Chris, this is of course a theory we want to see in |
I'm just curious about your style guidelines. Would |
Hard to say. For longer proofs were it is hard to see what you are refering to |
My view is that Especially since |
Thanks for the responses! It seems that there is a spectrum of opinions. I must say that I lean strongly toward the “use |
Hopefully stylistically better now
data/nat/choose.lean
Outdated
lemma succ_mul_choose_eq (n : ℕ) : ∀ k, succ n * choose n k = choose (succ n) (succ k) * succ k := | ||
begin | ||
induction n with n' hi, | ||
{ assume k, simp, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using simp
in the middle should be avoided. When the set of simp-rules changes (and it changes fast) then the goals afterwards change also. This results in unpredictable goals. Try to do a more structured approach:
- introduce a
have
with some subgoal? - add maybe simp rules to solve the goal in one step
- move the simp further down, in this case distribute it into both cases
data/nat/choose.lean
Outdated
induction n with n' hi, | ||
{ assume k, simp, | ||
cases k with k', | ||
simp, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Put simp
into { simp }
. This is especially important for simp
. Otherwise it might happen that the simp set changes and the next tactics still work. Then it is hard to figure out which simp
call failed.
data/nat/choose.lean
Outdated
begin | ||
induction n with n' hi, | ||
{ assume k hk, | ||
rw [eq_zero_of_le_zero hk,choose_zero_right], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you replace this branch by a simp call a la: simp [nat.le_zero_iff] {contextual := tt}
(maybe with dec_trivial
afterwards)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't actually know what {contextual := tt}
means, so possibly, but I don't really know how.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Play around! With this the simplifier gets very powerful :-)
What does it do: when the simplifier rewrites a term of the form a -> b
, it first rewrites a = a'
, and then when it rewrites b
it adds a'
to its new context. So if a'
is a rewrite rule it can use it for rewriting, if a'
is a proposition it will at least use this proposition to fullfill any conditions on rewrite rules.
Your goal has the form ∀n, 0 ≥ n → choose n k = ...
, now with nat.le_zero_iff
the simplifier will rewrite (0 ≥ n) = (n = 0)
. This is again a nice rewrite rule, so choose n k = ...
is rewritten to choose 0 k = ...
and choose_zero_right
matches!
Hey Chris, some further notes. The changed indentation helped me to see further problems in the tactic scripts. Also I have more stylisic change requests:
I know all these stylistic changes seam to be annoying and bike shedding, but they drastically increase readability. |
slightly simplify proof
Shorten proofs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Further proof cleanup suggestions: choose
is a nice recursively defined function. So lets use the equation compiler to simplify our proofs.
Warning: the induction hypothesis has the name of the lemma you proof. It looks very general, but the equation compiler foes not support more than primitive induction. So avoid giving it directly to the simplifier (no simp [*]
), but only instantiated
data/nat/choose.lean
Outdated
def choose : ℕ → ℕ → ℕ | ||
| _ 0 := 1 | ||
| 0 (succ k) := 0 | ||
| (succ n) (succ k) := choose n k + choose n (succ k) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The equation compiler and the elaborator understand that succ n = n + 1
, so it's nicer to use n + 1
instead of succ
.
data/nat/choose.lean
Outdated
| 0 (succ k) := 0 | ||
| (succ n) (succ k) := choose n k + choose n (succ k) | ||
|
||
@[simp] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@[simp]
is on the same line as the lemma. Change this here and in the following lemmas.
Move the proof to the next line, especially when the line gets longer than 100 characters.
data/nat/choose.lean
Outdated
lemma choose_succ_succ (n k : ℕ) : choose (succ n) (succ k) = choose n k + choose n (succ k) := rfl | ||
|
||
lemma choose_eq_zero_of_lt {n : ℕ} : ∀ {k}, n < k → choose n k = 0 := | ||
begin |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can be written very nice using the equation compiler:
lemma choose_eq_zero_of_lt : ∀ {n k}, n < k → choose n k = 0
| 0 (k+1) hk := by simp
| (n+1) (k+1) hk :=
have hnk : n < k, from lt_of_succ_lt_succ hk,
have hnk1 : n < k + 1, from lt_of_succ_lt hk,
by rw [choose_succ_succ, choose_eq_zero_of_lt hnk, choose_eq_zero_of_lt hnk1]
The equation compiler takes care of the absurd
cases (n < m
is n + 1 ≤ m
which is inductively defined). My personal preference is to state proofs explicitly using have ..
instead of adding the proof to to the rw
list.
data/nat/choose.lean
Outdated
by induction n; simp [*, choose] | ||
|
||
lemma choose_pos {n : ℕ} : ∀ {k}, k ≤ n → 0 < choose n k := | ||
begin |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this may also be a good application for the equation compiler. I guess 3 cases (0 n
, (n+1) 0
, (n+1) (k+1)
) are needed, but they could go through using simp
.
data/nat/choose.lean
Outdated
end | ||
|
||
lemma succ_mul_choose_eq (n : ℕ) : ∀ k, succ n * choose n k = choose (succ n) (succ k) * succ k := | ||
begin |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
try again the equation compiler
data/nat/choose.lean
Outdated
end | ||
|
||
lemma choose_mul_fact_mul_fact {n : ℕ} : ∀ {k}, k ≤ n → choose n k * fact k * fact (n - k) = fact n := | ||
begin |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dito
Used the equation compiler for inductive proofs
data/nat/choose.lean
Outdated
{ rw [hk₁, choose_succ_self, zero_mul, zero_mul, add_zero, fact_succ, mul_comm] } | ||
end | ||
|
||
theorem choose_def_alt {n k : ℕ} (hk : k ≤ n) : choose n k = fact n / (fact k * fact (n - k)) := |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what about calling it: choose_eq_fact_div_fact
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
State in the have
what you get after the rw [..] at this
. And then prove it using the rw
and the choose_mul_fact_mul
. Then you can even replace the begin .. end
part by have _, begin ... end, _
data/nat/choose.lean
Outdated
rw [choose_succ_succ, succ_sub_succ, add_mul, add_mul], | ||
have : choose n k * fact (succ k) * fact (n - k) = choose n k * fact k * fact (n - k) * succ k := | ||
by unfold fact; simp[mul_comm, mul_assoc, mul_left_comm], | ||
rw [this, choose_mul_fact_mul_fact (le_of_succ_le_succ hk)], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
indentation is wrong after this line; remove two spaces from the following tactics
data/nat/choose.lean
Outdated
rw [this, choose_mul_fact_mul_fact (le_of_succ_le_succ hk)], | ||
cases lt_or_eq_of_le hk with hk₁ hk₁, | ||
{ have : n - k = succ (n - succ k) := by rw [←succ_sub (le_of_succ_le_succ hk₁), succ_sub_succ], | ||
rw [this, fact_succ (n - succ k), mul_comm (succ (n - succ k)), ←mul_assoc], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could you sqash the follow rw
into one?
data/nat/choose.lean
Outdated
| (n + 1) 0 hk := by simp | ||
| (n + 1) (succ k) hk := | ||
begin | ||
rw [choose_succ_succ, succ_sub_succ, add_mul, add_mul], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do you first rw
, then show something, then continue rw
, then a case
and then more rw
?
Move the have
to the front. Then make the case distinction. And then do the rw
at once. But take a look at the goal of you rw
, maybe you can do it in one simp
call?
Slightly neater proof of `choose_mul_fact_mul_fact`. The proof involves subtraction of naturals, so cannot easily be done using simp.
deat(data/nat): add choose function to compute the binomial coefficients
Definition of choose function for naturals and basic lemmas