Skip to content

Commit 81dcdf5

Browse files
committed
feat(Data/Seq): coinductive predicates for sequences (#28874)
* Prove `eq_of_bisim'` and `eq_of_bisim_strong` coinductive principles for equality. * Develop API for a predicate `∀ x ∈ s, p x` where `s : Seq α`, including coinductive principle `all_coind`. * Introduce the `Pairwise` predicate along with API and coinductive principles `Pairwise.coind` and `Pairwise.coind_trans`. * Introduce `ENat`-valued length of a sequence, `length'`, along with minimal API. * Move theorems about `length` from `Defs.lean` to `Basic.lean`. * Prove a coinductive principle for a predicate `a.length' ≤ b.length'`. All of these "coinductive principles" are stated in the form as similar as possible to inductive principles. Co-authored-by: Vasily Nesterov <vas.nesterov63@gmail.com>
1 parent 5502a86 commit 81dcdf5

3 files changed

Lines changed: 410 additions & 48 deletions

File tree

Mathlib/Data/Seq/Basic.lean

Lines changed: 340 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ Released under Apache 2.0 license as described in the file LICENSE.
44
Authors: Mario Carneiro, Vasilii Nesterov
55
-/
66
import Mathlib.Data.Seq.Defs
7+
import Mathlib.Data.ENat.Basic
8+
import Mathlib.Tactic.ENatToNat
9+
import Mathlib.Tactic.ApplyFun
710

811
/-!
912
# Basic properties of sequences (possibly infinite lists)
@@ -20,6 +23,101 @@ namespace Seq
2023

2124
variable {α : Type u} {β : Type v} {γ : Type w}
2225

26+
section length
27+
28+
theorem length'_of_terminates {s : Seq α} (h : s.Terminates) :
29+
s.length' = s.length h := by
30+
simp [length', h]
31+
32+
theorem length'_of_not_terminates {s : Seq α} (h : ¬ s.Terminates) :
33+
s.length' = ⊤ := by
34+
simp [length', h]
35+
36+
@[simp]
37+
theorem length_nil : length (nil : Seq α) terminates_nil = 0 := rfl
38+
39+
@[simp]
40+
theorem length'_nil : length' (nil : Seq α) = 0 := by
41+
simp -implicitDefEqProofs [length']
42+
43+
theorem length_cons {x : α} {s : Seq α} (h : s.Terminates) :
44+
(cons x s).length (terminates_cons_iff.mpr h) = s.length h + 1 := by
45+
apply Nat.find_comp_succ
46+
simp
47+
48+
@[simp]
49+
theorem length'_cons (x : α) (s : Seq α) :
50+
(cons x s).length' = s.length' + 1 := by
51+
by_cases h : (cons x s).Terminates <;> have h' := h <;> rw [terminates_cons_iff] at h'
52+
· simp [length'_of_terminates h, length'_of_terminates h', length_cons h']
53+
· simp [length'_of_not_terminates h, length'_of_not_terminates h']
54+
55+
@[simp]
56+
theorem length_eq_zero {s : Seq α} {h : s.Terminates} :
57+
s.length h = 0 ↔ s = nil := by
58+
simp [length, TerminatedAt]
59+
60+
@[simp]
61+
theorem length'_eq_zero_iff_nil (s : Seq α) :
62+
s.length' = 0 ↔ s = nil := by
63+
cases s <;> simp
64+
65+
theorem length'_ne_zero_iff_cons (s : Seq α) :
66+
s.length' ≠ 0 ↔ ∃ x s', s = cons x s' := by
67+
cases s <;> simp
68+
69+
/-- The statement of `length_le_iff'` does not assume that the sequence terminates. For a
70+
simpler statement of the theorem where the sequence is known to terminate see `length_le_iff`. -/
71+
theorem length_le_iff' {s : Seq α} {n : ℕ} :
72+
(∃ h, s.length h ≤ n) ↔ s.TerminatedAt n := by
73+
simp only [length, Nat.find_le_iff, TerminatedAt, Terminates, exists_prop]
74+
refine ⟨?_, ?_⟩
75+
· rintro ⟨_, k, hkn, hk⟩
76+
exact le_stable s hkn hk
77+
· intro hn
78+
exact ⟨⟨n, hn⟩, ⟨n, le_rfl, hn⟩⟩
79+
80+
/-- The statement of `length_le_iff` assumes that the sequence terminates. For a
81+
statement of the where the sequence is not known to terminate see `length_le_iff'`. -/
82+
theorem length_le_iff {s : Seq α} {n : ℕ} {h : s.Terminates} :
83+
s.length h ≤ n ↔ s.TerminatedAt n := by
84+
rw [← length_le_iff']; simp [h]
85+
86+
theorem length'_le_iff {s : Seq α} {n : ℕ} :
87+
s.length' ≤ n ↔ s.TerminatedAt n := by
88+
by_cases h : s.Terminates
89+
· simpa [length'_of_terminates h] using length_le_iff
90+
· simpa [length'_of_not_terminates h] using forall_not_of_not_exists h n
91+
92+
/-- The statement of `lt_length_iff'` does not assume that the sequence terminates. For a
93+
simpler statement of the theorem where the sequence is known to terminate see `lt_length_iff`. -/
94+
theorem lt_length_iff' {s : Seq α} {n : ℕ} :
95+
(∀ h : s.Terminates, n < s.length h) ↔ ∃ a, a ∈ s.get? n := by
96+
simp only [Terminates, TerminatedAt, length, Nat.lt_find_iff, forall_exists_index, Option.mem_def,
97+
← Option.ne_none_iff_exists', ne_eq]
98+
refine ⟨?_, ?_⟩
99+
· intro h hn
100+
exact h n hn n le_rfl hn
101+
· intro hn _ _ k hkn hk
102+
exact hn <| le_stable s hkn hk
103+
104+
/-- The statement of `length_le_iff` assumes that the sequence terminates. For a
105+
statement of the where the sequence is not known to terminate see `length_le_iff'`. -/
106+
theorem lt_length_iff {s : Seq α} {n : ℕ} {h : s.Terminates} :
107+
n < s.length h ↔ ∃ a, a ∈ s.get? n := by
108+
rw [← lt_length_iff']; simp [h]
109+
110+
theorem lt_length'_iff {s : Seq α} {n : ℕ} :
111+
n < s.length' ↔ ∃ a, a ∈ s.get? n := by
112+
by_cases h : s.Terminates
113+
· simpa [length'_of_terminates h] using lt_length_iff
114+
· simp only [length'_of_not_terminates h, ENat.coe_lt_top, Option.mem_def, true_iff]
115+
rw [not_terminates_iff] at h
116+
rw [← Option.isSome_iff_exists]
117+
exact h n
118+
119+
end length
120+
23121
section OfStream
24122

25123
@[simp]
@@ -69,8 +167,7 @@ theorem getElem?_take : ∀ (n k : ℕ) (s : Seq α),
69167
rw [destruct_eq_cons h]
70168
match n with
71169
| 0 => simp
72-
| n+1 =>
73-
simp [List.getElem?_cons_succ, Nat.add_lt_add_iff_right, getElem?_take]
170+
| n+1 => simp [List.getElem?_cons_succ, getElem?_take]
74171

75172
theorem get?_mem_take {s : Seq α} {m n : ℕ} (h_mn : m < n) {x : α}
76173
(h_get : s.get? m = some x) : x ∈ s.take n := by
@@ -285,6 +382,13 @@ theorem length_map {s : Seq α} {f : α → β} (h : (s.map f).Terminates) :
285382
ext
286383
simp
287384

385+
@[simp]
386+
theorem length'_map {s : Seq α} {f : α → β} :
387+
(s.map f).length' = s.length' := by
388+
by_cases h : (s.map f).Terminates <;> have h' := h <;> rw [terminates_map_iff] at h'
389+
· rw [length'_of_terminates h, length'_of_terminates h', length_map h]
390+
· rw [length'_of_not_terminates h, length'_of_not_terminates h']
391+
288392
theorem mem_map (f : α → β) {a : α} : ∀ {s : Seq α}, a ∈ s → f a ∈ map f s
289393
| ⟨_, _⟩ => Stream'.mem_map (Option.map f)
290394

@@ -397,6 +501,9 @@ theorem head_dropn (s : Seq α) (n) : head (drop s n) = get? s n := by
397501
| zero => rfl
398502
| succ n IH => rw [← get?_tail, ← dropn_tail]; apply IH
399503

504+
@[simp]
505+
theorem drop_zero {s : Seq α} : s.drop 0 = s := rfl
506+
400507
@[simp]
401508
theorem drop_succ_cons {x : α} {s : Seq α} {n : ℕ} :
402509
(cons x s).drop (n + 1) = s.drop n := by
@@ -408,6 +515,21 @@ theorem drop_nil {n : ℕ} : (@nil α).drop n = nil := by
408515
| zero => simp [drop]
409516
| succ m ih => simp [← dropn_tail, ih]
410517

518+
@[simp]
519+
theorem drop_length' {n : ℕ} {s : Seq α} :
520+
(s.drop n).length' = s.length' - n := by
521+
cases n with
522+
| zero => simp
523+
| succ n =>
524+
cases s with
525+
| nil => simp
526+
| cons x s =>
527+
simp only [drop_succ_cons, length'_cons, Nat.cast_add, Nat.cast_one]
528+
convert drop_length' using 1
529+
generalize s.length' = m
530+
enat_to_nat
531+
omega
532+
411533
theorem take_drop {s : Seq α} {n m : ℕ} :
412534
(s.take n).drop m = (s.drop m).take (n - m) := by
413535
induction m generalizing n s with
@@ -604,6 +726,222 @@ theorem drop_set_of_lt (s : Seq α) {m n : ℕ} (h : m < n) : (s.set m x).drop n
604726

605727
end Update
606728

729+
section All
730+
731+
theorem all_cons {p : α → Prop} {hd : α} {tl : Seq α} (h_hd : p hd) (h_tl : ∀ x ∈ tl, p x) :
732+
(∀ x ∈ (cons hd tl), p x) := by
733+
simp only [mem_cons_iff, forall_eq_or_imp] at *
734+
exact ⟨h_hd, h_tl⟩
735+
736+
theorem all_get {p : α → Prop} {s : Seq α} (h : ∀ x ∈ s, p x) {n : ℕ} {x : α}
737+
(hx : s.get? n = .some x) :
738+
p x := by
739+
exact h _ (get?_mem hx)
740+
741+
theorem all_of_get {p : α → Prop} {s : Seq α} (h : ∀ n x, s.get? n = .some x → p x) :
742+
∀ x ∈ s, p x := by
743+
simp only [mem_iff_exists_get?]
744+
grind
745+
746+
private lemma all_coind_drop_motive {s : Seq α} (motive : Seq α → Prop) (base : motive s)
747+
(step : ∀ hd tl, motive (.cons hd tl) → motive tl) (n : ℕ) :
748+
motive (s.drop n) := by
749+
induction n with
750+
| zero => simpa
751+
| succ m ih =>
752+
simp only [drop]
753+
generalize s.drop m = t at *
754+
cases t
755+
· simpa
756+
· exact step _ _ ih
757+
758+
/-- Coinductive principle for `All`. -/
759+
theorem all_coind {s : Seq α} {p : α → Prop}
760+
(motive : Seq α → Prop) (base : motive s)
761+
(step : ∀ hd tl, motive (.cons hd tl) → p hd ∧ motive tl) :
762+
∀ x ∈ s, p x := by
763+
apply all_of_get
764+
intro n
765+
have := all_coind_drop_motive motive base (fun hd tl ih ↦ (step hd tl ih).right) n
766+
rw [← head_dropn]
767+
generalize s.drop n = s' at this
768+
cases s' with
769+
| nil => simp
770+
| cons hd tl => simp [(step hd tl this).left]
771+
772+
theorem map_all_iff {β : Type u} {f : α → β} {p : β → Prop} {s : Seq α} :
773+
(∀ x ∈ (s.map f), p x) ↔ (∀ x ∈ s, (p ∘ f) x) := by
774+
refine ⟨fun _ _ hx ↦ ?_, fun _ _ hx ↦ ?_⟩
775+
· solve_by_elim [mem_map f hx]
776+
· obtain ⟨_, _, hx'⟩ := exists_of_mem_map hx
777+
rw [← hx']
778+
solve_by_elim
779+
780+
theorem take_all {s : Seq α} {p : α → Prop} (h_all : ∀ x ∈ s, p x) {n : ℕ} {x : α}
781+
(hx : x ∈ s.take n) : p x := by
782+
induction n generalizing s with
783+
| zero => simp [take] at hx
784+
| succ m ih =>
785+
cases s with
786+
| nil => simp at hx
787+
| cons hd tl =>
788+
simp only [take_succ_cons, List.mem_cons, mem_cons_iff, forall_eq_or_imp] at hx h_all
789+
rcases hx with (rfl | hx)
790+
exacts [h_all.left, ih h_all.right hx]
791+
792+
theorem set_all {p : α → Prop} {s : Seq α} (h_all : ∀ x ∈ s, p x) {n : ℕ} {x : α}
793+
(hx : p x) : ∀ y ∈ (s.set n x), p y := by
794+
intro y hy
795+
simp only [mem_iff_exists_get?] at hy
796+
obtain ⟨m, hy⟩ := hy
797+
rcases eq_or_ne n m with (rfl | h_nm)
798+
· by_cases h_term : s.TerminatedAt n
799+
· simp [get?_set_of_terminatedAt _ h_term] at hy
800+
· simp_all [get?_set_of_not_terminatedAt _ h_term]
801+
· rw [get?_set_of_ne _ _ h_nm.symm] at hy
802+
apply h_all _ (get?_mem hy.symm)
803+
804+
end All
805+
806+
section Pairwise
807+
808+
@[simp]
809+
theorem Pairwise.nil {R : α → α → Prop} : Pairwise R (@nil α) := by
810+
simp [Pairwise]
811+
812+
theorem Pairwise.cons {R : α → α → Prop} {hd : α} {tl : Seq α}
813+
(h_hd : ∀ x ∈ tl, R hd x)
814+
(h_tl : Pairwise R tl) : Pairwise R (cons hd tl) := by
815+
simp only [Pairwise] at *
816+
intro i j h_ij x hx y hy
817+
cases j with
818+
| zero => simp at h_ij
819+
| succ k =>
820+
simp only [get?_cons_succ] at hy
821+
cases i with
822+
| zero =>
823+
simp only [get?_cons_zero, Option.mem_def, Option.some.injEq] at hx
824+
exact hx ▸ all_get h_hd hy
825+
| succ n => exact h_tl n k (by omega) x hx y hy
826+
827+
theorem Pairwise.cons_elim {R : α → α → Prop} {hd : α} {tl : Seq α}
828+
(h : Pairwise R (.cons hd tl)) : (∀ x ∈ tl, R hd x) ∧ Pairwise R tl := by
829+
simp only [Pairwise] at *
830+
refine ⟨?_, fun i j h_ij ↦ h (i + 1) (j + 1) (by omega)⟩
831+
intro x hx
832+
rw [mem_iff_exists_get?] at hx
833+
obtain ⟨n, hx⟩ := hx
834+
simpa [← hx] using h 0 (n + 1) (by omega)
835+
836+
@[simp]
837+
theorem Pairwise_cons_nil {R : α → α → Prop} {hd : α} : Pairwise R (cons hd nil) := by
838+
apply Pairwise.cons <;> simp
839+
840+
theorem Pairwise_cons_cons_head {R : α → α → Prop} {hd tl_hd : α} {tl_tl : Seq α}
841+
(h : Pairwise R (cons hd (cons tl_hd tl_tl))) :
842+
R hd tl_hd := by
843+
simp only [Pairwise] at h
844+
simpa using h 0 1 Nat.one_pos
845+
846+
theorem Pairwise.cons_cons_of_trans {R : α → α → Prop} [IsTrans _ R] {hd tl_hd : α} {tl_tl : Seq α}
847+
(h_hd : R hd tl_hd)
848+
(h_tl : Pairwise R (.cons tl_hd tl_tl)) : Pairwise R (.cons hd (.cons tl_hd tl_tl)) := by
849+
apply Pairwise.cons _ h_tl
850+
simp only [mem_cons_iff, forall_eq_or_imp]
851+
exact ⟨h_hd, fun x hx ↦ Trans.simple h_hd ((cons_elim h_tl).left x hx)⟩
852+
853+
854+
/-- Coinductive principle for `Pairwise`. -/
855+
theorem Pairwise.coind {R : α → α → Prop} {s : Seq α}
856+
(motive : Seq α → Prop) (base : motive s)
857+
(step : ∀ hd tl, motive (.cons hd tl) → (∀ x ∈ tl, R hd x) ∧ motive tl) : Pairwise R s := by
858+
simp only [Pairwise]
859+
intro i j h_ij x hx y hy
860+
obtain ⟨k, hj⟩ := Nat.exists_eq_add_of_lt h_ij
861+
rw [← head_dropn] at hx
862+
rw [hj, ← head_dropn, Nat.add_assoc, dropn_add, head_dropn] at hy
863+
have := all_coind_drop_motive motive base (fun hd tl ih ↦ (step hd tl ih).right) i
864+
generalize s.drop i = s' at *
865+
cases s' with
866+
| nil => simp at hx
867+
| cons hd tl =>
868+
simp at hx hy
869+
exact hx ▸ all_get (step hd tl this).left hy
870+
871+
/-- Coinductive principle for `Pairwise` that assumes that `R` is transitive. Compared to
872+
`Pairwise.coind`, this allows you to prove `R hd tl.head` instead of `tl.All (R hd ·)` in `step`.
873+
-/
874+
theorem Pairwise.coind_trans {R : α → α → Prop} [IsTrans α R] {s : Seq α}
875+
(motive : Seq α → Prop) (base : motive s)
876+
(step : ∀ hd tl, motive (.cons hd tl) → (∀ x ∈ tl.head, R hd x) ∧ motive tl) :
877+
Pairwise R s := by
878+
have h_succ {n} {x y} (hx : s.get? n = some x) (hy : s.get? (n + 1) = some y) : R x y := by
879+
rw [← head_dropn] at hx
880+
have := all_coind_drop_motive motive base (fun hd tl ih ↦ (step hd tl ih).right)
881+
exact (step x (s.drop (n + 1)) (head_eq_some hx ▸ this n)).left _ (by simpa)
882+
simp only [Pairwise]
883+
intro i j h_ij x hx y hy
884+
obtain ⟨k, rfl⟩ := Nat.exists_eq_add_of_lt h_ij
885+
clear h_ij
886+
induction k generalizing y with
887+
| zero => exact h_succ hx hy
888+
| succ k ih =>
889+
obtain ⟨z, hz⟩ := ge_stable (m := i + k + 1) _ (by omega) hy
890+
exact _root_.trans (ih z hz) <| h_succ hz hy
891+
892+
theorem Pairwise_tail {R : α → α → Prop} {s : Seq α} (h : s.Pairwise R) :
893+
s.tail.Pairwise R := by
894+
cases s
895+
· simp
896+
· simp [h.cons_elim.right]
897+
898+
theorem Pairwise_drop {R : α → α → Prop} {s : Seq α} (h : s.Pairwise R) {n : ℕ} :
899+
(s.drop n).Pairwise R := by
900+
induction n with
901+
| zero => simpa
902+
| succ m ih => simp [drop, Pairwise_tail ih]
903+
904+
end Pairwise
905+
906+
/-- Coinductive principle for proving `b.length' ≤ a.length'` for two sequences `a` and `b`. -/
907+
theorem at_least_as_long_as_coind {a : Seq α} {b : Seq β}
908+
(motive : Seq α → Seq β → Prop) (base : motive a b)
909+
(step : ∀ a b, motive a b →
910+
(∀ b_hd b_tl, (b = .cons b_hd b_tl) → ∃ a_hd a_tl, a = .cons a_hd a_tl ∧ motive a_tl b_tl)) :
911+
b.length' ≤ a.length' := by
912+
have (n) (hb : b.drop n ≠ .nil) : motive (a.drop n) (b.drop n) := by
913+
induction n with
914+
| zero => simpa
915+
| succ m ih =>
916+
simp only [drop] at hb ⊢
917+
generalize b.drop m = tb at *
918+
cases tb with
919+
| nil => simp at hb
920+
| cons tb_hd tb_tl =>
921+
simp only [ne_eq, cons_ne_nil, not_false_eq_true, forall_const] at ih
922+
obtain ⟨a_hd, a_tl, ha, h_tail⟩ := step (a.drop m) (.cons tb_hd tb_tl) ih _ _ rfl
923+
simpa [ha]
924+
by_cases ha : a.Terminates; swap
925+
· simp [length'_of_not_terminates ha]
926+
simp [length'_of_terminates ha, length'_le_iff]
927+
by_contra! hb
928+
have hb_cons : b.drop (a.length ha) ≠ .nil := by
929+
intro hb'
930+
simp only [← length'_eq_zero_iff_nil, drop_length', tsub_eq_zero_iff_le, length'_le_iff] at hb'
931+
contradiction
932+
specialize this (a.length ha) hb_cons
933+
generalize b.drop (a.length ha) = b' at *
934+
cases b' with
935+
| nil =>
936+
contradiction
937+
| cons b_hd b_tl =>
938+
obtain ⟨a_hd, a_tl, ha', _⟩ := step _ _ this _ _ rfl
939+
apply_fun length' at ha'
940+
simp only [drop_length', length'_of_terminates ha, tsub_self, length'_cons] at ha'
941+
generalize a_tl.length' = u at ha'
942+
enat_to_nat
943+
omega
944+
607945
instance : Functor Seq where map := @map
608946

609947
instance : LawfulFunctor Seq where

0 commit comments

Comments
 (0)