|
| 1 | +/- |
| 2 | +Copyright (c) 2025 Ching-Tsun Chou. All rights reserved. |
| 3 | +Released under Apache 2.0 license as described in the file LICENSE. |
| 4 | +Authors: Ching-Tsun Chou |
| 5 | +-/ |
| 6 | + |
| 7 | +import Cslib.Computability.Automata.NA.Basic |
| 8 | +import Cslib.Foundations.Data.OmegaSequence.Temporal |
| 9 | + |
| 10 | +/-! # Concatenation of nondeterministic automata. -/ |
| 11 | + |
| 12 | +namespace Cslib.Automata.NA |
| 13 | + |
| 14 | +open Sum ωSequence Acceptor |
| 15 | +open scoped Run LTS |
| 16 | + |
| 17 | +variable {Symbol State1 State2 : Type*} |
| 18 | + |
| 19 | +open scoped Classical in |
| 20 | +/-- `concat na1 na2` starts by running `na1` and then nondeterministically switches to `na2` |
| 21 | +by identifying an accepting state of `na1` with an initial state of `na2`. If `na1` accepts the |
| 22 | +empty word, it may also start running `na2` from the beginning. Once it starts running `na2`, |
| 23 | +it cannot go back to `na1`. -/ |
| 24 | +def concat (na1 : FinAcc State1 Symbol) (na2 : NA State2 Symbol) : NA (State1 ⊕ State2) Symbol where |
| 25 | + Tr s x t := match s, t with |
| 26 | + | inl s1, inl t1 => na1.Tr s1 x t1 |
| 27 | + | inl s1, inr t2 => ∃ t1, na1.Tr s1 x t1 ∧ t1 ∈ na1.accept ∧ t2 ∈ na2.start |
| 28 | + | inr s2, inr t2 => na2.Tr s2 x t2 |
| 29 | + | inr _, inl _ => False |
| 30 | + start := inl '' na1.start ∪ if [] ∈ language na1 then inr '' na2.start else ∅ |
| 31 | + |
| 32 | +variable {na1 : FinAcc State1 Symbol} {na2 : NA State2 Symbol} |
| 33 | + |
| 34 | +/-- A run of `concat na1 na2` containing at least one `na2` state is the concatenation of |
| 35 | +an accepting finite run of `na1` followed by a run of `na2`. -/ |
| 36 | +theorem concat_run_proj {xs : ωSequence Symbol} {ss : ωSequence (State1 ⊕ State2)} |
| 37 | + (hc : (concat na1 na2).Run xs ss) (hr : ∃ k, (ss k).isRight) : |
| 38 | + ∃ n, xs.take n ∈ language na1 ∧ ∃ ss2, na2.Run (xs.drop n) ss2 ∧ ss.drop n = ss2.map inr := by |
| 39 | + let n := Nat.find hr |
| 40 | + have h1 k (h_k : k < n := by grind) : ∃ s1, ss k = inl s1 := |
| 41 | + isLeft_iff.mp <| not_isRight.mp <| Nat.find_min hr h_k |
| 42 | + refine ⟨n, ?_, ?_⟩ |
| 43 | + · by_cases h_n : n = 0 |
| 44 | + · grind [concat] |
| 45 | + · choose ss1 h_ss1 using @h1 |
| 46 | + have h_init : ss1 0 ∈ na1.start := by grind [concat] |
| 47 | + have h_mtr k (h_k : k < n := by grind) : na1.MTr (ss1 0) (xs.take k) (ss1 k h_k) := by |
| 48 | + induction k |
| 49 | + case zero => grind |
| 50 | + case succ k h_ind => |
| 51 | + have h_tr : na1.Tr (ss1 k) (xs k) (ss1 (k + 1)) := by grind [concat, hc.trans k] |
| 52 | + grind [LTS.MTr.stepR na1.toLTS (h_ind ?_) h_tr] |
| 53 | + obtain ⟨t1, h_tr, _⟩ : |
| 54 | + ∃ t1, na1.Tr (ss1 (n - 1)) (xs (n - 1)) t1 ∧ t1 ∈ na1.accept := by |
| 55 | + grind only [concat, hc.trans (n - 1), Nat.find_spec, take_zero, isRight_inl] |
| 56 | + use ss1 0, h_init, t1 |
| 57 | + grind [LTS.MTr.stepR na1.toLTS (h_mtr (n - 1)) h_tr] |
| 58 | + · have h2 k : ∃ s2, ss (n + k) = inr s2 := by |
| 59 | + induction k |
| 60 | + case zero => grind [isRight_iff] |
| 61 | + case succ k h_ind => grind [concat, hc.trans (n + k)] |
| 62 | + choose ss2 h_ss2 using h2 |
| 63 | + refine ⟨ss2, Run.mk ?_ ?_, by grind⟩ |
| 64 | + · by_cases h_n : n = 0 |
| 65 | + · grind [concat] |
| 66 | + · obtain ⟨s1, _⟩ := h1 (n - 1) |
| 67 | + grind [concat, hc.trans (n - 1)] |
| 68 | + · intro k |
| 69 | + grind [concat, hc.trans (n + k)] |
| 70 | + |
| 71 | +/-- Given an accepting finite run of `na1` and a run of `na2`, there exists a run of |
| 72 | +`concat na1 na2` that is the concatenation of the two runs. -/ |
| 73 | +theorem concat_run_exists {xs1 : List Symbol} {xs2 : ωSequence Symbol} {ss2 : ωSequence State2} |
| 74 | + (h1 : xs1 ∈ language na1) (h2 : na2.Run xs2 ss2) : |
| 75 | + ∃ ss, (concat na1 na2).Run (xs1 ++ω xs2) ss ∧ ss.drop xs1.length = ss2.map inr := by |
| 76 | + by_cases h_xs1 : xs1.length = 0 |
| 77 | + · obtain ⟨rfl⟩ : xs1 = [] := List.eq_nil_iff_length_eq_zero.mpr h_xs1 |
| 78 | + refine ⟨ss2.map inr, by grind [concat], by simp⟩ |
| 79 | + · obtain ⟨s0, _, _, _, h_mtr⟩ := h1 |
| 80 | + obtain ⟨ss1, _, _, _, _⟩ := LTS.MTr.exists_states h_mtr |
| 81 | + let ss := (ss1.map inl).take xs1.length ++ω ss2.map inr |
| 82 | + refine ⟨ss, Run.mk ?_ ?_, ?_⟩ |
| 83 | + · grind [concat, get_append_left] |
| 84 | + · have (k) (h_k : ¬ k < xs1.length) : k + 1 - xs1.length = k - xs1.length + 1 := by grind |
| 85 | + grind [concat, get_append_right', get_append_left] |
| 86 | + · grind [drop_append_of_le_length] |
| 87 | + |
| 88 | +namespace Buchi |
| 89 | + |
| 90 | +open ωAcceptor Filter |
| 91 | + |
| 92 | +/-- The Buchi automaton formed from `concat na1 na2` accepts the ω-language that is |
| 93 | +the concatenation of the language of `na1` and the ω-language of `na2`. -/ |
| 94 | +@[simp] |
| 95 | +theorem concat_language_eq {acc2 : Set State2} : |
| 96 | + language (Buchi.mk (concat na1 na2) (inr '' acc2)) = |
| 97 | + language na1 * language (Buchi.mk na2 acc2) := by |
| 98 | + ext xs |
| 99 | + constructor |
| 100 | + · rintro ⟨ss, h_run, h_acc⟩ |
| 101 | + have h_ex2 : ∃ k, (ss k).isRight := by grind [Frequently.exists h_acc] |
| 102 | + obtain ⟨n, h_acc1, ss2, h_run2, h_map2⟩ := concat_run_proj h_run h_ex2 |
| 103 | + use xs.take n, h_acc1, xs.drop n, ?_, by simp |
| 104 | + use ss2, h_run2 |
| 105 | + grind [(drop_frequently_iff_frequently n).mpr h_acc] |
| 106 | + · rintro ⟨xs1, h_xs1, xs2, ⟨ss2, h_run2, h_acc2⟩, rfl⟩ |
| 107 | + obtain ⟨ss, h_run, h_map⟩ := concat_run_exists h_xs1 h_run2 |
| 108 | + use ss, h_run |
| 109 | + apply (drop_frequently_iff_frequently xs1.length).mp |
| 110 | + grind |
| 111 | + |
| 112 | +end Buchi |
| 113 | + |
| 114 | +end Cslib.Automata.NA |
0 commit comments