Zulip Chat Archive
Stream: lean4
Topic: Thought I had to fight with HEq, but successfully dodged it
Hirotaka Sato (Jul 19 2025 at 04:42):
In my proof, I need a version of List.zipIdx whose indices are of Fin, so I implemented zipIdxFin.
Since it is sometimes cumbersome to have the Fin.isLt condition, I would also like to state that forgetting this condition results in the usual List.zipIdx.
However, in trying to prove this fact, I came across HEq:
- Why does this even happen?
- How can I either solve this issue or get around this?
import Mathlib.Data.Finset.Defs
/--
Same as `List.zipIdx`, but with `Fin` indices.
-/
def zipIdxFin {α} (l : List α) {m : Nat} (h_len : m = l.length) (n : Nat := 0) : List (α × Fin (m + n)) :=
match hl : l, n with
| [], _=> []
| x :: xs, n =>
let m' := xs.length
let h_m : m = m' + 1 := by simp at h_len; grind only
let head_isLt : n < m + n := by simp [h_m]
let h_len' : m' = xs.length := by grind
let rest : List (α × Fin (m' + (n + 1))) := zipIdxFin xs h_len' (n + 1)
-- Now I need to convince that `List (α × Fin (m' + (n + 1)))` is the same thing as `List (α × Fin (m' + 1 + n))`
let cast : Fin (m' + (n + 1)) -> Fin (m + n) := fun ⟨ a, isLt' ⟩ =>
let new_isLt : a < m + n := by
rw [h_m, show m' + 1 + n = m' + (n + 1) from by ac_rfl]
assumption
⟨ a, new_isLt ⟩
(x, ⟨ n, head_isLt ⟩ ) :: rest.map (fun (x, i) => (x, cast i))
theorem zipIdxFin_forget_bound {α} {l : List α} {m : Nat} (h_len : m = l.length) (n : Nat := 0) :
List.map (fun ⟨ a, ⟨ i, isLt ⟩ ⟩ => (a, i)) (zipIdxFin l h_len n) = List.zipIdx l n := by
induction l generalizing m n with
| nil =>
unfold zipIdxFin
simp [List.zipIdx]
| cons x xs ih =>
simp
unfold zipIdxFin
simp
simp at h_len
match m with
| 0 => simp at h_len -- contradiction
| m' + 1 =>
simp at h_len
have ih2 := ih h_len (n + 1)
rw [← ih2]
congr
· exact h_len.symm
· sorry
· exact h_len.symm
· sorry
Hirotaka Sato (Jul 19 2025 at 04:57):
Fortunately, simplifying the definition eliminated the HEq.
import Mathlib.Data.Finset.Defs
/--
Same as `List.zipIdx`, but with `Fin` indices.
-/
def zipIdxFin {α} (l : List α) {m : Nat} (h_len : m = l.length) (n : Nat := 0) : List (α × Fin (m + n)) :=
match hl : l, n with
| [], _=> []
| x :: xs, n =>
let h_m : m = xs.length + 1 := by simp at h_len; assumption
let head_isLt : n < m + n := by simp [h_m]
let rest : List (α × Fin (xs.length + (n + 1))) := zipIdxFin xs (by rfl) (n + 1)
-- Now I need to convince that `List (α × Fin (xs.length + (n + 1)))` is the same thing as `List (α × Fin (xs.length + 1 + n))`
let cast : Fin (xs.length + (n + 1)) -> Fin (m + n) := fun ⟨ a, isLt' ⟩ =>
let new_isLt : a < m + n := by
rw [h_m, show xs.length + 1 + n = xs.length + (n + 1) from by ac_rfl]
assumption
⟨ a, new_isLt ⟩
(x, ⟨ n, head_isLt ⟩ ) :: rest.map (fun (x, i) => (x, cast i))
theorem zipIdxFin_forget_bound {α} {l : List α} {m : Nat} (h_len : m = l.length) (n : Nat := 0) :
List.map (fun ⟨ a, ⟨ i, _isLt ⟩ ⟩ => (a, i)) (zipIdxFin l h_len n) = List.zipIdx l n := by
induction l generalizing m n with
| nil =>
unfold zipIdxFin
simp [List.zipIdx]
| cons x xs ih =>
simp
unfold zipIdxFin
simp
simp at h_len
have ih2 := ih (by rfl) (n + 1)
rw [← ih2]
congr
pandaman (Jul 19 2025 at 06:41):
Just before congr, the (zipIdxFin xs ⋯ (n + 1)) term on the left hand side has type List (α × Fin (xs.length + (n + 1))), while (zipIdxFin xs h_len (n + 1)) on the right hand side has type List (α × Fin (m' + (n + 1))). I guess this is where the HEq comes from.
Substituting m early helped:
theorem zipIdxFin_forget_bound {α} {l : List α} {m : Nat} (h_len : m = l.length) (n : Nat := 0) :
List.map (fun ⟨ a, ⟨ i, isLt ⟩ ⟩ => (a, i)) (zipIdxFin l h_len n) = List.zipIdx l n := by
induction l generalizing m n with
| nil =>
unfold zipIdxFin
simp [List.zipIdx]
| cons x xs ih =>
subst m
simp [zipIdxFin]
rw [←ih rfl (n + 1)]
simp
pandaman (Jul 19 2025 at 06:44):
That being said, I would just use List.zipIdx in code and List.snd_lt_of_mem_zipIdx in proofs.
Last updated: Dec 20 2025 at 21:32 UTC