Zulip Chat Archive
Stream: general
Topic: rfl not working for well founded recursion
Frederick Pu (Dec 15 2024 at 19:52):
does anyone know why rfl fails in this case?
protected inductive ComputedAutoDiffTree.DiffTree (α : Type u) (β : Type v) : List Nat → Type (max u v)
| mk : ComputedAutoDiffTree.DiffTree α β outShape
protected inductive AutoDiffTree.DiffTree (α : Type u) (β : Type v) : List Nat → Type (max u v)
| mk
(outShape : List Nat)
(parents : Array (Σ shape, AutoDiffTree.DiffTree α β shape))
: AutoDiffTree.DiffTree α β outShape
| ofComputed {shape : List Nat}: ComputedAutoDiffTree.DiffTree α β shape → AutoDiffTree.DiffTree α β shape
protected def ComputedAutoDiffTree.DiffTree.valid {α : Type u} {β : Type v} {shape : List Nat} : ComputedAutoDiffTree.DiffTree α β shape → Prop :=
fun x => True
def AutoDiffTree.DiffTree.valid {α : Type u} {β : Type v} {shape : List Nat} : AutoDiffTree.DiffTree α β shape → Prop
| ofComputed computedTree => computedTree.valid
| mk _ parents => ∀ x ∈ parents, x.2.valid
termination_by
t => sizeOf t
decreasing_by
have : sizeOf x < sizeOf parents := Array.sizeOf_lt_of_mem (by assumption)
cases x
simp
simp at this
omega
example {α : Type u} {β : Type v} {shape : List Nat} (x : ComputedAutoDiffTree.DiffTree α β shape) : (AutoDiffTree.DiffTree.ofComputed x).valid = x.valid :=
rfl
/-
type mismatch
rfl
has type
?m.4183 = ?m.4183 : Prop
but is expected to have type
(AutoDiffTree.DiffTree.ofComputed x).valid = x.valid : Prop
-/
François G. Dorais (Dec 15 2024 at 22:23):
Wellfounded definitions are irreducible. Use this to make it work:
unseal AutoDiffTree.DiffTree.valid in
example {α : Type u} {β : Type v} {shape : List Nat} (x : ComputedAutoDiffTree.DiffTree α β shape) : (AutoDiffTree.DiffTree.ofComputed x).valid = x.valid :=
rfl
Frederick Pu (Dec 15 2024 at 22:25):
what does unseal do?
François G. Dorais (Dec 15 2024 at 22:25):
It locally makes the definition semireducible.
Frederick Pu (Dec 15 2024 at 22:26):
so is it like sorry?
François G. Dorais (Dec 15 2024 at 22:26):
No, not at all.
François G. Dorais (Dec 15 2024 at 22:28):
It allows lean to see the definition of AutoDiffTeee.DiffTree.valid
.
Frederick Pu (Dec 15 2024 at 22:30):
still broken for me
unseal AutoDiffTree.DiffTree.valid in
example {α : Type u} {β : Type v} {shape : List Nat} (c : ComputedAutoDiffTree.DiffTree α β shape) : (DiffTree.ofComputed c).valid = c.valid := by
rfl
/-
tactic 'rfl' failed, the left-hand side
(DiffTree.ofComputed c).valid
is not definitionally equal to the right-hand side
-/
François G. Dorais (Dec 15 2024 at 22:46):
Works fine for me.
François G. Dorais (Dec 15 2024 at 22:47):
Frederick Pu (Dec 16 2024 at 00:04):
Frederick Pu (Dec 16 2024 at 00:08):
protected inductive ComputedAutoDiffTree.DiffTree (α : Type u) (β : Type v) : List Nat → Type (max u v)
| mk
(outShape : List Nat)
(parents : Array (Σ shape, ComputedAutoDiffTree.DiffTree α β shape))
: ComputedAutoDiffTree.DiffTree α β outShape
protected inductive AutoDiffTree.DiffTree (α : Type u) (β : Type v) : List Nat → Type (max u v)
| mk
(outShape : List Nat)
(parents : Array (Σ shape, AutoDiffTree.DiffTree α β shape))
: AutoDiffTree.DiffTree α β outShape
| ofComputed {shape : List Nat}: ComputedAutoDiffTree.DiffTree α β shape → AutoDiffTree.DiffTree α β shape
protected def ComputedAutoDiffTree.DiffTree.valid {α : Type u} {β : Type v} {shape : List Nat} : ComputedAutoDiffTree.DiffTree α β shape → Prop :=
fun x => True
def AutoDiffTree.DiffTree.valid {α : Type u} {β : Type v} {shape : List Nat} : AutoDiffTree.DiffTree α β shape → Prop
| ofComputed computedTree => computedTree.valid
| mk _ parents => ∀ x ∈ parents, x.2.valid
termination_by
t => sizeOf t
decreasing_by
have : sizeOf x < sizeOf parents := Array.sizeOf_lt_of_mem (by assumption)
cases x
simp
simp at this
omega
unseal AutoDiffTree.DiffTree.valid in
example {α : Type u} {β : Type v} {shape : List Nat} (x : ComputedAutoDiffTree.DiffTree α β shape) : (AutoDiffTree.DiffTree.ofComputed x).valid = x.valid :=
rfl
/-
type mismatch
rfl
has type
?m.5453 = ?m.5453 : Prop
but is expected to have type
(AutoDiffTree.DiffTree.ofComputed x).valid = x.valid : Prop
-/
François G. Dorais (Dec 16 2024 at 01:00):
Why do you think it should work?
Frederick Pu (Dec 16 2024 at 01:03):
because of the definition of AutoDiffTree.DiffTree.valid
Mario Carneiro (Dec 16 2024 at 01:04):
Note that well founded definitions don't always reduce by rfl. This is the reason they were made irreducible in the first place
Frederick Pu (Dec 16 2024 at 01:04):
def AutoDiffTree.DiffTree.valid {α : Type u} {β : Type v} {shape : List Nat} : AutoDiffTree.DiffTree α β shape → Prop
| ofComputed computedTree => computedTree.valid
| mk _ parents => ∀ x ∈ parents, x.2.valid
Frederick Pu (Dec 16 2024 at 01:04):
all i did was change the definition ComputedAutoDiffTree.DiffTree
so i dont see why that should make rfl suddenly fail
Mario Carneiro (Dec 16 2024 at 01:06):
If you really want this to be true by rfl, you will probably have to do the structural recursion by hand
Mario Carneiro (Dec 16 2024 at 01:09):
Since this is a prop, another approach that should not be so bad is to define it as an inductive type
Mario Carneiro (Dec 16 2024 at 01:13):
inductive AutoDiffTree.DiffTree.valid {α : Type u} {β : Type v} : ∀ {shape : List Nat}, AutoDiffTree.DiffTree α β shape → Prop
| ofComputed : computedTree.valid → (ofComputed computedTree).valid
| mk : (∀ x ∈ parents, valid x.2) → (mk shape parents).valid
Mario Carneiro (Dec 16 2024 at 01:27):
the direct function definition by mutual recursion is still quite painful to write, but it has at least some of the definitional equalities you want:
def AutoDiffTree.DiffTree.valid {α : Type u} {β : Type v} {shape : List Nat} : AutoDiffTree.DiffTree α β shape → Prop := by
apply AutoDiffTree.DiffTree.rec
(motive_1 := fun a b => Prop) (motive_2 := fun a => Prop)
(motive_3 := fun a => Prop) (motive_4 := fun a => Prop)
case mk => intro _ _ ih; exact ih
case ofComputed => intro _ computedTree; exact computedTree.valid
case /-Array.-/ mk => intro _ ih; exact ih
case /-List.-/ nil => exact True
case /-List.-/ cons => intro _ _ ih1 ih2; exact ih1 ∧ ih2
case /-Prod.-/ mk => intro _ _ ih; exact ih
example {α : Type u} {β : Type v} {shape : List Nat} (x : ComputedAutoDiffTree.DiffTree α β shape) :
(AutoDiffTree.DiffTree.ofComputed x).valid = x.valid := rfl -- ok
example {α : Type u} {β : Type v} {shape : List Nat} (x : ComputedAutoDiffTree.DiffTree α β shape) :
(AutoDiffTree.DiffTree.mk shape parents).valid = ∀ x ∈ parents, x.2.valid := rfl -- fail
Frederick Pu (Dec 16 2024 at 01:50):
Mario Carneiro said:
Since this is a prop, another approach that should not be so bad is to define it as an inductive type
but then u still dont have a way of showing if (ofComputed c).valid then c.valid tho
Frederick Pu (Dec 16 2024 at 02:00):
nvm ig pattern matching solves that somehow
Frederick Pu (Dec 16 2024 at 04:17):
where did the additional shape✝ variable come from?
def forward {α : Type u} {β : Type v} {shape : List Nat} : AutoDiffTree α β shape → ComputedAutoDiffTree α β shape
| ⟨AutoDiffTree.DiffTree.ofComputed c, h⟩ => ⟨c, by {
cases h
trivial
}⟩
| ⟨AutoDiffTree.DiffTree.mk (parents : Array (Σ shape, AutoDiffTree.DiffTree α β shape)) (ctx : Σ shapes, EFunction α β shapes shape) (tensor : DualTensor α β shape), h⟩ =>
let temp := ctx.2.forward sorry sorry -- parents tensor
⟨⟨parents.map (fun ⟨shape, tree⟩ => ⟨shape, (⟨tree, sorry⟩ : AutoDiffTree α β shape).forward.1⟩), by {
exact ⟨ctx.snd.saveShapes,
⟨⟨
((List.finRange parents.size).map
(fun x =>
let tree := (parents.get x.val sorry).2
⟨(parents.get x.val _).fst, tree.tensor⟩
)).toArray,
sorry⟩, sorry⟩
⟩
}, ctx, sorry⟩, sorry⟩
termination_by
t => sizeOf t
decreasing_by
simp_wf
/-
α : Type u
β : Type v
shape✝ : List Nat
parents : Array ((shape : List Nat) × AutoDiffTree.DiffTree α β shape)
ctx : (shapes : Array (List Nat)) × EFunction α β shapes shape✝
tensor : DualTensor α β shape✝
h : (DiffTree.mk parents ctx tensor).valid
shape : List Nat
tree : AutoDiffTree.DiffTree α β shape
⊢ sizeOf tree < 1 + sizeOf shape✝ + sizeOf parents + sizeOf ctx + sizeOf tensor
-/
Frederick Pu (Dec 16 2024 at 18:10):
Mario Carneiro said:
If you really want this to be true by rfl, you will probably have to do the structural recursion by hand
so is there any way of proving stuff about well founded functions?
Kyle Miller (Dec 16 2024 at 18:31):
Yes, you can use tactics like unfold
/simp
to unfold the definitions. You may need to do cases
on arguments to make them unfoldable (according to the match patterns)
Kyle Miller (Dec 16 2024 at 18:32):
For example, using the code from the first message in this thread,
example {α : Type u} {β : Type v} {shape : List Nat} (x : ComputedAutoDiffTree.DiffTree α β shape) : (AutoDiffTree.DiffTree.ofComputed x).valid = x.valid := by
simp [AutoDiffTree.DiffTree.valid]
Kyle Miller (Dec 16 2024 at 18:40):
In particular, it's the first equation lemma that applies here:
example {α : Type u} {β : Type v} {shape : List Nat} (x : ComputedAutoDiffTree.DiffTree α β shape) : (AutoDiffTree.DiffTree.ofComputed x).valid = x.valid :=
AutoDiffTree.DiffTree.valid.eq_1 ..
(I don't think it's good practice to refer to equation lemmas directly, that's what unfold
/simp
are for, but I'm mentioning it for illustration. Lean does know, somewhere, that the function satisfies the equation you gave it.)
Frederick Pu (Dec 16 2024 at 19:03):
i wonder if there's a brute force tactic that could do this casework automatically for simpler definitions
Frederick Pu (Dec 16 2024 at 19:04):
can grind do this?
Kim Morrison (Jan 27 2025 at 13:53):
Kyle Miller said:
simp [AutoDiffTree.DiffTree.valid]
grind [AutoDiffTree.DiffTree.valid]
works equally well?
Last updated: May 02 2025 at 03:31 UTC