Zulip Chat Archive

Stream: general

Topic: How to prove Eq.ndrec


Moritz R (Jan 17 2026 at 15:27):

i have this Thing Eq.ndrec in my goal, and can't find out how to prove it :smiling_face_with_tear:
I have dumbed down the definition of Term a bit, but kept the spirit of my real problem otherwise.

import Mathlib.Data.Sym.Sym2



inductive Term (F :   Type*) (α : Type*) where
  | var : α  Term F α
export Term (var)

namespace Term

variable {α : Type*} {F :   Type*}

@[simp]
def varFinset [DecidableEq α] : Term F α  Finset α
  | .var a => {a}

/-- Restricts a term to use only a set of the given variables. -/
def restrictVar [DecidableEq α] :  (t : Term F α) (_f : t.varFinset  β), Term F β
  | var a, f => var (f a, Finset.mem_singleton_self a)

end Term

/-- An Atom is an unordered pair of Terms, representing an (unordered)
    equation of the form s1 ≈ s2. -/
abbrev Atom (F :   Type*) (α : Type*) := Sym2 (Term F α)

abbrev Prod.toAtom (ts : Term F α × Term F α) : Atom F α := Sym2.mk ts



section rec
/- just for easier reference a copy of the used recursion theorem (Quot.rec). The copy isn't used below, only the original. -/
variable {α : Sort u}
variable {r : α  α  Prop}
variable {motive : Quot r  Sort v}

@[elab_as_elim] abbrev rec22
    (f : (a : α)  motive (Quot.mk r a))
    (h : (a b : α)  (p : r a b)  (Eq.ndrec (f a) (Quot.sound p)) = f b)
    (q : Quot r) : motive q :=
  Eq.ndrecOn (Quot.liftIndepPr1 f h q) ((Quot.lift (Quot.indep f) (Quot.indepCoherent f h) q).2)

end rec

section restrictAtom

namespace Atom

variable {α : Type*} {F :   Type*}

/-- The Finset of all variables that appear in a given atom. -/
def varFinset [DecidableEq α] : Atom F α  Finset α :=
  Quot.lift (fun ts  ts.fst.varFinset  ts.snd.varFinset) (by grind [Sym2.Rel])

@[simp, grind =]
lemma varFinset_eq [DecidableEq α] (t1 t2 : Term F α) :
    Atom.varFinset s(t1, t2) = Term.varFinset t1  Term.varFinset t2 := by
    rfl

def restrictVar {β : Type*} [DecidableEq α] : (a :Atom F α)  ((a.varFinset  β)  Atom F β) :=
    Quot.rec (fun ts transf =>
      s(ts.fst.restrictVar (transf  Set.inclusion (by grind)), ts.snd.restrictVar (transf  Set.inclusion (by grind))))
  (by
    intro a b hrel
    sorry

  )


end Atom

end restrictAtom

Aaron Liu (Jan 17 2026 at 15:34):

  (by
    intro a b hrel
    rw! [Quot.sound hrel]
    simp only
    -- the goal now has no `Eq.ndrec`
    sorry

  )

for the rw! tactic you will need to add an import Mathlib.Tactic.DepRewrite

Aaron Liu (Jan 17 2026 at 15:36):

you probably shouldn't be using the Quot.rec but rather the specialized docs#Sym2.rec since you're using Sym2

Aaron Liu (Jan 17 2026 at 15:37):

even though it's exactly the same the benefit is that you don't break api boundaries

Moritz R (Jan 17 2026 at 15:44):

It seems something has changed about rw! in the latest mathlib, on my version, it screams at me to use rw! (castMode := .all) [Quot.sound hrel] in stable mathlib (but it works then)

Moritz R (Jan 17 2026 at 15:45):

Why does rewriting with [a] = [b] eliminate this Eq.ndrec?

Aaron Liu (Jan 17 2026 at 15:45):

because the equality it's working on is rewritten from [a] = [b] to [b] = [b]

Aaron Liu (Jan 17 2026 at 15:46):

and [b] = [b] is definitionally equal to rfl

Moritz R (Jan 17 2026 at 16:08):

Let me phrase it in my own words: We have the goal
(Eq.ndrec (context [a]) ([a] = [b])) = context [b]).
rewriting it changes it to
(Eq.ndrec (context [b]) ([b] = [b])) = context [b])? i dont think that's the goal after the rw!.

Can you dumb it down a bit more please?

Aaron Liu (Jan 17 2026 at 16:08):

