Skip to content

Commit c7cd377

Browse files
committed
feat(Tactic/ComputeAsymptotics/Multiseries): non-primitive corecursion for Seq: FriendlyOperation API (#35072)
This is a continuation of #34311. This PR adds more API about friendly operations: * Basic friendly operations constructions * `FriendlyOperation.destruct`: a "coinductive destructor" for `FriendlyOperation`
1 parent 507f3af commit c7cd377

File tree

1 file changed

+125
-2
lines changed

1 file changed

+125
-2
lines changed

Mathlib/Tactic/ComputeAsymptotics/Multiseries/Corecursion.lean

Lines changed: 125 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ where f is friendly.
4242
are friendly.
4343
* `gcorec`: a generalization of `Seq.corec` that allows a corecursive call to be guarded by
4444
a friendly function.
45+
* `FriendlyOperation.coind`, `FriendlyOperation.coind_comp_friend_left`,
46+
`FriendlyOperation.coind_comp_friend_right`: coinduction principles for proving that an operation
47+
is friendly.
48+
* `FriendlyOperation.eq_of_bisim`: a generalisation of `Seq.eq_of_bisim'` that allows using a
49+
friendly operation in the tail of the sequences.
4550
4651
## Implementation details
4752
@@ -163,6 +168,14 @@ theorem dist_nil_cons (x : α) (s : Seq α) : dist nil (cons x s) = 1 := by
163168
rw [dist_comm]
164169
simp
165170

171+
theorem dist_le_half_iff {s t : Seq α} :
172+
dist s t ≤ 2⁻¹ ↔ (s = .nil ∧ t = .nil) ∨ ∃ hd s' t', s = .cons hd s' ∧ t = .cons hd t' where
173+
mp h := by
174+
cases s <;> cases t <;> norm_num at h <;> simp
175+
grind [dist_cons_cons_eq_one]
176+
mpr h := by
177+
obtain ⟨rfl, rfl⟩ | ⟨hd, s', t', rfl, rfl⟩ := h <;> simp
178+
166179
/-- A function on sequences is called a "friend" if any `n`-prefix of its output depends only on
167180
the `n`-prefix of the input. Such functions can be used in the tail of (non-primitive) corecursive
168181
definitions. -/
@@ -172,6 +185,10 @@ def FriendlyOperation (op : Seq α → Seq α) : Prop := LipschitzWith 1 op
172185
class FriendlyOperationClass (F : γ → Seq α → Seq α) : Prop where
173186
friend : ∀ c : γ, FriendlyOperation (F c)
174187

188+
theorem friendlyOperation_iff_dist_le_dist (op : Seq α → Seq α) :
189+
FriendlyOperation op ↔ ∀ s t, dist (op s) (op t) ≤ dist s t := by
190+
simp [FriendlyOperation, lipschitzWith_iff_dist_le_mul]
191+
175192
theorem FriendlyOperation.id : FriendlyOperation (id : Seq α → Seq α) :=
176193
LipschitzWith.id
177194

@@ -183,7 +200,7 @@ theorem FriendlyOperation.comp {op op' : Seq α → Seq α}
183200
simp
184201

185202
theorem FriendlyOperation.const {s : Seq α} : FriendlyOperation (fun _ ↦ s) := by
186-
simp [FriendlyOperation, lipschitzWith_iff_dist_le_mul]
203+
simp [friendlyOperation_iff_dist_le_dist]
187204

188205
theorem FriendlyOperationClass.comp (F : γ → Seq α → Seq α) (g : γ' → γ)
189206
[h : FriendlyOperationClass F] : FriendlyOperationClass (fun c ↦ F (g c)) := by
@@ -193,7 +210,7 @@ theorem FriendlyOperation.ite {op₁ op₂ : Seq α → Seq α}
193210
(h₁ : FriendlyOperation op₁) (h₂ : FriendlyOperation op₂)
194211
{P : Option α → Prop} [DecidablePred P] :
195212
FriendlyOperation (fun s ↦ if P s.head then op₁ s else op₂ s) := by
196-
rw [FriendlyOperation, lipschitzWith_iff_dist_le_mul, NNReal.coe_one] at h₁ h₂ ⊢
213+
rw [friendlyOperation_iff_dist_le_dist] at h₁ h₂ ⊢
197214
intro s t
198215
by_cases! h_head : s.head ≠ t.head
199216
· simp [dist_eq_one_of_head h_head]
@@ -291,4 +308,110 @@ theorem gcorec_some {F : β → Option (α × γ × β)} {op : γ → Seq α →
291308
have := (FriendlyOperation.exists_fixed_point F op).choose_spec b
292309
simpa [h] using this
293310

311+
/-- The operation `cons hd ·` is friendly. -/
312+
theorem FriendlyOperation.cons (hd : α) : FriendlyOperation (cons hd) := by
313+
simp only [friendlyOperation_iff_dist_le_dist, dist_cons_cons]
314+
intro x y
315+
linarith [dist_nonneg (x := x) (y := y)]
316+
317+
/-- If two sequences have the same head and applying `op` reduces their distance, then
318+
it also reduces the distance of their tails. -/
319+
lemma dist_const_tail_cons_tail_le
320+
{op : Seq α → Seq α} {hd : α} {x y : Stream'.Seq α}
321+
(h : dist (op (cons hd x)) (op (cons hd y)) ≤ dist (cons hd x) (cons hd y)) :
322+
dist (op (cons hd x)).tail (op (cons hd y)).tail ≤ dist x y := by
323+
rwa [dist_cons_cons, dist_eq_half_of_head, mul_le_mul_iff_right₀ (by norm_num)] at h
324+
grw [dist_le_one x y, mul_one] at h
325+
obtain (⟨hx, hy⟩ | ⟨_, _, _, hx, hy⟩) := dist_le_half_iff.mp h <;> simp [hx, hy]
326+
327+
/-- The operation `(op (.cons hd ·)).tail` is friendly if `op` is friendly. -/
328+
theorem FriendlyOperation.cons_tail {op : Seq α → Seq α} {hd : α} (h : FriendlyOperation op) :
329+
FriendlyOperation (fun s ↦ (op (.cons hd s)).tail) := by
330+
simp_rw [friendlyOperation_iff_dist_le_dist] at h ⊢
331+
intro x y
332+
specialize h (.cons hd x) (.cons hd y)
333+
exact dist_const_tail_cons_tail_le h
334+
335+
/-- The first element of `op (a :: s)` depends only on `a`. -/
336+
theorem FriendlyOperation.op_cons_head_eq {op : Seq α → Seq α} (h : FriendlyOperation op) {a : α}
337+
{s t : Seq α} : (op <| .cons a s).head = (op <| .cons a t).head := by
338+
rw [friendlyOperation_iff_dist_le_dist] at h
339+
specialize h (.cons a s) (.cons a t)
340+
simp only [dist_cons_cons] at h
341+
replace h : dist (op (.cons a s)) (op (.cons a t)) ≤ 2⁻¹ := by
342+
apply h.trans
343+
simp
344+
rw [dist_le_half_iff] at h
345+
generalize op (Seq.cons a s) = s' at *
346+
generalize op (Seq.cons a t) = t' at *
347+
obtain ⟨rfl, rfl⟩ | ⟨hd, s_tl, t_tl, rfl, rfl⟩ := h <;> rfl
348+
349+
/-- Decomposes a friendly operation by the head of the input sequence. Returns `none` if the output
350+
is `nil`, or `some (out_hd, op')` where `out_hd` is the head of the output and `op'` is a friendly
351+
operation mapping the tail of the input to the tail of the output. See
352+
`destruct_apply_eq_unfold` for the correctness statement. -/
353+
def FriendlyOperation.unfold {op : Seq α → Seq α} (h : FriendlyOperation op) (hd? : Option α) :
354+
Option (α × Subtype (@FriendlyOperation α)) :=
355+
match hd? with
356+
| none =>
357+
match (op nil).destruct with
358+
| none => none
359+
| some (t_hd, t_tl) =>
360+
some (t_hd, ⟨fun _ ↦ t_tl, FriendlyOperation.const⟩)
361+
| some s_hd =>
362+
match (op <| .cons s_hd nil).destruct with
363+
| none => none
364+
| some (t_hd, _) =>
365+
some (t_hd, ⟨fun s_tl ↦ (op (.cons s_hd s_tl)).tail, FriendlyOperation.cons_tail h⟩)
366+
367+
set_option backward.isDefEq.respectTransparency false in
368+
/-- `unfold` correctly decomposes a friendly operation: the head of `op s` depends only on the
369+
head of `s` (and is given by `unfold`), while the tail of `op s` is obtained by applying the
370+
friendly operation returned by `unfold` to the tail of `s`. This gives a coinductive
371+
characterization of `FriendlyOperation`. For the coinduction principle, see
372+
`FriendlyOperation.coind`. -/
373+
theorem FriendlyOperation.destruct_apply_eq_unfold {op : Seq α → Seq α} (h : FriendlyOperation op)
374+
{s : Seq α} :
375+
destruct (op s) = (h.unfold s.head).map (fun (hd, op') => (hd, op'.val s.tail)) := by
376+
unfold unfold
377+
cases s with
378+
| nil =>
379+
generalize op nil = t
380+
cases t <;> simp
381+
| cons s_hd s_tl =>
382+
simp only [Seq.tail_cons, Seq.head_cons]
383+
generalize ht0 : op (.cons s_hd nil) = t0 at *
384+
generalize ht : op (.cons s_hd s_tl) = t at *
385+
have : t0.head = t.head := by
386+
rw [← ht0, ← ht, FriendlyOperation.op_cons_head_eq h]
387+
cases t0 with
388+
| nil =>
389+
cases t with
390+
| nil => simp
391+
| cons => simp at this
392+
| cons =>
393+
cases t with
394+
| nil => simp at this
395+
| cons => simp_all
396+
397+
set_option backward.isDefEq.respectTransparency false in
398+
/-- If `op` is friendly, then `op s` and `op t` have the same head if `s` and `t`
399+
have the same head. -/
400+
theorem FriendlyOperation.op_head_eq {op : Seq α → Seq α} (h : FriendlyOperation op) {s t : Seq α}
401+
(h_head : s.head = t.head) : (op s).head = (op t).head := by
402+
simp only [head_eq_destruct, Option.map_eq_map, h.destruct_apply_eq_unfold, Option.map_map]
403+
at h_head ⊢
404+
simp [h_head]
405+
rfl
406+
407+
theorem FriendlyOperation.of_dist_le_pow {op : Seq α → Seq α}
408+
(h : ∀ s t n, dist s t ≤ (2⁻¹ : ℝ) ^ n → dist (op s) (op t) ≤ (2⁻¹ : ℝ) ^ n) :
409+
FriendlyOperation op := by
410+
rw [friendlyOperation_iff_dist_le_dist]
411+
intro s t
412+
by_cases hst : s = t
413+
· simp [hst]
414+
obtain ⟨n, hst⟩ := dist_eq_two_inv_pow hst
415+
grind
416+
294417
end Tactic.ComputeAsymptotics.Seq

0 commit comments

Comments
 (0)