-
Notifications
You must be signed in to change notification settings - Fork 189
feat(Automata): Two-way automaton #834
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
crei
wants to merge
9
commits into
leanprover:main
Choose a base branch
from
crei:two-na
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
e70ed7d
Two-way automaton.
crei be2abca
Merge remote-tracking branch 'origin/main' into two-na
crei 7a37fac
Base TwoNA on top of NA.
crei db280cb
Define TwoNA via LTS on configurations.
crei 33dd1d0
An alternative design of two-way automata
ctchou 355da3c
Bundle input into configurations.
crei 4e8d6e4
Rename files.
crei f505fe9
Cleanup.
crei 9fe8b57
Add more comments.
crei File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,109 @@ | ||
| /- | ||
| Copyright (c) 2026 Christian Reitwiessner. All rights reserved. | ||
| Released under Apache 2.0 license as described in the file LICENSE. | ||
| Authors: Christian Reitwiessner | ||
| -/ | ||
|
|
||
| module | ||
|
|
||
| public import Cslib.Computability.Automata.NA.Basic | ||
| public import Cslib.Computability.Automata.Acceptors.Acceptor | ||
| public import Mathlib.Data.List.Chain | ||
| public import Mathlib.Data.Sign.Basic | ||
| public import Cslib.Foundations.Data.List.IsChainFromTo | ||
|
|
||
| /-! # Nondeterministic Two-Way Automaton | ||
|
|
||
| A Nondeterministic Two-Way Automaton (`TwoWayNA`) reads a finite input word on a tape and may move | ||
| its input head in either direction. A transition reads the symbol under the head and, besides | ||
| changing the state, moves the head one cell to the left, keeps it in place, or moves it one cell to | ||
| the right. | ||
|
|
||
| The input head cannot leave the input word to the left (in the sense that execution gets stuck in | ||
| this case) and once it leaves the word to the right, it stops. A run is accepting if and only if it | ||
| ends in an accepting state with the head just past the end of the input. | ||
|
|
||
| ## Main definitions | ||
|
|
||
| * `TwoWayNA`, the automaton itself | ||
| * `TwoWayNACfg`, a configuration of a `TwoWayNA`: Its input plus a state and the head position. | ||
| * `TwoWayNA.Step`, The single-step relation between configurations. | ||
|
|
||
| ## Implementation notes | ||
|
|
||
| The definition of `TwoWayNA` is kept close to [Vardi][Vardi1989]'s, because the main point is to | ||
| prove that it accepts the same languages as `NA.FinAcc` via his proof. This means we do not allow | ||
| the head to move off the input to the left (in the sense that if the transition relation has an | ||
| entry that would cause that, there is no successor configuration, the computation is stuck), but | ||
| also do not provide an end marker. Once the head moves off to the right, the machine instantly | ||
| stops, so it also cannot move back into the word. | ||
|
|
||
| ## References | ||
|
|
||
| * [Moshe Y. Vardi, *A Note on the Reduction of Two-Way Automata to One-Way Automata*][Vardi1989] | ||
|
|
||
| -/ | ||
|
|
||
| @[expose] public section | ||
|
|
||
| namespace Cslib.Automata | ||
|
|
||
| variable {State Symbol : Type*} | ||
|
|
||
| /-- A nondeterministic two-way automaton: a transition relation that reads an input symbol and | ||
| moves the input head, together with a set of initial and a set of accepting states. -/ | ||
| structure TwoWayNA (State Symbol : Type*) where | ||
| /-- The transition relation. `Tr q x m q'` means that, while reading the symbol `x`, the | ||
| automaton attempts to transition from state `q` to state `q'` and move its head according to | ||
| `m`. -/ | ||
| Tr (q : State) (x : Symbol) (m : SignType) (q' : State) : Prop | ||
| /-- The set of initial states of the automaton. -/ | ||
| start : Set State | ||
| /-- The set of accepting states of the automaton. -/ | ||
| accept : Set State | ||
|
|
||
| /-- The configuration of a two-way nondeterministic automaton. -/ | ||
| @[ext] | ||
| structure TwoWayNACfg (State Symbol : Type*) where | ||
| /-- The original input to the automaton. -/ | ||
| input : List Symbol | ||
| /-- The state of the automaton. -/ | ||
| state : State | ||
| /-- The input head position of the automaton: it can be on any symbol of the input or on the | ||
| position one step to the right of the input. -/ | ||
| pos : Fin (input.length + 1) | ||
|
|
||
| /-- This defines the set of initial configurations on a specific input. -/ | ||
| def TwoWayNACfg.IsInitialForInput (a : TwoWayNA State Symbol) (c : TwoWayNACfg State Symbol) | ||
| (input : List Symbol) : Prop := | ||
| c.state ∈ a.start ∧ c.pos = 0 ∧ c.input = input | ||
|
|
||
| /-- If a configuration is a accepting. -/ | ||
| def TwoWayNACfg.IsAccepting (a : TwoWayNA State Symbol) (c : TwoWayNACfg State Symbol) : Prop := | ||
| c.state ∈ a.accept ∧ c.pos = Fin.last _ | ||
|
|
||
| /-- Returns a nondeterministic finite acceptor on the configurations as states, accepting exactly | ||
| the runs of the two-way automaton on `input` that end in an accepting configuration. -/ | ||
| def TwoWayNA.toCfgNAFinAcc {State Symbol : Type*} (a : TwoWayNA State Symbol) | ||
| (input : List Symbol) : | ||
| NA.FinAcc (TwoWayNACfg State Symbol) (Symbol × SignType) where | ||
| Tr | ||
| | c, (x, m), c' => | ||
| c.input = c'.input ∧ | ||
| -- This also enforces that `c`'s input head position has to be inside the word. | ||
| some x = c.input[c.pos]? ∧ | ||
| a.Tr c.state x m c'.state ∧ | ||
| -- By doing input head position arithmetic and comparison in the integers, we get the | ||
| -- desired restrictions since `0 ≤ c'.pos < n + 1` and `0 ≤ c.pos < n`: | ||
| -- The input head after the transition cannot be left of the word but it is fine to be | ||
| -- one position right of the word (in which case no further transition is possible). | ||
| (c'.pos : ℤ) = (c.pos : ℤ) + (m.cast : ℤ) | ||
| start := { c | c.IsInitialForInput a input } | ||
| accept := { c | c.IsAccepting a } | ||
|
|
||
| @[simp, scoped grind =] | ||
| instance : Acceptor (TwoWayNA State Symbol) Symbol where | ||
| Accepts (a : TwoWayNA State Symbol) (input : List Symbol) := | ||
| ∃ μs, Acceptor.Accepts (a.toCfgNAFinAcc input) μs | ||
|
|
||
| end Cslib.Automata | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,81 @@ | ||
| /- | ||
| Copyright (c) 2026 Christian Reitwiessner. All rights reserved. | ||
| Released under Apache 2.0 license as described in the file LICENSE. | ||
| Authors: Christian Reitwiessner | ||
| -/ | ||
|
|
||
| module | ||
|
|
||
| public import Cslib.Computability.Automata.NA.Basic | ||
| public import Cslib.Computability.Automata.Acceptors.Acceptor | ||
| public import Mathlib.Data.List.Chain | ||
| public import Mathlib.Data.Sign.Basic | ||
| public import Cslib.Foundations.Data.List.IsChainFromTo | ||
|
|
||
| /-! # Nondeterministic Two-Way Automaton | ||
| -/ | ||
|
|
||
| @[expose] public section | ||
|
|
||
| namespace Cslib.Automata | ||
|
|
||
| variable {State Symbol : Type*} | ||
|
|
||
| /-- The word to be accepted is put in the state component `input`. This is necessary | ||
| because the `Acceptor` framework refers only to the automaton and nothing else. So | ||
| you can't make the word to be accepted a parameter of the automaton. -/ | ||
| @[ext] | ||
| structure TwoWayState (State Symbol : Type*) where | ||
| state : State | ||
| input : List Symbol | ||
| inpPos : ℕ | ||
|
|
||
| /-- Another alternative is to use `Unit` for this type. But I thik exposing the contents of | ||
| the state transitions can potentially faciliate proofs because an execution in the sense | ||
| of `LTS.Execution` would then include all nondeterministic choices made by an execution. -/ | ||
| @[ext] | ||
| structure TwoWaySymbol (Symbol : Type*) where | ||
| symbol : Symbol | ||
| dir : SignType | ||
|
|
||
| abbrev TwoWayLTS (State Symbol : Type*) := | ||
| LTS (TwoWayState State Symbol) (TwoWaySymbol Symbol) | ||
|
|
||
| structure TwoWayNA (State Symbol : Type*) where | ||
| LTS : TwoWayLTS State Symbol | ||
| start : Set (TwoWayState State Symbol) | ||
| accept : Set (TwoWayState State Symbol) | ||
| /-- We require that the state component `input` never changes. -/ | ||
| input_inv : ∀ s x t, LTS.Tr s x t → t.input = s.input | ||
|
|
||
| namespace TwoWayNA | ||
|
|
||
| /-- The word to be accepted is put in the state component `input` at the beginning. | ||
| The read pointer `inpPos` is at 0 at the beginning and just beyond `input` at the end. | ||
| You can choose to change that requirement. -/ | ||
| instance : Acceptor (TwoWayNA State Symbol) Symbol where | ||
| Accepts (a : TwoWayNA State Symbol) (xs : List Symbol) : Prop := | ||
| ∃ s, s ∈ a.start ∧ s.input = xs ∧ s.inpPos = 0 ∧ | ||
| ∃ ys t, a.LTS.MTr s ys t ∧ t ∈ a.accept ∧ t.inpPos = s.input.length | ||
|
|
||
| end TwoWayNA | ||
|
|
||
| def TwoWayTr (State Symbol : Type*) := State → Symbol → SignType → State → Prop | ||
|
|
||
| def twoWayLTS.mk (tr : TwoWayTr State Symbol) : TwoWayLTS State Symbol where | ||
| Tr s' x' t' := ∃ _ : s'.inpPos < s'.input.length, | ||
| s'.input[s'.inpPos] = x'.symbol ∧ t'.input = s'.input ∧ | ||
| tr s'.state x'.symbol x'.dir t'.state ∧ | ||
| match x'.dir with | ||
| | SignType.zero => t'.inpPos = s'.inpPos | ||
| | SignType.pos => t'.inpPos = s'.inpPos.succ | ||
| | SignType.neg => t'.inpPos = s'.inpPos.pred | ||
|
|
||
| def twoWayNA.mk (tr : TwoWayTr State Symbol) (start accept : Set State) | ||
| : TwoWayNA State Symbol where | ||
| LTS := twoWayLTS.mk tr | ||
| start := (fun s' ↦ s'.state) ⁻¹' start | ||
| accept := (fun s' ↦ s'.state) ⁻¹' accept | ||
| input_inv s' x' t' := by grind [twoWayLTS.mk] | ||
|
|
||
| end Cslib.Automata |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it is clearer to not involve
ℤat all. For example, what happens ifc.pos = 0andm = neg? It would seem that the RHS is then-1 : ℤ. Does this forcec'.posto be0? It takes some thinking and looking up the manual to figure that out. Why not spell it out via case splitting onmand (if necessary)c.pos?[Edit] Actually it is worse than I thought, because you can prove this:
That is, if
c.pos = 0andm = neg, there is no next step. Is this what you want?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, this is exactly what I want, see the
Implementation notes. This is also the way it is stated in the paper I'm formalizing here.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The informal text of your implementation note allows at least two interpretations when c.pos = 0 and m = neg:
(1) there is no next configuration and hence the execution gets stuck at this point.
(2) there is a next configuration but
posstays at position 0.Both approaches are reasonable in the sense that both will result in a regular language being accepted. Your code does (1), but that is not transparent. I had to squint hard at it and prove a theorem to be certain that my understanding is correct. You should spell out what you want either in the code or in the comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added more comments.