Skip to content

Commit a9d6f7d

Browse files
ctchouchenson2018
andauthored
feat: prove that omega-regular languages are closed under left-concatenation by regular languages (#216)
This patch proves that the concatenation of a regular language and an omega-regular language is omega-regular. The construction in `NA/Concat.lean` (perhaps with some modification) should work for proving that the concatenation of two regular languages is regular as well. But that is for future work. --------- Co-authored-by: Chris Henson <chrishenson.net@gmail.com>
1 parent 01d43b6 commit a9d6f7d

5 files changed

Lines changed: 156 additions & 2 deletions

File tree

Cslib.lean

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import Cslib.Computability.Automata.EpsilonNA.ToNA
1111
import Cslib.Computability.Automata.NA.Basic
1212
import Cslib.Computability.Automata.NA.BuchiEquiv
1313
import Cslib.Computability.Automata.NA.BuchiInter
14+
import Cslib.Computability.Automata.NA.Concat
1415
import Cslib.Computability.Automata.NA.Hist
1516
import Cslib.Computability.Automata.NA.Prod
1617
import Cslib.Computability.Automata.NA.Sum
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
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

Cslib/Computability/Languages/OmegaRegularLanguage.lean

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,20 @@ Authors: Ching-Tsun Chou
77
import Cslib.Computability.Automata.DA.Buchi
88
import Cslib.Computability.Automata.NA.BuchiEquiv
99
import Cslib.Computability.Automata.NA.BuchiInter
10+
import Cslib.Computability.Automata.NA.Concat
1011
import Cslib.Computability.Automata.NA.Sum
1112
import Cslib.Computability.Languages.ExampleEventuallyZero
1213
import Cslib.Computability.Languages.RegularLanguage
1314
import Mathlib.Data.Finite.Sigma
15+
import Mathlib.Data.Finite.Sum
1416

1517
/-!
1618
# ω-Regular languages
1719
1820
This file defines ω-regular languages and proves some properties of them.
1921
-/
2022

21-
open Set Function Filter Cslib.ωSequence Cslib.Automata ωAcceptor
23+
open Set Sum Filter Cslib.ωSequence Cslib.Automata ωAcceptor
2224
open scoped Computability Cslib.Automata.NA.Run
2325

2426
universe u v
@@ -163,6 +165,18 @@ theorem IsRegular.iInf {I : Type*} [Finite I] {s : Set I} {p : I → ωLanguage
163165
rw [iInf_insert]
164166
grind [IsRegular.inf]
165167

168+
/-- The concatenation of a regular language and an ω-regular language is ω-regular. -/
169+
@[simp]
170+
theorem IsRegular.hmul {l : Language Symbol} {p : ωLanguage Symbol}
171+
(h1 : l.IsRegular) (h2 : p.IsRegular) : (l * p).IsRegular := by
172+
obtain ⟨State1, h_fin1, ⟨na1, acc1⟩, rfl⟩ := Language.IsRegular.iff_nfa.mp h1
173+
obtain ⟨State2, h_fin1, ⟨na2, acc2⟩, rfl⟩ := h2
174+
let State := State1 ⊕ State2
175+
let na := NA.concat ⟨na1, acc1⟩ na2
176+
let acc : Set State := inr '' acc2
177+
use State, inferInstance, ⟨na, acc⟩
178+
rw [NA.Buchi.concat_language_eq]
179+
166180
/-- McNaughton's Theorem. -/
167181
proof_wanted IsRegular.iff_da_muller {p : ωLanguage Symbol} :
168182
p.IsRegular ↔

Cslib/Foundations/Data/OmegaSequence/Temporal.lean

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
44
Authors: Ching-Tsun Chou
55
-/
66

7-
import Cslib.Foundations.Data.OmegaSequence.Defs
7+
import Cslib.Foundations.Data.OmegaSequence.Init
88
import Mathlib.Order.Filter.AtTopBot.Basic
99

1010
/-!
@@ -87,4 +87,15 @@ theorem frequently_leadsTo_frequently {p q : Set α}
8787
have := h1 k0
8888
grind
8989

90+
theorem drop_frequently_iff_frequently {p : Set α} (n : ℕ) :
91+
(∃ᶠ k in atTop, (xs.drop n) k ∈ p) ↔ (∃ᶠ k in atTop, xs k ∈ p) := by
92+
simp only [frequently_atTop, get_drop]
93+
constructor
94+
· intro h m
95+
grind [h m]
96+
· intro h m
97+
obtain ⟨k, _⟩ := h (m + n)
98+
use k - n
99+
grind
100+
90101
end Cslib.ωSequence

Cslib/Foundations/Semantics/LTS/Basic.lean

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,20 @@ theorem LTS.MTr.nil_eq (h : lts.MTr s1 [] s2) : s1 = s2 := by
127127
cases h
128128
rfl
129129

130+
/-- For every multi-step transition, there exists a sequence of intermediate states
131+
which satisfies the single-step transition at every step. -/
132+
theorem LTS.MTr.exists_states {lts : LTS State Label} {s1 s2 : State} {μs : List Label}
133+
(h : lts.MTr s1 μs s2) : ∃ ss : List State, ∃ _ : ss.length = μs.length + 1,
134+
ss[0] = s1 ∧ ss[μs.length] = s2 ∧ ∀ k, ∀ _ : k < μs.length, lts.Tr ss[k] μs[k] ss[k + 1] := by
135+
induction h
136+
case refl t =>
137+
use [t]
138+
grind
139+
case stepL t1 μ t2 μs t3 h_tr h_mtr h_ind =>
140+
obtain ⟨ss', _, _, _, _⟩ := h_ind
141+
use [t1] ++ ss'
142+
grind
143+
130144
/-- A state `s1` can reach a state `s2` if there exists a multi-step transition from
131145
`s1` to `s2`. -/
132146
@[scoped grind =]

0 commit comments

Comments
 (0)