Zulip Chat Archive
Stream: new members
Topic: Vector nil_append
sgcs (Apr 18 2023 at 09:52):
I'm trying to prove some things about the vector data type but occasionally run into problems dealing with the vector size in type definitions. For example, I have the following code:
universe u v w
inductive Vector (α : Type u) : Nat → Type u where
| nil : Vector α 0
| cons {n : Nat} : α → Vector α n → Vector α (Nat.succ n)
infixr:67 " :: " => Vector.cons
namespace Vector
def append {α : Type u} {m n : Nat} : Vector α m → Vector α n → Vector α (m + n)
| nil, ys => by simp only [Nat.zero_add]; exact ys
| x :: xs, ys => by
simp only [Nat.succ_add]
exact cons x (append xs ys)
@[simp]
theorem nil_append {n : Nat} {α : Type} (as : Vector α n) : @Eq (Vector α n) (append (@nil α) as) as := by sorry
How do I define nil_append
in a type correct way? If I could use Nat.zero_add
in the function definition, the types would match but not sure how to proceed.
sgcs (Apr 18 2023 at 09:59):
I've tried something like this:
@[simp]
theorem nil_append {n : Nat} {α : Type} (as : Vector α n) : @Eq (Vector α n) ((@Eq.mp (Vector α (0 + n)) (Vector α n) (by simp) (append (@nil α) as))) as := by sorry
It ypechecks, but I'm not really sure how to prove it with the cast present. And it doesn't seem like it would be useful the way it's defined (at the very least, nil ++ xs doesn't seem to simplify in other places)
Kyle Miller (Apr 18 2023 at 10:26):
Here are two places in mathlib that implement a pattern to solve this sort of issue: docs4#SimpleGraph.Walk.copy
docs4#Quiver.Hom.cast
Kyle Miller (Apr 18 2023 at 10:28):
The idea would be that you define a Vector.cast
function that takes an equality that swaps out the size of the array with something else. This is just Eq.rec
under the hood but it helps keep the rewrites circumscribed to just the size (otherwise you're liable to get equalities of types, which aren't great)
Kyle Miller (Apr 18 2023 at 10:29):
For example, in the Vector
namespace:
def cast (v : Vector α m) (h : m = n) : Vector α n := h ▸ v
@[simp] theorem cast_rfl (v : Vector α m) : v.cast rfl = v := rfl
@[simp] theorem cast_cast (v : Vector α m) (h : m = m') (h' : m' = m'') :
(v.cast h).cast h' = v.cast (h.trans h') := by subst_vars; rfl
Kyle Miller (Apr 18 2023 at 10:30):
Then this typechecks:
@[simp]
theorem nil_append {n : Nat} {α : Type} (as : Vector α n) :
append nil as = as.cast (Nat.zero_add n).symm := by sorry
Kyle Miller (Apr 18 2023 at 10:32):
With a new version of append
that makes use of cast
, nil_append
is a one-liner:
def append {α : Type u} {m n : Nat} : Vector α m → Vector α n → Vector α (m + n)
| nil, ys => ys.cast (by simp [Nat.succ_add])
| x :: xs, ys => (cons x (append xs ys)).cast (by simp only [Nat.succ_add])
@[simp]
theorem nil_append {n : Nat} {α : Type} (as : Vector α n) :
append nil as = as.cast (Nat.zero_add n).symm := by
cases as <;> simp [append]
Floris van Doorn (Apr 18 2023 at 10:37):
Since +
on Nat
is defined by recursion on the second argument, I strongly recommend that you define Vector.append
also by recursion on the second argument. This allows you to define it without any casts. Of course, to state nil_append
you still have to use casts or heterogenous equality.
Kyle Miller (Apr 18 2023 at 10:43):
Vector.cons
also needs to add elements to the end of the list to be able to implement Vector.append
efficiently if you're inducting on the second argument. (Or we need to go back to the drawing board and refine Nat.add
to induct on the first argument too :smile:)
Floris van Doorn (Apr 18 2023 at 10:44):
You're right, this would need an snoc
operation...
Kyle Miller (Apr 18 2023 at 10:46):
Re HEq
: this is another way to state nil_append
, and HEq
can sometimes be more convenient than cast
. The downsides are (1) you can't rewrite with a HEq
lemma and (2) the HEq
doesn't "know" that the sizes are equal, unlike cast
which records that equality.
Eric Rodriguez (Apr 18 2023 at 10:57):
i think you are able to extract the equality of types and therefore sizes from the HEq
Kyle Miller (Apr 18 2023 at 12:14):
I think using only type equalities, from Vector Nat m
you can only tell whether m
is zero or nonzero, since m
is zero if and only if the type is a singleton and m
is nonzero if and only if the type is countable.
Eric Rodriguez (Apr 18 2023 at 12:31):
Ah, yes, the constructor isn't necessarily injective on types unless alpha is a fintype.
Eric Rodriguez (Apr 18 2023 at 12:32):
I wonder if stating that those families are disjoint is possible in type theories
Last updated: Dec 20 2023 at 11:08 UTC