Zulip Chat Archive

Stream: new members

Topic: Eliminating eq.rec applications in goals.


Julian Sutherland (Aug 27 2022 at 16:08):

I'm trying to re-implement fixed length vector type vec α n as an exercise and wanted to know if I'm I approaching this correctly? :smile:

I start of with the following inductive definition:

universe u

inductive vec : Type u    Type (u + 1)
  | nil  :  {α : Type u}, vec α 0
  | cons : {α : Type u} {n : }, α  vec α n  vec α (n + 1)

I've defined a concat function that concatenates two fixed length vectors in the obvious way:

def concat :  {α : Type u} {n m : }, vec α n  vec α m  vec α (n + m)
 | _ 0 m nil v₂ :=
    begin
      rw nat.zero_add m,
      exact v₂,
    end
 | α _ _ (cons e v₁) v₂ :=
    begin
      rw add_assoc,
      rw add_comm 1 _x,
      rw add_assoc,
      exact (cons e (concat v₁ v₂)),
    end

as well as a function to convert a fixed length vector into a list:

def v2l :  {α : Type} {n : }, vec α n -> list α
| _ 0 nil := []
| _ _ (cons e vs) := e :: (v2l vs)

I'm not trying to show that the concat operation on fixed length vectors is equivalent to the ++ operator on lists:

lemma vec_concat_equiv :
   {α : Type} {n : } {v₁ v₂ : vec α n},
    v2l (concat v₁ v₂) = (v2l v₁) ++ (v2l v₂) :=
  begin
    intros α n v₁ v₂,
    induction v₁,
    rw [ v2l
       , list.append_eq_has_append
       , list.append
       , concat
    ],
    refl,
    rw concat,
    rw v2l,
    sorry,
  end

In the base case of this proof, the application of eq.mpr, introduced by the use of the rw tactic in the definition of concat, can be "eliminated" by the use of the refl tactic, however, I cannot find a way of eliminating them in subterms of my goals as is required in the inductive case.

Junyan Xu (Aug 27 2022 at 16:32):

You definition of concat is awkward because it goes against definitional equality: m is defeq to m+0 but not to 0+m, and (n+m)+1 is defeq to n+(m+1) but not to (n+1)+m. So if you define concat by induction on the vec α m argument, you wouldn't need any rewrite. But in order to do so, you also need to switch the order of the two arguments of the constructor vec.cons, and the following works:

universe u

inductive vec : Type u    Type (u + 1)
| nil  :  {α : Type u}, vec α 0
| cons :  {α : Type u} {n : }, vec α n  α  vec α (n + 1)

open vec
def concat :  {α : Type u} {n m : }, vec α n  vec α m  vec α (n + m)
| _ n 0 v₁ nil := v₁
| α _ _ v₁ (cons v₂ e) := cons (concat v₁ v₂) e

Eric Rodriguez (Aug 27 2022 at 16:35):

Usually the answer (and this is still something that is being discussed and researched and argued about) is that if you see eq.rec in goals, the design is "wrong" in some way and you should see how you can work around that (e.g. by making more things defeq, or by passing a proof obligation; e.g. a concat of typr vec a n -> vec a m -> \forall k, k = n + m -> vec a k)

Julian Sutherland (Aug 27 2022 at 17:03):

@Junyan Xu Thank you very much for your help, flipping the order of addition of n and m in the type of concat yields a much simpler definition by exploiting definitional equality:

def concat :  {α : Type u} {n m : }, vec α n  vec α m  vec α (m + n)
 | _ 0 m nil v₂ := v₂
 | α _ _ (cons e v₁) v₂ := cons e (concat v₁ v₂)

In turn, this definition allows for a simple proof of a (slight variation) of the statement above:

lemma vec_concat_equiv :
   {α : Type} {n m : } {v₁ : vec α n} {v₂ : vec α m},
    v2l (concat v₁ v₂) = (v2l v₁) ++ (v2l v₂) :=
  begin
    intros α n m v₁ v₂,
    induction v₁,
    rw [ v2l
       , list.append_eq_has_append
       , list.append
       , concat
    ],
    rw [concat, v2l, v2l, v₁_ih],
    simp,
  end

Note that above I had accidentally bound the vector lengths to be the same, which was impeding the induction on v₁.

@Eric Rodriguez Thank you for your advice, I'll strive to apply this rule in future :)

Junyan Xu (Aug 27 2022 at 17:17):

I think a subtype like docs#vector is much more robust to defeq issues than an inductive definition like yours, and that's probably why mathlib doesn't use your definition, and why docs#fin has much more API than docs#fin2 ...

Kyle Miller (Aug 27 2022 at 18:14):

@Julian Sutherland By the way, it would be helpful having a #mwe for this since there are some missing commands to get it working (you can put it in a spoiler block at the end of a post if you want to keep the flow).

