|
| 1 | +/- |
| 2 | +Copyright (c) 2020 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 data.fintype.basic |
| 8 | +import computability.DFA |
| 9 | + |
| 10 | +/-! |
| 11 | +# Nondeterministic Finite Automata |
| 12 | +This file contains the definition of a Nondeterministic Finite Automaton (NFA), a state machine |
| 13 | +which determines whether a string (implemented as a list over an arbitrary alphabet) is in a regular |
| 14 | +set by evaluating the string over every possible path. |
| 15 | +We show that DFA's are equivalent to NFA's however the construction from NFA to DFA uses an |
| 16 | +exponential number of states. |
| 17 | +Note that this definition allows for Automaton with infinite states, a `fintype` instance must be |
| 18 | +supplied for true NFA's. |
| 19 | +-/ |
| 20 | + |
| 21 | +universes u v |
| 22 | + |
| 23 | +/-- An NFA is a set of states (`σ`), a transition function from state to state labelled by the |
| 24 | + alphabet (`step`), a starting state (`start`) and a set of acceptance states (`accept`). |
| 25 | + Note the transition function sends a state to a `set` of states. These are the states that it |
| 26 | + may be sent to. -/ |
| 27 | +structure NFA (α : Type u) (σ : Type v) := |
| 28 | +(step : σ → α → 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 | +/-- `M.step_set S a` is the union of `M.step s a` for all `s ∈ S`. -/ |
| 39 | +def step_set : set σ → α → set σ := |
| 40 | +λ Ss a, Ss >>= (λ S, (M.step S a)) |
| 41 | + |
| 42 | +lemma mem_step_set (s : σ) (S : set σ) (a : α) : |
| 43 | + s ∈ M.step_set S a ↔ ∃ t ∈ S, s ∈ M.step t a := |
| 44 | +by simp only [step_set, set.mem_Union, set.bind_def] |
| 45 | + |
| 46 | +/-- `M.eval_from S x` computes all possible paths though `M` with input `x` starting at an element |
| 47 | + of `S`. -/ |
| 48 | +def eval_from (start : set σ) : list α → set σ := |
| 49 | +list.foldl M.step_set start |
| 50 | + |
| 51 | +/-- `M.eval x` computes all possible paths though `M` with input `x` starting at an element of |
| 52 | + `M.start`. -/ |
| 53 | +def eval := M.eval_from M.start |
| 54 | + |
| 55 | +/-- `M.accepts` is the language of `x` such that there is an accept state in `M.eval x`. -/ |
| 56 | +def accepts : language α := |
| 57 | +λ x, ∃ S ∈ M.accept, S ∈ M.eval x |
| 58 | + |
| 59 | +/-- `M.to_DFA` is an `DFA` constructed from a `NFA` `M` using the subset construction. The |
| 60 | + states is the type of `set`s of `M.state` and the step function is `M.step_set`. -/ |
| 61 | +def to_DFA : DFA α (set σ) := |
| 62 | +{ step := M.step_set, |
| 63 | + start := M.start, |
| 64 | + accept := {S | ∃ s ∈ S, s ∈ M.accept} } |
| 65 | + |
| 66 | +@[simp] lemma to_DFA_correct : |
| 67 | + M.to_DFA.accepts = M.accepts := |
| 68 | +begin |
| 69 | + ext x, |
| 70 | + rw [accepts, DFA.accepts, eval, DFA.eval], |
| 71 | + change list.foldl _ _ _ ∈ {S | _} ↔ _, |
| 72 | + finish |
| 73 | +end |
| 74 | + |
| 75 | +end NFA |
| 76 | + |
| 77 | +namespace DFA |
| 78 | + |
| 79 | +/-- `M.to_NFA` is an `NFA` constructed from a `DFA` `M` by using the same start and accept |
| 80 | + states and a transition function which sends `s` with input `a` to the singleton `M.step s a`. -/ |
| 81 | +def to_NFA (M : DFA α σ') : NFA α σ' := |
| 82 | +{ step := λ s a, {M.step s a}, |
| 83 | + start := {M.start}, |
| 84 | + accept := M.accept } |
| 85 | + |
| 86 | +@[simp] lemma to_NFA_eval_from_match (M : DFA α σ) (start : σ) (s : list α) : |
| 87 | + M.to_NFA.eval_from {start} s = {M.eval_from start s} := |
| 88 | +begin |
| 89 | + change list.foldl M.to_NFA.step_set {start} s = {list.foldl M.step start s}, |
| 90 | + induction s with a s ih generalizing start, |
| 91 | + { tauto }, |
| 92 | + { rw [list.foldl, list.foldl], |
| 93 | + have h : M.to_NFA.step_set {start} a = {M.step start a}, |
| 94 | + { rw NFA.step_set, |
| 95 | + finish }, |
| 96 | + rw h, |
| 97 | + tauto } |
| 98 | +end |
| 99 | + |
| 100 | +@[simp] lemma to_NFA_correct (M : DFA α σ) : |
| 101 | + M.to_NFA.accepts = M.accepts := |
| 102 | +begin |
| 103 | + ext x, |
| 104 | + change (∃ S H, S ∈ M.to_NFA.eval_from {M.start} x) ↔ _, |
| 105 | + rw to_NFA_eval_from_match, |
| 106 | + split, |
| 107 | + { rintro ⟨ S, hS₁, hS₂ ⟩, |
| 108 | + rw set.mem_singleton_iff at hS₂, |
| 109 | + rw hS₂ at hS₁, |
| 110 | + assumption }, |
| 111 | + { intro h, |
| 112 | + use M.eval x, |
| 113 | + finish } |
| 114 | +end |
| 115 | + |
| 116 | +end DFA |
0 commit comments