-
Notifications
You must be signed in to change notification settings - Fork 197
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
Merged
Merged
Changes from all commits
Commits
Show all changes
16 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 bb88bbb
Merge remote-tracking branch 'origin/main' into two-na
crei 341fc23
Fix after mathlib bump.
crei f5fb9a3
Merge remote-tracking branch 'origin/main' into two-na
crei a885767
Add invariant about input.
crei 33974bd
Merge remote-tracking branch 'self/two-na' into two-na
crei 87ae7e4
Remove alternative.
crei a1a7ef6
Merge remote-tracking branch 'origin/main' into two-na
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,116 @@ | ||
| /- | ||
| 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.Basic.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 } | ||
|
|
||
| /-- Any reachable state of `a.toCfgNAFinAcc input` contains the original input. -/ | ||
| lemma TwoWayNA.toCfgNAFinAcc_input_eq {State Symbol : Type*} (a : TwoWayNA State Symbol) | ||
| (input : List Symbol) : | ||
| (a.toCfgNAFinAcc input).toLTS.TrInv (fun c => c.input = input) := by | ||
| intro c μ c' h_tr rfl | ||
| simp_all [TwoWayNA.toCfgNAFinAcc] | ||
|
|
||
| @[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
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.
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.
Thanks for adding the clarifying comment. You can actually define an invariant and prove that it is indeed an invariant in the sense of
LTS.TrInvandLTS.MTrInvas theorems.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.
Sorry, I don't understand, which invariant would you suggest?
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.
Ignore my comment. I realized that the property I wish to state is about a transition, not a single state. On the other hand, if the invariant takes the
inputas a parameter, you should be able to assert and prove thatc.input = inputas an invariant.