what's the goal after rw!?

Aaron Liu (Jan 17 2026 at 16:09):

rw! went through some fixes over the past month so what I see might not be the same as what you see

Moritz R (Jan 17 2026 at 16:11):

Lets use the Lean 4 Web on latest mathlib with this code:

import Mathlib.Data.Sym.Sym2
import Mathlib.Tactic.DepRewrite


inductive Term (F :   Type*) (α : Type*) where
  | var : α  Term F α
export Term (var)

namespace Term

variable {α : Type*} {F :   Type*}

@[simp]
def varFinset [DecidableEq α] : Term F α  Finset α
  | .var a => {a}

/-- Restricts a term to use only a set of the given variables. -/
def restrictVar [DecidableEq α] :  (t : Term F α) (_f : t.varFinset  β), Term F β
  | var a, f => var (f a, Finset.mem_singleton_self a)

end Term

/-- An Atom is an unordered pair of Terms, representing an (unordered)
    equation of the form s1 ≈ s2. -/
abbrev Atom (F :   Type*) (α : Type*) := Sym2 (Term F α)

abbrev Prod.toAtom (ts : Term F α × Term F α) : Atom F α := Sym2.mk ts



section rec
/- just for easier reference a copy of the used recursion theorem: -/
variable {α : Sort u}
variable {r : α  α  Prop}
variable {motive : Quot r  Sort v}

@[elab_as_elim] abbrev rec22
    (f : (a : α)  motive (Quot.mk r a))
    (h : (a b : α)  (p : r a b)  (Eq.ndrec (f a) (Quot.sound p)) = f b)
    (q : Quot r) : motive q :=
  Eq.ndrecOn (Quot.liftIndepPr1 f h q) ((Quot.lift (Quot.indep f) (Quot.indepCoherent f h) q).2)

end rec

section restrictAtom

namespace Atom

variable {α : Type*} {F :   Type*}

/-- The Finset of all variables that appear in a given atom. -/
def varFinset [DecidableEq α] : Atom F α  Finset α :=
  Quot.lift (fun ts  ts.fst.varFinset  ts.snd.varFinset) (by grind [Sym2.Rel])

@[simp, grind =]
lemma varFinset_eq [DecidableEq α] (t1 t2 : Term F α) :
    Atom.varFinset s(t1, t2) = Term.varFinset t1  Term.varFinset t2 := by
    rfl

def restrictVar {β : Type*} [DecidableEq α] : (a :Atom F α)  ((a.varFinset  β)  Atom F β) :=
    Quot.rec (fun ts transf =>
      s(ts.fst.restrictVar (transf  Set.inclusion (by grind)), ts.snd.restrictVar (transf  Set.inclusion (by grind))))
  (by
    intro a b hrel
    rw! [Quot.sound hrel]
    simp only

  )


end Atom

end restrictAtom

Moritz R (Jan 17 2026 at 16:11):

Before the rw! we have the goal:

Eq.ndrec (motive := fun x  ((varFinset x)  β)  Atom F β)
    (fun transf  s(a.1.restrictVar (transf  Set.inclusion ), a.2.restrictVar (transf  Set.inclusion )))  =
  fun transf  s(b.1.restrictVar (transf  Set.inclusion ), b.2.restrictVar (transf  Set.inclusion ))

Moritz R (Jan 17 2026 at 16:12):

