Skip to content
This repository was archived by the owner on Jul 24, 2024. It is now read-only.

Commit 07600ee

Browse files
committed
feat(computability/epsilon_nfa): epsilon-NFA definition (#6108)
Co-authored-by: foxthomson <11833933+foxthomson@users.noreply.github.com>
1 parent ac19b4a commit 07600ee

File tree

1 file changed

+131
-0
lines changed

1 file changed

+131
-0
lines changed

src/computability/epsilon_NFA.lean

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
/-
2+
Copyright (c) 2021 Fox Thomson. All rights reserved.
3+
Released under Apache 2.0 license as described in the file LICENSE.
4+
Authors: Fox Thomson
5+
-/
6+
7+
import computability.NFA
8+
9+
/-!
10+
# Epsilon Nondeterministic Finite Automata
11+
This file contains the definition of an epsilon Nondeterministic Finite Automaton (`ε_NFA`), a state
12+
machine which determines whether a string (implemented as a list over an arbitrary alphabet) is in a
13+
regular set by evaluating the string over every possible path, also having access to ε-transitons,
14+
which can be followed without reading a character.
15+
Since this definition allows for automata with infinite states, a `fintype` instance must be
16+
supplied for true `ε_NFA`'s.
17+
-/
18+
19+
universes u v
20+
21+
/-- An `ε_NFA` is a set of states (`σ`), a transition function from state to state labelled by the
22+
alphabet (`step`), a starting state (`start`) and a set of acceptance states (`accept`).
23+
Note the transition function sends a state to a `set` of states and can make ε-transitions by
24+
inputing `none`.
25+
Since this definition allows for Automata with infinite states, a `fintype` instance must be
26+
supplied for true `ε_NFA`'s.-/
27+
structure ε_NFA (α : Type u) (σ : Type v) :=
28+
(step : σ → option α → set σ)
29+
(start : set σ)
30+
(accept : set σ)
31+
32+
variables {α : Type u} {σ σ' : Type v} (M : ε_NFA α σ)
33+
34+
namespace ε_NFA
35+
36+
instance : inhabited (ε_NFA α σ) := ⟨ ε_NFA.mk (λ _ _, ∅) ∅ ∅ ⟩
37+
38+
/-- The `ε_closure` of a set is the set of states which can be reached by taking a finite string of
39+
ε-transitions from an element of the the set -/
40+
inductive ε_closure : set σ → set σ
41+
| base : ∀ S (s ∈ S), ε_closure S s
42+
| step : ∀ S s (t ∈ M.step s none), ε_closure S s → ε_closure S t
43+
44+
/-- `M.step_set S a` is the union of the ε-closure of `M.step s a` for all `s ∈ S`. -/
45+
def step_set : set σ → α → set σ :=
46+
λ S a, S >>= (λ s, M.ε_closure (M.step s a))
47+
48+
/-- `M.eval_from S x` computes all possible paths though `M` with input `x` starting at an element
49+
of `S`. -/
50+
def eval_from (start : set σ) : list α → set σ :=
51+
list.foldl M.step_set (M.ε_closure start)
52+
53+
/-- `M.eval x` computes all possible paths though `M` with input `x` starting at an element of
54+
`M.start`. -/
55+
def eval := M.eval_from M.start
56+
57+
/-- `M.accepts` is the language of `x` such that there is an accept state in `M.eval x`. -/
58+
def accepts : language α :=
59+
λ x, ∃ S ∈ M.accept, S ∈ M.eval x
60+
61+
/-- `M.to_NFA` is an `NFA` constructed from an `ε_NFA` `M`. -/
62+
def to_NFA : NFA α σ :=
63+
{ step := λ S a, M.ε_closure (M.step S a),
64+
start := M.ε_closure M.start,
65+
accept := M.accept }
66+
67+
@[simp] lemma to_NFA_eval_from_match (start : set σ) :
68+
M.to_NFA.eval_from (M.ε_closure start) = M.eval_from start := rfl
69+
70+
@[simp] lemma to_NFA_correct :
71+
M.to_NFA.accepts = M.accepts :=
72+
begin
73+
ext x,
74+
rw [accepts, NFA.accepts, eval, NFA.eval, ←to_NFA_eval_from_match],
75+
refl
76+
end
77+
78+
lemma pumping_lemma [fintype σ] {x : list α} (hx : x ∈ M.accepts)
79+
(hlen : fintype.card (set σ) + 1 ≤ list.length x) :
80+
∃ a b c, x = a ++ b ++ c ∧ a.length + b.length ≤ fintype.card (set σ) + 1 ∧ b ≠ [] ∧
81+
{a} * language.star {b} * {c} ≤ M.accepts :=
82+
begin
83+
rw ←to_NFA_correct at hx ⊢,
84+
exact M.to_NFA.pumping_lemma hx hlen
85+
end
86+
87+
end ε_NFA
88+
89+
namespace NFA
90+
91+
/-- `M.to_ε_NFA` is an `ε_NFA` constructed from an `NFA` `M` by using the same start and accept
92+
states and transition functions. -/
93+
def to_ε_NFA (M : NFA α σ) : ε_NFA α σ :=
94+
{ step := λ s a, a.cases_on' ∅ (λ a, M.step s a),
95+
start := M.start,
96+
accept := M.accept }
97+
98+
@[simp] lemma to_ε_NFA_ε_closure (M : NFA α σ) (S : set σ) : M.to_ε_NFA.ε_closure S = S :=
99+
begin
100+
ext a,
101+
split,
102+
{ rintro ( ⟨ _, _, h ⟩ | ⟨ _, _, _, h, _ ⟩ ),
103+
exact h,
104+
cases h },
105+
{ intro h,
106+
apply ε_NFA.ε_closure.base,
107+
exact h }
108+
end
109+
110+
@[simp] lemma to_ε_NFA_eval_from_match (M : NFA α σ) (start : set σ) :
111+
M.to_ε_NFA.eval_from start = M.eval_from start :=
112+
begin
113+
rw [eval_from, ε_NFA.eval_from, step_set, ε_NFA.step_set, to_ε_NFA_ε_closure],
114+
congr,
115+
ext S s,
116+
simp only [exists_prop, set.mem_Union, set.bind_def],
117+
apply exists_congr,
118+
simp only [and.congr_right_iff],
119+
intros t ht,
120+
rw M.to_ε_NFA_ε_closure,
121+
refl
122+
end
123+
124+
@[simp] lemma to_ε_NFA_correct (M : NFA α σ) :
125+
M.to_ε_NFA.accepts = M.accepts :=
126+
begin
127+
rw [accepts, ε_NFA.accepts, eval, ε_NFA.eval, to_ε_NFA_eval_from_match],
128+
refl
129+
end
130+
131+
end NFA

0 commit comments

Comments
 (0)