Zulip Chat Archive
Stream: general
Topic: Custom Principle Of Induction
Henning Dieterichs (Jan 05 2021 at 13:09):
I have a tree
and a recursive proposition that no two branches share a leaf (tree_disjoint: tree.disjoint
).
I would like to do an inductive proof, say that the list of all leaves of a tree has no duplicates.
Since I'm doing a lot of such inductive proofs, I want a very concise induction scheme.
I don't like the mess that the tree.disjoint
hypothesis produces though.
(In my actual scenario, I have more tree cases that represent marked edges and about 40 such inductive proofs.)
A standard inductive proof on tree
adds *.disjoint
to all induction hypothesis and it does not unfold tree.disjoint
,
even though I only need the disjoint prop for the branch case. All other cases just unfold and pass the prop as is.
So I was thinking of a custom induction scheme.
I get this "mess" with induction tr
:
case Tree.leaf
α: Type
t: α
tree_disjoint: (Tree.leaf t).disjoint
⊢ C (Tree.leaf t)
case Tree.branch
α: Type
t_tr1t_tr2: Tree α
t_ih_tr1: t_tr1.disjoint → C t_tr1
t_ih_tr2: t_tr2.disjoint → C t_tr2
tree_disjoint: (t_tr1.branch t_tr2).disjoint
⊢ C (t_tr1.branch t_tr2)
But I would like to get this with induction tr using (Tree.disjoint_induction tree_disjoint)
:
case Tree.leaf
α: Type
t: α
⊢ C (Tree.leaf t)
case Tree.branch
α: Type
t_tr1t_tr2: Tree α
t_ih_tr1: C t_tr1 -- No disjoint hypothesis here!
t_ih_tr2: C t_tr2
-- This is the only thing I need from tr.disjoint:
tree_disjoint: disjoint t_tr1.leaves t_tr2.leaves
⊢ C (t_tr1.branch t_tr2)
I played around with a custom induction lemma, but failed. The docs don't say much about custom induction lemmas.
It seems like I cannot even add decidable_eq type classes (I non-deterministically get "induction tactic failed, major premise type is ill-formed").
Also, I don't know how to pass the prop tr.tree
to the induction lemma.
Do you have an idea? Is this even possible with a custom induction lemma?
This is the context:
import tactic
import data.finset
inductive Tree (α: Type)
| leaf (a: α): Tree
| branch (tr1 tr2: Tree) : Tree
variables { α: Type } [decidable_eq α]
def Tree.leaves_list: Tree α → list α
| (Tree.leaf a) := { a }
| (Tree.branch tr1 tr2) := tr1.leaves_list ++ tr2.leaves_list
def Tree.leaves (a: Tree α): finset α := a.leaves_list.to_finset
def Tree.disjoint : Tree α → Prop
| (Tree.leaf leaf) := true
| (Tree.branch tr1 tr2) := tr1.disjoint ∧ tr2.disjoint ∧ disjoint tr1.leaves tr2.leaves
lemma Tree.rec_on2 {C : Tree α → Sort} (n : Tree α)
(leaf: ∀ (a : α), C (Tree.leaf a))
(branch: ∀ (tr1 tr2 : Tree α) (h1: C tr1) (h2: C tr2) (x: disjoint tr1.leaves tr2.leaves), C (tr1.branch tr2)):
C n :=
begin
sorry,
end
lemma Tree.leaves_list.nodup { t: Tree α } (tree_disjoint: t.disjoint):
t.leaves_list.nodup :=
begin
-- This non-deterministically fails with `induction tactic failed, major premise type is ill-formed` in lean 3.21.0
induction t using Tree.rec_on2,
{
sorry,
}, {
sorry,
}
end
Henning Dieterichs (Jan 05 2021 at 13:22):
Why doesn't this work? Tree.rec_on2
should be true.
lemma Tree.rec_on2 { α: Type } [decidable_eq α] {C : Tree α → Sort} (n : Tree α)
(n_disjoint: n.disjoint)
(leaf: ∀ (a : α), C (Tree.leaf a))
(branch: ∀ (tr1 tr2 : Tree α) (h1: C tr1) (h2: C tr2) (x: disjoint tr1.leaves tr2.leaves), C (tr1.branch tr2)):
C n :=
begin
sorry,
end
lemma Tree.leaves_list.nodup { t: Tree α } (tree_disjoint: t.disjoint):
t.leaves_list.nodup :=
begin
induction t using Tree.rec_on2,
{ sorry, },
{ sorry, },
{ sorry, },
end
Yakov Pechersky (Jan 05 2021 at 16:00):
All you need is @[elab_as_eliminator]
tagging Tree.rec_on2
, and then induction t using Tree.rec_on2
works
Mario Carneiro (Jan 05 2021 at 16:06):
I just use refine
instead of induction using
, it's a lot more reliable
refine Tree.rec_on2 t (λ a, _) (λ tr1 tr2 h1 h2 x, _),
Yakov Pechersky (Jan 05 2021 at 16:10):
What would be your proof for Tree.rec_on2
? How are you going to show disjoint tr1.leaves tr2.leaves
?
Eric Wieser (Jan 05 2021 at 16:11):
It seems a shame to have an induction
tactic that intros all the hypotheses for you and applies case names, only to not be able to use it
Eric Wieser (Jan 05 2021 at 16:13):
I ran into a similar problem with https://leanprover.zulipchat.com/#narrow/stream/113488-general/topic/.5Brecursor.5D.20doesn't.20understand.20coe_to_sort/near/217396242
Mario Carneiro (Jan 05 2021 at 16:13):
are you volunteering to fix induction using
in the C++?
Eric Wieser (Jan 05 2021 at 16:15):
Perhaps if I had an understanding of the problem. Does lean4 have the same problem with induction using
, or is there no using
any more?
Mario Carneiro (Jan 05 2021 at 16:15):
I think lean 4 has something fancier, but equivalent
Mario Carneiro (Jan 05 2021 at 16:15):
I would assume it didn't inherit whatever bug this is
Jannis Limperg (Jan 05 2021 at 16:30):
Does with_cases { refine ... }
or with_cases { apply ... }
do anything useful? I think there's some related functionality there.
Mario Carneiro (Jan 05 2021 at 16:31):
It's certainly possible to get equivalent functionality if you use with_cases
, but you would have to manually tag the subgoals
Mario Carneiro (Jan 05 2021 at 16:32):
I just don't find induction using
to be very useful, so I don't really care that it's buggy
Jannis Limperg (Jan 05 2021 at 16:40):
with_cases { apply ... }
picks up the case names if you give them in the induction principle:
@[elab_as_eliminator]
lemma nat_ind {P : ℕ → Prop} (zero : P 0) (successor : ∀ n, P n → P (n + 1)) (n : ℕ) : P n :=
nat.rec zero successor n
example {P : ℕ → Prop} (z : P 0) (s : ∀ n, P n → P (n + 1)) (n : ℕ) : P n :=
begin
with_cases { apply nat_ind },
case zero { exact z},
case successor : m ih { exact s m ih }
end
with_cases { refine ... }
doesn't...
Henning Dieterichs (Jan 05 2021 at 18:35):
I just use refine instead of induction using, it's a lot more reliable
It does not tag the goals though... I like the case ...
statement really much!
Thank you very much for the trick with (λ tr1 tr2 h1 h2 x, _)
. I thought I had to use assume when after refine!
Henning Dieterichs (Jan 05 2021 at 18:36):
What would be your proof for Tree.rec_on2? How are you going to show disjoint tr1.leaves tr2.leaves
The first "case" of the induction proof should imply this!
Henning Dieterichs (Jan 05 2021 at 18:43):
with_cases
is doing something strange here:
lemma Tree.leaves_list.nodup { t: Tree α } (tree_disjoint: t.disjoint):
t.leaves_list.nodup :=
begin
with_cases { refine Tree.rec_on2 t (λ a, _) (λ tr1 tr2 h1 h2 x, _), },
end
Adding (λ a, _) (λ tr1 tr2 h1 h2 x, _),
has no effect!
Last updated: Dec 20 2023 at 11:08 UTC