After the rw! with [a] = [b]` we have the goal

((fun {α} a motive refl  refl) (Quot.mk (Sym2.Rel (Term F α)) b)
    (fun x x_1  (fun x  ((varFinset x)  β)  Atom F β) x) fun transf 
    s(a.1.restrictVar (transf  Set.inclusion ), a.2.restrictVar (transf  Set.inclusion ))) =
  fun transf  s(b.1.restrictVar (transf  Set.inclusion ), b.2.restrictVar (transf  Set.inclusion ))

Moritz R (Jan 17 2026 at 16:13):

And further after the simp only we have the for me solvable goal

(fun transf  s(a.1.restrictVar (transf  Set.inclusion ), a.2.restrictVar (transf  Set.inclusion ))) = fun transf 
  s(b.1.restrictVar (transf  Set.inclusion ), b.2.restrictVar (transf  Set.inclusion ))

Aaron Liu (Jan 17 2026 at 16:14):

seems like your version of mathlib doesn't have #32306 yet

Aaron Liu (Jan 17 2026 at 16:14):

the goal might be more understandable if you rewrite! instead?

Aaron Liu (Jan 17 2026 at 16:14):

and then simp only

Moritz R (Jan 17 2026 at 16:14):

So before the rw! we have a goal along the lines of

(Eq.ndrec (context [a]) ([a] = [b])) = context [b])

Aaron Liu (Jan 17 2026 at 16:16):

yes

Moritz R (Jan 17 2026 at 16:16):

aha, after rewrite! (not rw!) the goal is "exactly the same" but some of the proofs have been changed.

Moritz R (Jan 17 2026 at 16:19):

and after the this suddenly it simps to the nice goal

context [a] = context [b]

Moritz R (Jan 17 2026 at 16:23):

E.g. it changes a proof of

(Term.varFinset a.1)  (Atom.varFinset [a])

to a proof of

(Term.varFinset a.1)  (Atom.varFinset [b])

(i have simplified notation a bit)

Moritz R (Jan 17 2026 at 16:27):

But why is the Eq.ndrec now vanishing after the simp only?

Moritz R (Jan 17 2026 at 16:28):

i.e. why is (now with the invisible proofs hidden in context changed)

(Eq.ndrec (context [a]) ([a] = [b])) = context [a]

Aaron Liu (Jan 17 2026 at 16:29):

it's not just the proofs that change

Aaron Liu (Jan 17 2026 at 16:30):

the implicit arguments change too

Moritz R (Jan 17 2026 at 16:32):

ah yes, there is a bunch of [a]s which change to [b]s.

Moritz R (Jan 17 2026 at 16:33):

But still i dont get it. I dont really know what Eq.ndrec is trying to say

Kevin Buzzard (Jan 17 2026 at 17:57):

I think it's worth stressing the point that you probably shouldn't be dealing with Eq.ndrec in the first place and a better question is "how did I end up with this in my goal and what should I have done instead".

Kevin Buzzard (Jan 17 2026 at 17:57):

Aaron Liu said:

you probably shouldn't be using the Quot.rec but rather the specialized docs#Sym2.rec since you're using Sym2

Is this the issue?

Moritz R (Jan 17 2026 at 17:59):

Sym2.rec is basically just a renaming of Quot.rec, (instantiated for the quotient Sym 2 of course)

Moritz R (Jan 17 2026 at 18:00):

I don't know how to the restrictvar definition without one of the .recs, since I don't think it's possible using only Quot.lift

Ruben Van de Velde (Jan 17 2026 at 18:06):

Looks like you introduced it yourself in rec22

Ruben Van de Velde (Jan 17 2026 at 18:06):

Why?

Aaron Liu (Jan 17 2026 at 18:06):

Kevin Buzzard said:

I think it's worth stressing the point that you probably shouldn't be dealing with Eq.ndrec in the first place and a better question is "how did I end up with this in my goal and what should I have done instead".

in this case it's pretty unavoidable though?

Aaron Liu (Jan 17 2026 at 18:06):

you would have to change the definition of Atom or something

Moritz R (Jan 17 2026 at 18:07):

Ruben Van de Velde schrieb:

Looks like you introduced it yourself in rec22

sorry for the confusion, that is just a copy of Quot.rec so i had it viewable in the same file. It isnt even used, its just for helping me with the arguments

Aaron Liu (Jan 17 2026 at 18:08):

well you can avoid it if you prefer HEq instead

Aaron Liu (Jan 17 2026 at 18:08):

I would consider HEq to be worse though

Moritz R (Jan 17 2026 at 18:17):

Oh wait, i can use Quot.map (or better Sym2.map) since my function only works on each element of the unordered pair by it's own!

Moritz R (Jan 17 2026 at 18:17):

Then i don't need a lifting proof at all

Moritz R (Jan 17 2026 at 18:18):

oh wait no that doesn't work

Moritz R (Jan 17 2026 at 18:18):

maybe i can use some dependant version of map

Moritz R (Jan 17 2026 at 18:31):

Yes! Sym2.pmap works out for me

Moritz R (Jan 17 2026 at 18:33):

def restrictVar {β : Type*} [DecidableEq α] (a : Atom F α) (f : (a.varFinset  β)) : Atom F β :=
    Sym2.pmap (P := fun t  t  a) (fun (t: Term F α) (h_t_in_a)  (t.restrictVar (f  Set.inclusion (by sorry))) a (by simp)

The sorry that is left, is provable for my actual definition of Term (i used a simplified one for the example)


Last updated: Feb 28 2026 at 14:05 UTC