Zulip Chat Archive
Stream: Is there code for X?
Topic: Finitely many strings of length ≤ n?
Jacob Weightman (Feb 16 2025 at 18:58):
I just wrapped up a proof about a string rewriting system that makes use of the pigeonhole principle and the fact that there are finitely many short strings. I have the following definition of "short strings," and the corresponding hole in my proof:
variable {α : Type u} [Inhabited α][Fintype α]
def ShortList (α : Type u) (n : ℕ) :=
{ l : List α // l.length ≤ n }
instance ShortList.fintype [Fintype α] {n : ℕ} : Fintype (ShortList α n) :=
-- There are finitely many List α of length i, by induction:
-- zero => there is exactly one empty string, [].
-- succ i => prefix each of the finitely many List α of length i with each of the finitely many
-- elements of α. There are #a * #(ShortList α i) of these.
-- The strings of length ≤ n is the union of strings of length i ≤ n. Since there are finitely
-- many strings of length i, and the union of finite sets is finite, there are also finitely many
-- strings of length ≤ n. ∎
sorry
My initial searching of Mathlib didn't turn up anything quite like what I was looking for, but I thought I'd ask here before I spent time reinventing the wheel. Is there something like this already?
Robin Arnez (Feb 16 2025 at 19:16):
Your ShortList
is List.Vector
and in fact there is an instance for Fintype
of it!
variable {α : Type u} [Inhabited α][Fintype α]
abbrev ShortList (α : Type u) (n : ℕ) := List.Vector α n
instance ShortList.fintype [Fintype α] {n : ℕ} : Fintype (ShortList α n) :=
inferInstance
Edward van de Meent (Feb 16 2025 at 19:17):
Robin Arnez said:
Your
ShortList
isList.Vector
and in fact there is an instance forFintype
of it!
not quite. List.Vector
is lists of a certain length, while it seems ShortList
is meant to refer to lists of a certain length or below
Robin Arnez (Feb 16 2025 at 19:18):
Oh I missed that detail
Robin Arnez (Feb 16 2025 at 19:18):
Then perhaps you'd actually want to do this yourself
Robin Arnez (Feb 16 2025 at 19:19):
or actually this works:
abbrev ShortList (α : Type u) (n : ℕ) := (i : Fin (n + 1)) ×' List.Vector α i.val
instance ShortList.fintype [Fintype α] {n : ℕ} : Fintype (ShortList α n) :=
inferInstance
Edward van de Meent (Feb 16 2025 at 19:21):
actually, you don't need PSigma
here, just Sigma
works
Edward van de Meent (Feb 16 2025 at 19:22):
and i imagine that the subtype definition has better definitional equality, so it will probably be easier to get the Fintype
instance from proving an equivalence between the sigmatype and the subtype
Robin Arnez (Feb 16 2025 at 19:23):
Yeah, right, for some reason I thought ×
was Prod
and ×'
was Sigma
lol. And yeah, an equiv is probably what you want, shouldn't be too hard though.
Edward van de Meent (Feb 16 2025 at 19:26):
(×
works for both Prod
and Sigma
)
Robin Arnez (Feb 16 2025 at 19:28):
(yeah I'm aware)
Kyle Miller (Feb 16 2025 at 19:28):
(i : Fin (n + 1)) × List.Vector α i
seems fine to me
Edward van de Meent (Feb 16 2025 at 19:55):
it is prone to getting into dependent type hell tho
Violeta Hernández (Feb 17 2025 at 03:43):
Maybe you instead just want to build an equivalence between {l // l.length ≤ n}
and the sigma type?
Chris Wong (Feb 17 2025 at 15:13):
Since nobody's brought it up yet, docs#Fintype.ofEquiv
Edward van de Meent (Feb 17 2025 at 15:41):
Edward van de Meent said:
so it will probably be easier to get the
Fintype
instance from proving an equivalence between the sigmatype and the subtype
that was what i was attempting to hint at here
Jacob Weightman (Feb 17 2025 at 18:44):
Okay, I haven't done much practical Lean work with dependent product types, but it isn't quite obvious to me how to change my proof to work with them — "dependent type hell" seems like an apt description. I've also tried building the equivalence suggested by @Violeta Hernández, but run into a similar problem. Maybe someone could give me a pointer on how to finish this proof?
def ShortList (α : Type u) (n : ℕ) :=
{ l : List α // l.length ≤ n }
abbrev ShortList' (α : Type u) (n : ℕ) := (i : Fin (n + 1)) × List.Vector α i
def ShortList_iso (α : Type u) (n : ℕ) : ShortList' α n ≃ ShortList α n :=
let toFun (l : ShortList' α n) : ShortList α n :=
let ⟨i, v⟩ := l
have π : v.val.length ≤ n := by
simp; rw [← Nat.lt_succ]; exact i.isLt
⟨v.val, π⟩
let fromFun (l : ShortList α n) : ShortList' α n :=
let len : Fin (n + 1) := l.val.length
have π : l.val.length = len := by
simp [len]; rw [Nat.mod_eq_of_lt]; rw[Nat.lt_succ]; exact l.property
⟨l.val.length, ⟨l.val, π⟩⟩
have π₁ : Function.LeftInverse fromFun toFun := by
intro l; simp [toFun, fromFun]
induction' l with a b
simp
-- rw [← eq_cast_iff_heq]
-- rfl
-- #check eq_cast_iff_heq
sorry
have π₂ : Function.RightInverse fromFun toFun := by
intro l; simp [toFun, fromFun]
⟨toFun, fromFun, π₁, π₂⟩
instance ShortList'.fintype [Fintype α] {n : ℕ} : Fintype (ShortList' α n) :=
inferInstance
instance ShortList.fintype [Fintype α] {n : ℕ} : Fintype (ShortList α n) :=
Fintype.ofEquiv _ (ShortList_iso α n)
Here's the context at the point of the sorry
:
α✝ : Type u
inst✝¹ : Inhabited α✝
inst✝ : Fintype α✝
T : TagSystem α✝
α : Type u
n : ℕ
toFun : ShortList' α n → ShortList α n := fun l ↦
match l with
| ⟨i, v⟩ =>
let_fun π := ⋯;
⟨↑v, π⟩
fromFun : ShortList α n → ShortList' α n := fun l ↦
let len := ↑(↑l).length;
let_fun π := ⋯;
⟨↑(↑l).length, ⟨↑l, π⟩⟩
a : Fin (n + 1)
b : List.Vector α ↑a
⊢ HEq ⟨↑b, ⋯⟩ b
I haven't encountered heterogeneous equality before, and my familiarity with casting is quite lacking. This has me at a loss for how to prove that. At this point, I'm also not really convinced that pulling in this heavy machinery simplifies the proof at all, which makes me feel like I must be missing something. Could any of you kind folks help me bridge this gap?
Violeta Hernández (Feb 17 2025 at 18:57):
Currently AFK so can't help much with the proof, but perhaps you want to prove a more general equivalence? Namely for f : b -> a
and p : a -> Prop
, {x // p (f x)}
is equivalent to {a // p a} ×' {x // f x = a}
.
Violeta Hernández (Feb 17 2025 at 18:57):
Your specific case is f = length
and p x = x ≤ n
Violeta Hernández (Feb 17 2025 at 18:58):
(in fact, maybe this already exists?)
Violeta Hernández (Feb 17 2025 at 18:59):
I mention this because I've realized this result would also be useful to me!
Bhavik Mehta (Feb 17 2025 at 20:01):
Note docs#List.finite_length_le exists
Kyle Miller (Feb 17 2025 at 20:06):
(@Jacob Weightman It's worth evaluating whether you need Fintype
in your argument for some reason, or if Finite
suffices — if it's for proofs, it's usually the right choice. docs#Fintype.ofFinite is one of the ways to temporarily get a Fintype
instance in the middle of a proof if you really need it.)
Matt Diamond (Feb 17 2025 at 20:16):
yeah, if Finite
suffices then the proof is literally just (List.finite_length_le α n).to_subtype
, as @Bhavik Mehta pointed out
Violeta Hernández (Feb 17 2025 at 20:19):
That's a shame, I do think the Fintype
instance could still be useful.
Aaron Liu (Feb 17 2025 at 20:44):
Why does ext
work better than simp
here?
import Mathlib
def ShortList (α : Type u) (n : ℕ) :=
{ l : List α // l.length ≤ n }
abbrev ShortList' (α : Type u) (n : ℕ) := (i : Fin (n + 1)) × List.Vector α i
def ShortList_iso (α : Type u) (n : ℕ) : ShortList' α n ≃ ShortList α n where
toFun a := ⟨a.snd.toList, a.snd.toList_length.symm ▸ a.fst.is_le⟩
invFun b := ⟨⟨b.val.length, Nat.lt_add_one_of_le b.prop⟩, ⟨b.val, rfl⟩⟩
left_inv a := by
ext i j
· simp
· simp [List.Vector.toList]
right_inv b := rfl
instance ShortList'.fintype [Fintype α] {n : ℕ} : Fintype (ShortList' α n) :=
inferInstance
instance ShortList.fintype [Fintype α] {n : ℕ} : Fintype (ShortList α n) :=
Fintype.ofEquiv _ (ShortList_iso α n)
Violeta Hernández (Feb 17 2025 at 20:46):
Here's the proof I was talking about:
import Mathlib
variable {α β : Type*}
def preimageEquiv (f : β → α) (p : α → Prop) :
{x // p (f x)} ≃ (a : {a // p a}) ×' f ⁻¹' {a.1} where
toFun x := ⟨⟨f x.1, x.2⟩, ⟨x.1, rfl⟩⟩
invFun x := ⟨x.2, by convert x.1.2; exact x.2.2⟩
left_inv x := by simp
right_inv x := by
have : f x.2 = x.1 := x.2.2
ext
· simpa
· simp
instance (n : ℕ) [Fintype α] : Fintype { l : List α // l.length ≤ n } :=
let _ : Fintype { a // a ≤ n } :=
Fintype.ofFinset (p := Set.Iic n) (Finset.Iic n) (by simp)
let _ : Fintype ((a : { a // a ≤ n }) ×' ↑(List.length ⁻¹' {↑a})) :=
inferInstanceAs (Fintype ((a : { a // a ≤ n }) ×' List.Vector α a))
Fintype.ofEquiv _ (preimageEquiv List.length (· ≤ n)).symm
Violeta Hernández (Feb 17 2025 at 20:47):
(there's probably some better way to get the Fintype { a // a ≤ n }
instance)
Kyle Miller (Feb 17 2025 at 20:48):
Violeta Hernández said:
That's a shame, I do think the
Fintype
instance could still be useful.
There's nothing about this that means that there couldn't also be a Fintype
instance. I'd suggest adding it to the same file with the Set.Finite
lemmas.
Finite
instances don't suffice for Fintype
instances, since the Fintype
instance needs a real Fintype
instance for the element type to be computationally useful.
Violeta Hernández (Feb 17 2025 at 20:50):
Aaron Liu said:
Why does
ext
work better thansimp
here?
I think simp
has an easier time applying after some of the dependent type theory hell is dispatched via docs#PSigma.ext.
Violeta Hernández (Feb 17 2025 at 20:53):
At some point I think I wanted this exact Fintype
instance for my Rubik's cube project, so I'd be glad if we could PR that alongside the Finite
instance.
Kyle Miller (Feb 17 2025 at 20:56):
The pattern can be (1) define the Fintype
instance and (2) make use of Fintype.ofFinite
to apply the instance for the Finite
instance.
This was done a lot in the core Finite
instances, which, at least at some point, were defined in terms of corresponding Fintype
instances.
Jacob Weightman (Feb 18 2025 at 23:09):
Wow, if only I'd found List.finite_length_le
before! Finite
is definitely sufficient to apply the relevant pigeonhole principle theorems. I ended up with this very economical code:
def ShortList (α : Type u) (n : ℕ) :=
{ l : List α // l.length ≤ n }
instance ShortList.finite {α : Type u}{n : ℕ} [Finite α] : Finite (ShortList α n) :=
List.finite_length_le α n
Thanks so much for all the good discussion, I definitely learned some new things!
Matt Diamond (Feb 18 2025 at 23:38):
you might want to write it as (List.finite_length_le α n).to_subtype
to avoid things breaking if the definition of Set.Finite
changes (this is what people mean when they talk about "defeq abuse"), but it's also not a big deal, especially for a personal project
Last updated: May 02 2025 at 03:31 UTC