Here's a way to get your original setup to work (with the generalization of vec_concat_equiv):

import tactic

universe u

inductive vec : Type u    Type (u + 1)
  | nil  :  {α : Type u}, vec α 0
  | cons : {α : Type u} {n : }, α  vec α n  vec α (n + 1)

open vec

def concat :  {α : Type u} {n m : }, vec α n  vec α m  vec α (n + m)
 | _ 0 m nil v₂ :=
    begin
      rw nat.zero_add m,
      exact v₂,
    end
 | α _ m (@cons _ n e v₁) v₂ :=
    begin
      rw [add_assoc, add_comm 1,  add_assoc],
      exact (cons e (concat v₁ v₂)),
    end

def v2l :  {α : Type} {n : }, vec α n -> list α
| _ 0 nil := []
| _ _ (cons e vs) := e :: (v2l vs)

@[simp]
lemma v2l_cast {α : Type} {n m : } (h : n = m) (v : vec α n) :
  v2l (cast (by rw h : vec α n = vec α m) v) = v2l v :=
begin
  subst h,
  refl,
end

lemma vec_concat_equiv :
   {α : Type} {n m : } {v₁ : vec α n} {v₂ : vec α m},
    v2l (concat v₁ v₂) = (v2l v₁) ++ (v2l v₂) :=
  begin
    intros α n m v₁ v₂,
    induction v₁ generalizing m,
    { rw [v2l, list.append_eq_has_append, list.append, concat],
      simp [v2l_cast] },
    { rw [concat, v2l],
      simp only [eq_mpr_eq_cast, cast_eq, cast_cast, list.cons_append],
      rw v2l_cast,
      rw [v2l, v₁_ih],
      rw [add_assoc, add_comm 1,  add_assoc], },
  end

Kyle Miller (Aug 27 2022 at 18:15):

The key tool is v2l_cast, which shows v2l is invariant under a certain kind of docs#cast. I'm using cast rather than eq.mpr since docs#eq_mpr_eq_cast is a simp lemma.

Kyle Miller (Aug 27 2022 at 18:18):

Generally speaking, the way you deal with eq.rec, eq.mpr, and cast is that you find some way to turn the eq into a definitional equality (i.e., rewrite things until you have a = b such that (rfl : a = b)). The v2l_cast lemma is doing this by using subst so that the cast reduces away.

Kyle Miller (Aug 27 2022 at 18:26):

Here's another way to do it, which is to create your own cast function (maybe more often named copy?) that is specialized to rewriting the indices of a type.

import tactic

universe u

inductive vec : Type u    Type (u + 1)
  | nil  :  {α : Type u}, vec α 0
  | cons : {α : Type u} {n : }, α  vec α n  vec α (n + 1)

open vec

def vec.cast {α : Type u} {n m : } (v : vec α n) (h : n = m) : vec α m :=
eq.rec v h

@[simp] lemma vec.cast_cast {α : Type u} {n₁ n₂ n₃ : } (v : vec α n₁)
  (h : n₁ = n₂) (h' : n₂ = n₃) :
  (v.cast h).cast h' = v.cast (h.trans h') :=
by { subst_vars, refl }

@[simp] lemma vec.cast_rfl {α : Type u} {n : } (v : vec α n) :
  v.cast rfl = v := rfl

def concat :  {α : Type u} {n m : }, vec α n  vec α m  vec α (n + m)
 | _ 0 m nil v₂ := v₂.cast (nat.zero_add m).symm
 | α _ m (cons e v₁) v₂ := (cons e (concat v₁ v₂)).cast
    (by rw [add_assoc, add_comm m,  add_assoc])

def v2l :  {α : Type u} {n : }, vec α n -> list α
| _ 0 nil := []
| _ _ (cons e vs) := e :: v2l vs

@[simp]
lemma v2l_cast {α : Type u} {n m : } (h : n = m) (v : vec α n) :
  v2l (v.cast h) = v2l v :=
begin
  subst h,
  refl,
end

lemma vec_concat_equiv {α : Type u} {n m : } {v₁ : vec α n} {v₂ : vec α m} :
  v2l (concat v₁ v₂) = v2l v₁ ++ v2l v₂ :=
begin
  induction v₁,
  { simp [v2l, concat], },
  { simp [v2l, concat, v₁_ih], },
end

Kyle Miller (Aug 27 2022 at 18:28):

I like how these cast/copy functions tend to work out, since they lead to proofs that are mostly just simp. The key to its operation is that it syntactically gives you a proof that the indices are equal right in the vec.cast expression, which is what proofs about vec.cast can then make use of by a simple subst many times.

Kyle Miller (Aug 27 2022 at 18:31):

If you write your simp lemmas to "bubble up" the casts to the top level of the expression, then you have a good shot at eliminating them completely. For example, v2l is able to consume them via v2l_cast.


Last updated: Dec 20 2023 at 11:08 UTC