Turing machines #
This file defines a sequence of simple machine languages, starting with Turing machines and working up to more complex languages based on Wang B-machines.
Naming conventions #
Each model of computation in this file shares a naming convention for the elements of a model of computation. These are the parameters for the language:
Γ
is the alphabet on the tape.Λ
is the set of labels, or internal machine states.σ
is the type of internal memory, not on the tape. This does not exist in the TM0 model, and later models achieve this by mixing it intoΛ
.K
is used in the TM2 model, which has multiple stacks, and denotes the number of such stacks.
All of these variables denote "essentially finite" types, but for technical reasons it is convenient to allow them to be infinite anyway. When using an infinite type, we will be interested to prove that only finitely many values of the type are ever interacted with.
Given these parameters, there are a few common structures for the model that arise:
Stmt
is the set of all actions that can be performed in one step. For the TM0 model this set is finite, and for later models it is an infinite inductive type representing "possible program texts".Cfg
is the set of instantaneous configurations, that is, the state of the machine together with its environment.Machine
is the set of all machines in the model. Usually this is approximately a functionΛ → Stmt
, although different models have different ways of halting and other actions.step : Cfg → Option Cfg
is the function that describes how the state evolves over one step. Ifstep c = none
, thenc
is a terminal state, and the result of the computation is read off fromc
. Because of the type ofstep
, these models are all deterministic by construction.init : Input → Cfg
sets up the initial state. The typeInput
depends on the model; in most cases it isList Γ
.eval : Machine → Input → Part Output
, given a machineM
and inputi
, starts frominit i
, runsstep
until it reaches an output, and then applies a functionCfg → Output
to the final state to obtain the result. The typeOutput
depends on the model.Supports : Machine → Finset Λ → Prop
asserts that a machineM
starts inS : Finset Λ
, and can only ever jump to other states insideS
. This implies that the behavior ofM
on any input cannot depend on its values outsideS
. We use this to allowΛ
to be an infinite set when convenient, and prove that only finitely many of these states are actually accessible. This formalizes "essentially finite" mentioned above.
The BlankExtends
partial order holds of l₁
and l₂
if l₂
is obtained by adding
blanks (default : Γ
) to the end of l₁
.
Equations
- Turing.BlankExtends l₁ l₂ = ∃ (n : ℕ), l₂ = l₁ ++ List.replicate n default
Instances For
Any two extensions by blank l₁,l₂
of l
have a common join (which can be taken to be the
longer of l₁
and l₂
).
Instances For
BlankRel
is the symmetric closure of BlankExtends
, turning it into an equivalence
relation. Two lists are related by BlankRel
if one extends the other by blanks.
Equations
- Turing.BlankRel l₁ l₂ = (Turing.BlankExtends l₁ l₂ ∨ Turing.BlankExtends l₂ l₁)
Instances For
Given two BlankRel
lists, there exists (constructively) a common join.
Instances For
Given two BlankRel
lists, there exists (constructively) a common meet.
Instances For
Construct a setoid instance for BlankRel
.
Equations
- Turing.BlankRel.setoid Γ = { r := Turing.BlankRel, iseqv := ⋯ }
Instances For
Equations
- Turing.ListBlank.inhabited = { default := Quotient.mk'' [] }
Equations
- Turing.ListBlank.hasEmptyc = { emptyCollection := Quotient.mk'' [] }
A modified version of Quotient.liftOn'
specialized for ListBlank
, with the stronger
precondition BlankExtends
instead of BlankRel
.
Equations
- l.liftOn f H = Quotient.liftOn' l f ⋯
Instances For
The tail of a ListBlank
is well defined (up to the tail of blanks).
Equations
- l.tail = l.liftOn (fun (l : List Γ) => Turing.ListBlank.mk l.tail) ⋯
Instances For
We can cons an element onto a ListBlank
.
Equations
- Turing.ListBlank.cons a l = l.liftOn (fun (l : List Γ) => Turing.ListBlank.mk (a :: l)) ⋯
Instances For
The cons
and head
/tail
functions are mutually inverse, unlike in the case of List
where
this only holds for nonempty lists.
The cons
and head
/tail
functions are mutually inverse, unlike in the case of List
where
this only holds for nonempty lists.
Apply a function to a value stored at the nth position of the list.
Equations
- Turing.ListBlank.modifyNth f 0 x✝ = Turing.ListBlank.cons (f x✝.head) x✝.tail
- Turing.ListBlank.modifyNth f n.succ x✝ = Turing.ListBlank.cons x✝.head (Turing.ListBlank.modifyNth f n x✝.tail)
Instances For
Equations
- Turing.instInhabitedPointedMap = { default := { f := default, map_pt' := ⋯ } }
Equations
- Turing.instCoeFunPointedMapForall = { coe := Turing.PointedMap.f }
The map
function on lists is well defined on ListBlank
s provided that the map is
pointed.
Equations
- Turing.ListBlank.map f l = l.liftOn (fun (l : List Γ) => Turing.ListBlank.mk (List.map f.f l)) ⋯
Instances For
The i
-th projection as a pointed map.
Equations
- Turing.proj i = { f := fun (a : (i : ι) → Γ i) => a i, map_pt' := ⋯ }
Instances For
Append a list on the left side of a ListBlank
.
Equations
- Turing.ListBlank.append [] x✝ = x✝
- Turing.ListBlank.append (a :: l) x✝ = Turing.ListBlank.cons a (Turing.ListBlank.append l x✝)
Instances For
The flatMap
function on lists is well defined on ListBlank
s provided that the default
element is sent to a sequence of default elements.
Equations
- l.flatMap f hf = l.liftOn (fun (l : List Γ) => Turing.ListBlank.mk (l.flatMap f)) ⋯
Instances For
Alias of Turing.ListBlank.flatMap
.
The flatMap
function on lists is well defined on ListBlank
s provided that the default
element is sent to a sequence of default elements.
Instances For
Alias of Turing.ListBlank.flatMap_mk
.
Alias of Turing.ListBlank.cons_flatMap
.
The tape of a Turing machine is composed of a head element (which we imagine to be the
current position of the head), together with two ListBlank
s denoting the portions of the tape
going off to the left and right. When the Turing machine moves right, an element is pulled from the
right side and becomes the new head, while the head element is cons
ed onto the left side.
- head : Γ
The current position of the head.
- left : Turing.ListBlank Γ
The portion of the tape going off to the left.
- right : Turing.ListBlank Γ
The portion of the tape going off to the right.
Instances For
Equations
- Turing.Tape.inhabited = { default := { head := default, left := default, right := default } }
A direction for the Turing machine move
command, either
left or right.
- left : Turing.Dir
- right : Turing.Dir
Instances For
Equations
- Turing.instDecidableEqDir x✝ y✝ = if h : x✝.toCtorIdx = y✝.toCtorIdx then isTrue ⋯ else isFalse ⋯
Equations
- Turing.instInhabitedDir = { default := Turing.Dir.left }
The "inclusive" left side of the tape, including both left
and head
.
Equations
- T.left₀ = Turing.ListBlank.cons T.head T.left
Instances For
The "inclusive" right side of the tape, including both right
and head
.
Equations
- T.right₀ = Turing.ListBlank.cons T.head T.right
Instances For
Move the tape in response to a motion of the Turing machine. Note that T.move Dir.left
makes
T.left
smaller; the Turing machine is moving left and the tape is moving right.
Equations
- Turing.Tape.move Turing.Dir.left { head := a, left := L, right := R } = { head := L.head, left := L.tail, right := Turing.ListBlank.cons a R }
- Turing.Tape.move Turing.Dir.right { head := a, left := L, right := R } = { head := R.head, left := Turing.ListBlank.cons a L, right := R.tail }
Instances For
Construct a tape from a left side and an inclusive right side.
Equations
- Turing.Tape.mk' L R = { head := R.head, left := L, right := R.tail }
Instances For
Construct a tape from a left side and an inclusive right side.
Equations
Instances For
Construct a tape from a list, with the head of the list at the TM head and the rest going to the right.
Equations
- Turing.Tape.mk₁ l = Turing.Tape.mk₂ [] l
Instances For
Replace the current value of the head on the tape.
Equations
- Turing.Tape.write b T = { head := b, left := T.left, right := T.right }
Instances For
Apply a pointed map to a tape to change the alphabet.
Equations
- Turing.Tape.map f T = { head := f.f T.head, left := Turing.ListBlank.map f T.left, right := Turing.ListBlank.map f T.right }
Instances For
Run a state transition function σ → Option σ
"to completion". The return value is the last
state returned before a none
result. If the state transition function always returns some
,
then the computation diverges, returning Part.none
.
Equations
- Turing.eval f = PFun.fix fun (s : σ) => Part.some ((f s).elim (Sum.inl s) Sum.inr)
Instances For
The reflexive transitive closure of a state transition function. Reaches f a b
means
there is a finite sequence of steps f a = some a₁
, f a₁ = some a₂
, ... such that aₙ = b
.
This relation permits zero steps of the state transition function.
Equations
- Turing.Reaches f = Relation.ReflTransGen fun (a b : σ) => b ∈ f a
Instances For
The transitive closure of a state transition function. Reaches₁ f a b
means there is a
nonempty finite sequence of steps f a = some a₁
, f a₁ = some a₂
, ... such that aₙ = b
.
This relation does not permit zero steps of the state transition function.
Equations
- Turing.Reaches₁ f = Relation.TransGen fun (a b : σ) => b ∈ f a
Instances For
A variation on Reaches
. Reaches₀ f a b
holds if whenever Reaches₁ f b c
then
Reaches₁ f a c
. This is a weaker property than Reaches
and is useful for replacing states with
equivalent states without taking a step.
Equations
- Turing.Reaches₀ f a b = ∀ (c : σ), Turing.Reaches₁ f b c → Turing.Reaches₁ f a c
Instances For
(co-)Induction principle for eval
. If a property C
holds of any point a
evaluating to b
which is either terminal (meaning a = b
) or where the next point also satisfies C
, then it
holds of any point where eval f a
evaluates to b
. This formalizes the notion that if
eval f a
evaluates to b
then it reaches terminal state b
in finitely many steps.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Given a relation tr : σ₁ → σ₂ → Prop
between state spaces, and state transition functions
f₁ : σ₁ → Option σ₁
and f₂ : σ₂ → Option σ₂
, Respects f₁ f₂ tr
means that if tr a₁ a₂
holds
initially and f₁
takes a step to a₂
then f₂
will take one or more steps before reaching a
state b₂
satisfying tr a₂ b₂
, and if f₁ a₁
terminates then f₂ a₂
also terminates.
Such a relation tr
is also known as a refinement.
Equations
- Turing.Respects f₁ f₂ tr = ∀ ⦃a₁ : σ₁⦄ ⦃a₂ : σ₂⦄, tr a₁ a₂ → match f₁ a₁ with | some b₁ => ∃ (b₂ : σ₂), tr b₁ b₂ ∧ Turing.Reaches₁ f₂ a₂ b₂ | none => f₂ a₂ = none
Instances For
A simpler version of Respects
when the state transition relation tr
is a function.
Equations
- Turing.FRespects f₂ tr a₂ (some b₁) = Turing.Reaches₁ f₂ a₂ (tr b₁)
- Turing.FRespects f₂ tr a₂ none = (f₂ a₂ = none)
Instances For
The TM0 model #
A TM0 Turing machine is essentially a Post-Turing machine, adapted for type theory.
A Post-Turing machine with symbol type Γ
and label type Λ
is a function
Λ → Γ → Option (Λ × Stmt)
, where a Stmt
can be either move left
, move right
or write a
for a : Γ
. The machine works over a "tape", a doubly-infinite sequence of elements of Γ
, and
an instantaneous configuration, Cfg
, is a label q : Λ
indicating the current internal state of
the machine, and a Tape Γ
(which is essentially ℤ →₀ Γ
). The evolution is described by the
step
function:
- If
M q T.head = none
, then the machine halts. - If
M q T.head = some (q', s)
, then the machine performs actions : Stmt
and then transitions to stateq'
.
The initial state takes a List Γ
and produces a Tape Γ
where the head of the list is the head
of the tape and the rest of the list extends to the right, with the left side all blank. The final
state takes the entire right side of the tape right or equal to the current position of the
machine. (This is actually a ListBlank Γ
, not a List Γ
, because we don't know, at this level
of generality, where the output ends. If equality to default : Γ
is decidable we can trim the list
to remove the infinite tail of blanks.)
A Turing machine "statement" is just a command to either move left or right, or write a symbol on the tape.
- move {Γ : Type u_1} : Turing.Dir → Turing.TM0.Stmt Γ
- write {Γ : Type u_1} : Γ → Turing.TM0.Stmt Γ
Instances For
Equations
- Turing.TM0.Stmt.inhabited Γ = { default := Turing.TM0.Stmt.write default }
A Post-Turing machine with symbol type Γ
and label type Λ
is a function which, given the current state q : Λ
and
the tape head a : Γ
, either halts (returns none
) or returns
a new state q' : Λ
and a Stmt
describing what to do,
either a move left or right, or a write command.
Both Λ
and Γ
are required to be inhabited; the default value
for Γ
is the "blank" tape value, and the default value of Λ
is
the initial state.
Equations
- Turing.TM0.Machine Γ Λ = (Λ → Γ → Option (Λ × Turing.TM0.Stmt Γ))
Instances For
Equations
- Turing.TM0.Machine.inhabited Γ Λ = id inferInstance
The configuration state of a Turing machine during operation
consists of a label (machine state), and a tape.
The tape is represented in the form (a, L, R)
, meaning the tape looks like L.rev ++ [a] ++ R
with the machine currently reading the a
. The lists are
automatically extended with blanks as the machine moves around.
- q : Λ
The current machine state.
- Tape : Turing.Tape Γ
The current state of the tape: current symbol, left and right parts.
Instances For
Equations
- Turing.TM0.Cfg.inhabited = { default := { q := default, Tape := default } }
Execution semantics of the Turing machine.
Equations
- One or more equations did not get rendered due to their size.
Instances For
The statement Reaches M s₁ s₂
means that s₂
is obtained
starting from s₁
after a finite number of steps from s₂
.
Equations
- Turing.TM0.Reaches M = Relation.ReflTransGen fun (a b : Turing.TM0.Cfg Γ Λ) => b ∈ Turing.TM0.step M a
Instances For
The initial configuration.
Equations
- Turing.TM0.init l = { q := default, Tape := Turing.Tape.mk₁ l }
Instances For
Evaluate a Turing machine on initial input to a final state, if it terminates.
Equations
- Turing.TM0.eval M l = Part.map (fun (c : Turing.TM0.Cfg Γ Λ) => c.Tape.right₀) (Turing.eval (Turing.TM0.step M) (Turing.TM0.init l))
Instances For
The raw definition of a Turing machine does not require that
Γ
and Λ
are finite, and in practice we will be interested
in the infinite Λ
case. We recover instead a notion of
"effectively finite" Turing machines, which only make use of a
finite subset of their states. We say that a set S ⊆ Λ
supports a Turing machine M
if S
is closed under the
transition function and contains the initial state.
Equations
- Turing.TM0.Supports M S = (default ∈ S ∧ ∀ {q : Λ} {a : Γ} {q' : Λ} {s : Turing.TM0.Stmt Γ}, (q', s) ∈ M q a → q ∈ S → q' ∈ S)
Instances For
Map a TM statement across a function. This does nothing to move statements and maps the write values.
Equations
- Turing.TM0.Stmt.map f (Turing.TM0.Stmt.move d) = Turing.TM0.Stmt.move d
- Turing.TM0.Stmt.map f (Turing.TM0.Stmt.write a_1) = Turing.TM0.Stmt.write (f.f a_1)
Instances For
Map a configuration across a function, given f : Γ → Γ'
a map of the alphabets and
g : Λ → Λ'
a map of the machine states.
Equations
- Turing.TM0.Cfg.map f g { q := q, Tape := T } = { q := g q, Tape := Turing.Tape.map f T }
Instances For
Because the state transition function uses the alphabet and machine states in both the input
and output, to map a machine from one alphabet and machine state space to another we need functions
in both directions, essentially an Equiv
without the laws.
Equations
- M.map f₁ f₂ g₁ g₂ x✝¹ x✝ = Option.map (Prod.map g₁ (Turing.TM0.Stmt.map f₁)) (M (g₂ x✝¹) (f₂.f x✝))
Instances For
The TM1 model #
The TM1 model is a simplification and extension of TM0 (Post-Turing model) in the direction of
Wang B-machines. The machine's internal state is extended with a (finite) store σ
of variables
that may be accessed and updated at any time.
A machine is given by a Λ
indexed set of procedures or functions. Each function has a body which
is a Stmt
. Most of the regular commands are allowed to use the current value a
of the local
variables and the value T.head
on the tape to calculate what to write or how to change local
state, but the statements themselves have a fixed structure. The Stmt
s can be as follows:
move d q
: move left or right, and then doq
write (f : Γ → σ → Γ) q
: writef a T.head
to the tape, then doq
load (f : Γ → σ → σ) q
: change the internal state tof a T.head
branch (f : Γ → σ → Bool) qtrue qfalse
: Iff a T.head
is true, doqtrue
, elseqfalse
goto (f : Γ → σ → Λ)
: Go to labelf a T.head
halt
: Transition to the halting state, which halts on the following step
Note that here most statements do not have labels; goto
commands can only go to a new function.
Only the goto
and halt
statements actually take a step; the rest is done by recursion on
statements and so take 0 steps. (There is a uniform bound on how many statements can be executed
before the next goto
, so this is an O(1)
speedup with the constant depending on the machine.)
The halt
command has a one step stutter before actually halting so that any changes made before
the halt have a chance to be "committed", since the eval
relation uses the final configuration
before the halt as the output, and move
and write
etc. take 0 steps in this model.
The TM1 model is a simplification and extension of TM0
(Post-Turing model) in the direction of Wang B-machines. The machine's
internal state is extended with a (finite) store σ
of variables
that may be accessed and updated at any time.
A machine is given by a Λ
indexed set of procedures or functions.
Each function has a body which is a Stmt
, which can either be a
move
or write
command, a branch
(if statement based on the
current tape value), a load
(set the variable value),
a goto
(call another function), or halt
. Note that here
most statements do not have labels; goto
commands can only
go to a new function. All commands have access to the variable value
and current tape value.
- move {Γ : Type u_1} {Λ : Type u_2} {σ : Type u_3} : Turing.Dir → Turing.TM1.Stmt Γ Λ σ → Turing.TM1.Stmt Γ Λ σ
- write {Γ : Type u_1} {Λ : Type u_2} {σ : Type u_3} : (Γ → σ → Γ) → Turing.TM1.Stmt Γ Λ σ → Turing.TM1.Stmt Γ Λ σ
- load {Γ : Type u_1} {Λ : Type u_2} {σ : Type u_3} : (Γ → σ → σ) → Turing.TM1.Stmt Γ Λ σ → Turing.TM1.Stmt Γ Λ σ
- branch {Γ : Type u_1} {Λ : Type u_2} {σ : Type u_3} : (Γ → σ → Bool) → Turing.TM1.Stmt Γ Λ σ → Turing.TM1.Stmt Γ Λ σ → Turing.TM1.Stmt Γ Λ σ
- goto {Γ : Type u_1} {Λ : Type u_2} {σ : Type u_3} : (Γ → σ → Λ) → Turing.TM1.Stmt Γ Λ σ
- halt {Γ : Type u_1} {Λ : Type u_2} {σ : Type u_3} : Turing.TM1.Stmt Γ Λ σ
Instances For
Equations
- Turing.TM1.Stmt.inhabited Γ Λ σ = { default := Turing.TM1.Stmt.halt }
The configuration of a TM1 machine is given by the currently evaluating statement, the variable store value, and the tape.
- l : Option Λ
The statement (if any) which is currently evaluated
- var : σ
The current value of the variable store
- Tape : Turing.Tape Γ
The current state of the tape
Instances For
Equations
- Turing.TM1.Cfg.inhabited Γ Λ σ = { default := { l := default, var := default, Tape := default } }
The semantics of TM1 evaluation.
Equations
- Turing.TM1.stepAux (Turing.TM1.Stmt.move d q) x✝¹ x✝ = Turing.TM1.stepAux q x✝¹ (Turing.Tape.move d x✝)
- Turing.TM1.stepAux (Turing.TM1.Stmt.write a q) x✝¹ x✝ = Turing.TM1.stepAux q x✝¹ (Turing.Tape.write (a x✝.head x✝¹) x✝)
- Turing.TM1.stepAux (Turing.TM1.Stmt.load s q) x✝¹ x✝ = Turing.TM1.stepAux q (s x✝.head x✝¹) x✝
- Turing.TM1.stepAux (Turing.TM1.Stmt.branch p q₁ q₂) x✝¹ x✝ = bif p x✝.head x✝¹ then Turing.TM1.stepAux q₁ x✝¹ x✝ else Turing.TM1.stepAux q₂ x✝¹ x✝
- Turing.TM1.stepAux (Turing.TM1.Stmt.goto l) x✝¹ x✝ = { l := some (l x✝.head x✝¹), var := x✝¹, Tape := x✝ }
- Turing.TM1.stepAux Turing.TM1.Stmt.halt x✝¹ x✝ = { l := none, var := x✝¹, Tape := x✝ }
Instances For
The state transition function.
Equations
- Turing.TM1.step M { l := none, var := var, Tape := Tape } = none
- Turing.TM1.step M { l := some l, var := v, Tape := T } = some (Turing.TM1.stepAux (M l) v T)
Instances For
A set S
of labels supports the statement q
if all the goto
statements in q
refer only to other functions in S
.
Equations
- Turing.TM1.SupportsStmt S (Turing.TM1.Stmt.move a q) = Turing.TM1.SupportsStmt S q
- Turing.TM1.SupportsStmt S (Turing.TM1.Stmt.write a q) = Turing.TM1.SupportsStmt S q
- Turing.TM1.SupportsStmt S (Turing.TM1.Stmt.load a q) = Turing.TM1.SupportsStmt S q
- Turing.TM1.SupportsStmt S (Turing.TM1.Stmt.branch a q₁ q₂) = (Turing.TM1.SupportsStmt S q₁ ∧ Turing.TM1.SupportsStmt S q₂)
- Turing.TM1.SupportsStmt S (Turing.TM1.Stmt.goto l) = ∀ (a : Γ) (v : σ), l a v ∈ S
- Turing.TM1.SupportsStmt S Turing.TM1.Stmt.halt = True
Instances For
The subterm closure of a statement.
Equations
- Turing.TM1.stmts₁ (Turing.TM1.Stmt.move a q) = insert (Turing.TM1.Stmt.move a q) (Turing.TM1.stmts₁ q)
- Turing.TM1.stmts₁ (Turing.TM1.Stmt.write a q) = insert (Turing.TM1.Stmt.write a q) (Turing.TM1.stmts₁ q)
- Turing.TM1.stmts₁ (Turing.TM1.Stmt.load a q) = insert (Turing.TM1.Stmt.load a q) (Turing.TM1.stmts₁ q)
- Turing.TM1.stmts₁ (Turing.TM1.Stmt.branch a q₁ q₂) = insert (Turing.TM1.Stmt.branch a q₁ q₂) (Turing.TM1.stmts₁ q₁ ∪ Turing.TM1.stmts₁ q₂)
- Turing.TM1.stmts₁ x✝ = {x✝}
Instances For
The set of all statements in a Turing machine, plus one extra value none
representing the
halt state. This is used in the TM1 to TM0 reduction.
Equations
- Turing.TM1.stmts M S = Finset.insertNone (S.biUnion fun (q : Λ) => Turing.TM1.stmts₁ (M q))
Instances For
A set S
of labels supports machine M
if all the goto
statements in the functions in S
refer only to other functions
in S
.
Equations
- Turing.TM1.Supports M S = (default ∈ S ∧ ∀ q ∈ S, Turing.TM1.SupportsStmt S (M q))
Instances For
The initial state, given a finite input that is placed on the tape starting at the TM head and going to the right.
Equations
- Turing.TM1.init l = { l := some default, var := default, Tape := Turing.Tape.mk₁ l }
Instances For
Evaluate a TM to completion, resulting in an output list on the tape (with an indeterminate number of blanks on the end).
Equations
- Turing.TM1.eval M l = Part.map (fun (c : Turing.TM1.Cfg Γ Λ σ) => c.Tape.right₀) (Turing.eval (Turing.TM1.step M) (Turing.TM1.init l))
Instances For
TM1 emulator in TM0 #
To prove that TM1 computable functions are TM0 computable, we need to reduce each TM1 program to a TM0 program. So suppose a TM1 program is given. We take the following:
- The alphabet
Γ
is the same for both TM1 and TM0 - The set of states
Λ'
is defined to beOption Stmt₁ × σ
, that is, a TM1 statement ornone
representing halt, and the possible settings of the internal variables. Note that this is an infinite set, becauseStmt₁
is infinite. This is okay because we assume that from the initial TM1 state, only finitely many other labels are reachable, and there are only finitely many statements that appear in all of these functions.
Even though Stmt₁
contains a statement called halt
, we must separate it from none
(some halt
steps to none
and none
actually halts) because there is a one step stutter in the
TM1 semantics.
The base machine state space is a pair of an Option Stmt₁
representing the current program
to be executed, or none
for the halt state, and a σ
which is the local state (stored in the TM,
not the tape). Because there are an infinite number of programs, this state space is infinite, but
for a finitely supported TM1 machine and a finite type σ
, only finitely many of these states are
reachable.
Equations
- Turing.TM1to0.Λ' M = (Option (Turing.TM1.Stmt Γ Λ σ) × σ)
Instances For
Equations
- Turing.TM1to0.instInhabitedΛ' M = { default := (some (M default), default) }
The core TM1 → TM0 translation function. Here s
is the current value on the tape, and the
Stmt₁
is the TM1 statement to translate, with local state v : σ
. We evaluate all regular
instructions recursively until we reach either a move
or write
command, or a goto
; in the
latter case we emit a dummy write s
step and transition to the new target location.
Equations
- Turing.TM1to0.trAux M s (Turing.TM1.Stmt.move d q) x✝ = ((some q, x✝), Turing.TM0.Stmt.move d)
- Turing.TM1to0.trAux M s (Turing.TM1.Stmt.write a q) x✝ = ((some q, x✝), Turing.TM0.Stmt.write (a s x✝))
- Turing.TM1to0.trAux M s (Turing.TM1.Stmt.load a q) x✝ = Turing.TM1to0.trAux M s q (a s x✝)
- Turing.TM1to0.trAux M s (Turing.TM1.Stmt.branch p q₁ q₂) x✝ = bif p s x✝ then Turing.TM1to0.trAux M s q₁ x✝ else Turing.TM1to0.trAux M s q₂ x✝
- Turing.TM1to0.trAux M s (Turing.TM1.Stmt.goto l) x✝ = ((some (M (l s x✝)), x✝), Turing.TM0.Stmt.write s)
- Turing.TM1to0.trAux M s Turing.TM1.Stmt.halt x✝ = ((none, x✝), Turing.TM0.Stmt.write s)
Instances For
The translated TM0 machine (given the TM1 machine input).
Equations
- Turing.TM1to0.tr M (none, snd) x✝ = none
- Turing.TM1to0.tr M (some q, v) x✝ = some (Turing.TM1to0.trAux M x✝ q v)
Instances For
Translate configurations from TM1 to TM0.
Equations
- Turing.TM1to0.trCfg M { l := l, var := v, Tape := T } = { q := (Option.map M l, v), Tape := T }
Instances For
Given a finite set of accessible Λ
machine states, there is a finite set of accessible
machine states in the target (even though the type Λ'
is infinite).
Equations
- Turing.TM1to0.trStmts M S = Turing.TM1.stmts M S ×ˢ Finset.univ
Instances For
TM1(Γ) emulator in TM1(Bool) #
The most parsimonious Turing machine model that is still Turing complete is TM0
with Γ = Bool
.
Because our construction in the previous section reducing TM1
to TM0
doesn't change the
alphabet, we can do the alphabet reduction on TM1
instead of TM0
directly.
The basic idea is to use a bijection between Γ
and a subset of Vector Bool n
, where n
is a
fixed constant. Each tape element is represented as a block of n
bools. Whenever the machine
wants to read a symbol from the tape, it traverses over the block, performing n
branch
instructions to each any of the 2^n
results.
For the write
instruction, we have to use a goto
because we need to follow a different code
path depending on the local state, which is not available in the TM1 model, so instead we jump to
a label computed using the read value and the local state, which performs the writing and returns
to normal execution.
Emulation overhead is O(1)
. If not for the above write
behavior it would be 1-1 because we are
exploiting the 0-step behavior of regular commands to avoid taking steps, but there are
nevertheless a bounded number of write
calls between goto
statements because TM1 statements are
finitely long.
The configuration state of the TM.
- normal {Γ : Type u_1} {Λ : Type u_2} {σ : Type u_3} : Λ → Turing.TM1to1.Λ'
- write {Γ : Type u_1} {Λ : Type u_2} {σ : Type u_3} : Γ → Turing.TM1.Stmt Γ Λ σ → Turing.TM1to1.Λ'
Instances For
Equations
- Turing.TM1to1.instInhabitedΛ' = { default := Turing.TM1to1.Λ'.normal default }
Read a vector of length n
from the tape.
Equations
- One or more equations did not get rendered due to their size.
- Turing.TM1to1.readAux 0 f = f List.Vector.nil
Instances For
A move left or right corresponds to n
moves across the super-cell.
Equations
- Turing.TM1to1.move d q = (Turing.TM1.Stmt.move d)^[n] q
Instances For
To read a symbol from the tape, we use readAux
to traverse the symbol,
then return to the original position with n
moves to the left.
Equations
- Turing.TM1to1.read dec f = Turing.TM1to1.readAux n fun (v : List.Vector Bool n) => Turing.TM1to1.move Turing.Dir.left (f (dec v))
Instances For
Write a list of bools on the tape.
Equations
- Turing.TM1to1.write [] x✝ = x✝
- Turing.TM1to1.write (a :: l) x✝ = Turing.TM1.Stmt.write (fun (x : Bool) (x : σ) => a) (Turing.TM1.Stmt.move Turing.Dir.right (Turing.TM1to1.write l x✝))
Instances For
Translate a normal instruction. For the write
command, we use a goto
indirection so that
we can access the current value of the tape.
Equations
- One or more equations did not get rendered due to their size.
- Turing.TM1to1.trNormal dec (Turing.TM1.Stmt.move a q) = Turing.TM1to1.move a (Turing.TM1to1.trNormal dec q)
- Turing.TM1to1.trNormal dec (Turing.TM1.Stmt.write a q) = Turing.TM1to1.read dec fun (a_1 : Γ) => Turing.TM1.Stmt.goto fun (x : Bool) (s : σ) => Turing.TM1to1.Λ'.write (a a_1 s) q
- Turing.TM1to1.trNormal dec (Turing.TM1.Stmt.load a q) = Turing.TM1to1.read dec fun (a_1 : Γ) => Turing.TM1.Stmt.load (fun (x : Bool) (s : σ) => a a_1 s) (Turing.TM1to1.trNormal dec q)
- Turing.TM1to1.trNormal dec (Turing.TM1.Stmt.goto l) = Turing.TM1to1.read dec fun (a : Γ) => Turing.TM1.Stmt.goto fun (x : Bool) (s : σ) => Turing.TM1to1.Λ'.normal (l a s)
- Turing.TM1to1.trNormal dec Turing.TM1.Stmt.halt = Turing.TM1.Stmt.halt
Instances For
The low level tape corresponding to the given tape over alphabet Γ
.
Equations
- Turing.TM1to1.trTape' enc0 L R = Turing.Tape.mk' (L.flatMap (fun (x : Γ) => (enc x).toList.reverse) ⋯) (R.flatMap (fun (x : Γ) => (enc x).toList) ⋯)
Instances For
The low level tape corresponding to the given tape over alphabet Γ
.
Equations
- Turing.TM1to1.trTape enc0 T = Turing.TM1to1.trTape' enc0 T.left T.right₀
Instances For
The top level program.
Equations
- Turing.TM1to1.tr enc dec M (Turing.TM1to1.Λ'.normal l) = Turing.TM1to1.trNormal dec (M l)
- Turing.TM1to1.tr enc dec M (Turing.TM1to1.Λ'.write a q) = Turing.TM1to1.write (enc a).toList (Turing.TM1to1.move Turing.Dir.left (Turing.TM1to1.trNormal dec q))
Instances For
The machine configuration translation.
Equations
- Turing.TM1to1.trCfg enc enc0 { l := l, var := v, Tape := T } = { l := Option.map Turing.TM1to1.Λ'.normal l, var := v, Tape := Turing.TM1to1.trTape enc0 T }
Instances For
The set of accessible Λ'.write
machine states.
Equations
- Turing.TM1to1.writes (Turing.TM1.Stmt.move a q) = Turing.TM1to1.writes q
- Turing.TM1to1.writes (Turing.TM1.Stmt.write a q) = Finset.image (fun (a : Γ) => Turing.TM1to1.Λ'.write a q) Finset.univ ∪ Turing.TM1to1.writes q
- Turing.TM1to1.writes (Turing.TM1.Stmt.load a q) = Turing.TM1to1.writes q
- Turing.TM1to1.writes (Turing.TM1.Stmt.branch a q₁ q₂) = Turing.TM1to1.writes q₁ ∪ Turing.TM1to1.writes q₂
- Turing.TM1to1.writes (Turing.TM1.Stmt.goto l) = ∅
- Turing.TM1to1.writes Turing.TM1.Stmt.halt = ∅
Instances For
The set of accessible machine states, assuming that the input machine is supported on S
,
are the normal states embedded from S
, plus all write states accessible from these states.
Equations
- Turing.TM1to1.trSupp M S = S.biUnion fun (l : Λ) => insert (Turing.TM1to1.Λ'.normal l) (Turing.TM1to1.writes (M l))
Instances For
TM0 emulator in TM1 #
To establish that TM0 and TM1 are equivalent computational models, we must also have a TM0 emulator
in TM1. The main complication here is that TM0 allows an action to depend on the value at the head
and local state, while TM1 doesn't (in order to have more programming language-like semantics).
So we use a computed goto
to go to a state that performs the desired action and then returns to
normal execution.
One issue with this is that the halt
instruction is supposed to halt immediately, not take a step
to a halting state. To resolve this we do a check for halt
first, then goto
(with an
unreachable branch).
The machine states for a TM1 emulating a TM0 machine. States of the TM0 machine are embedded
as normal q
states, but the actual operation is split into two parts, a jump to act s q
followed by the action and a jump to the next normal
state.
- normal {Γ : Type u_1} {Λ : Type u_2} : Λ → Turing.TM0to1.Λ'
- act {Γ : Type u_1} {Λ : Type u_2} : Turing.TM0.Stmt Γ → Λ → Turing.TM0to1.Λ'
Instances For
Equations
- Turing.TM0to1.instInhabitedΛ' = { default := Turing.TM0to1.Λ'.normal default }
The program.
Equations
- One or more equations did not get rendered due to their size.
- Turing.TM0to1.tr M (Turing.TM0to1.Λ'.act (Turing.TM0.Stmt.move d) q) = Turing.TM1.Stmt.move d (Turing.TM1.Stmt.goto fun (x : Γ) (x : Unit) => Turing.TM0to1.Λ'.normal q)
Instances For
The configuration translation.
Equations
- Turing.TM0to1.trCfg M { q := q, Tape := T } = { l := bif (M q T.head).isSome then some (Turing.TM0to1.Λ'.normal q) else none, var := (), Tape := T }
Instances For
The TM2 model #
The TM2 model removes the tape entirely from the TM1 model, replacing it with an arbitrary (finite)
collection of stacks, each with elements of different types (the alphabet of stack k : K
is
Γ k
). The statements are:
push k (f : σ → Γ k) q
putsf a
on thek
-th stack, then doesq
.pop k (f : σ → Option (Γ k) → σ) q
changes the state tof a (S k).head
, whereS k
is the value of thek
-th stack, and removes this element from the stack, then doesq
.peek k (f : σ → Option (Γ k) → σ) q
changes the state tof a (S k).head
, whereS k
is the value of thek
-th stack, then doesq
.load (f : σ → σ) q
reads nothing but appliesf
to the internal state, then doesq
.branch (f : σ → Bool) qtrue qfalse
doesqtrue
orqfalse
according tof a
.goto (f : σ → Λ)
jumps to labelf a
.halt
halts on the next step.
The configuration is a tuple (l, var, stk)
where l : Option Λ
is the current label to run or
none
for the halting state, var : σ
is the (finite) internal state, and stk : ∀ k, List (Γ k)
is the collection of stacks. (Note that unlike the TM0
and TM1
models, these are not
ListBlank
s, they have definite ends that can be detected by the pop
command.)
Given a designated stack k
and a value L : List (Γ k)
, the initial configuration has all the
stacks empty except the designated "input" stack; in eval
this designated stack also functions
as the output stack.
The TM2 model removes the tape entirely from the TM1 model,
replacing it with an arbitrary (finite) collection of stacks.
The operation push
puts an element on one of the stacks,
and pop
removes an element from a stack (and modifying the
internal state based on the result). peek
modifies the
internal state but does not remove an element.
- push {K : Type u_1} {Γ : K → Type u_2} {Λ : Type u_3} {σ : Type u_4} (k : K) : (σ → Γ k) → Turing.TM2.Stmt Γ Λ σ → Turing.TM2.Stmt Γ Λ σ
- peek {K : Type u_1} {Γ : K → Type u_2} {Λ : Type u_3} {σ : Type u_4} (k : K) : (σ → Option (Γ k) → σ) → Turing.TM2.Stmt Γ Λ σ → Turing.TM2.Stmt Γ Λ σ
- pop {K : Type u_1} {Γ : K → Type u_2} {Λ : Type u_3} {σ : Type u_4} (k : K) : (σ → Option (Γ k) → σ) → Turing.TM2.Stmt Γ Λ σ → Turing.TM2.Stmt Γ Λ σ
- load {K : Type u_1} {Γ : K → Type u_2} {Λ : Type u_3} {σ : Type u_4} : (σ → σ) → Turing.TM2.Stmt Γ Λ σ → Turing.TM2.Stmt Γ Λ σ
- branch {K : Type u_1} {Γ : K → Type u_2} {Λ : Type u_3} {σ : Type u_4} : (σ → Bool) → Turing.TM2.Stmt Γ Λ σ → Turing.TM2.Stmt Γ Λ σ → Turing.TM2.Stmt Γ Λ σ
- goto {K : Type u_1} {Γ : K → Type u_2} {Λ : Type u_3} {σ : Type u_4} : (σ → Λ) → Turing.TM2.Stmt Γ Λ σ
- halt {K : Type u_1} {Γ : K → Type u_2} {Λ : Type u_3} {σ : Type u_4} : Turing.TM2.Stmt Γ Λ σ
Instances For
Equations
- Turing.TM2.Stmt.inhabited Γ Λ σ = { default := Turing.TM2.Stmt.halt }
A configuration in the TM2 model is a label (or none
for the halt state), the state of
local variables, and the stacks. (Note that the stacks are not ListBlank
s, they have a definite
size.)
- l : Option Λ
The current label to run (or
none
for the halting state) - var : σ
The internal state
- stk (k : K) : List (Γ k)
The (finite) collection of internal stacks
Instances For
Equations
- Turing.TM2.Cfg.inhabited Γ Λ σ = { default := { l := default, var := default, stk := default } }
The step function for the TM2 model.
Equations
- Turing.TM2.stepAux (Turing.TM2.Stmt.push k f q) x✝¹ x✝ = Turing.TM2.stepAux q x✝¹ (Function.update x✝ k (f x✝¹ :: x✝ k))
- Turing.TM2.stepAux (Turing.TM2.Stmt.peek k f q) x✝¹ x✝ = Turing.TM2.stepAux q (f x✝¹ (x✝ k).head?) x✝
- Turing.TM2.stepAux (Turing.TM2.Stmt.pop k f q) x✝¹ x✝ = Turing.TM2.stepAux q (f x✝¹ (x✝ k).head?) (Function.update x✝ k (x✝ k).tail)
- Turing.TM2.stepAux (Turing.TM2.Stmt.load a q) x✝¹ x✝ = Turing.TM2.stepAux q (a x✝¹) x✝
- Turing.TM2.stepAux (Turing.TM2.Stmt.branch f q₁ q₂) x✝¹ x✝ = bif f x✝¹ then Turing.TM2.stepAux q₁ x✝¹ x✝ else Turing.TM2.stepAux q₂ x✝¹ x✝
- Turing.TM2.stepAux (Turing.TM2.Stmt.goto f) x✝¹ x✝ = { l := some (f x✝¹), var := x✝¹, stk := x✝ }
- Turing.TM2.stepAux Turing.TM2.Stmt.halt x✝¹ x✝ = { l := none, var := x✝¹, stk := x✝ }
Instances For
The step function for the TM2 model.
Equations
- Turing.TM2.step M { l := none, var := var, stk := stk } = none
- Turing.TM2.step M { l := some l, var := v, stk := S } = some (Turing.TM2.stepAux (M l) v S)
Instances For
The (reflexive) reachability relation for the TM2 model.
Equations
- Turing.TM2.Reaches M = Relation.ReflTransGen fun (a b : Turing.TM2.Cfg Γ Λ σ) => b ∈ Turing.TM2.step M a
Instances For
Given a set S
of states, SupportsStmt S q
means that q
only jumps to states in S
.
Equations
- Turing.TM2.SupportsStmt S (Turing.TM2.Stmt.push k a q) = Turing.TM2.SupportsStmt S q
- Turing.TM2.SupportsStmt S (Turing.TM2.Stmt.peek k a q) = Turing.TM2.SupportsStmt S q
- Turing.TM2.SupportsStmt S (Turing.TM2.Stmt.pop k a q) = Turing.TM2.SupportsStmt S q
- Turing.TM2.SupportsStmt S (Turing.TM2.Stmt.load a q) = Turing.TM2.SupportsStmt S q
- Turing.TM2.SupportsStmt S (Turing.TM2.Stmt.branch a q₁ q₂) = (Turing.TM2.SupportsStmt S q₁ ∧ Turing.TM2.SupportsStmt S q₂)
- Turing.TM2.SupportsStmt S (Turing.TM2.Stmt.goto l) = ∀ (v : σ), l v ∈ S
- Turing.TM2.SupportsStmt S Turing.TM2.Stmt.halt = True
Instances For
The set of subtree statements in a statement.
Equations
- Turing.TM2.stmts₁ (Turing.TM2.Stmt.push k a q) = insert (Turing.TM2.Stmt.push k a q) (Turing.TM2.stmts₁ q)
- Turing.TM2.stmts₁ (Turing.TM2.Stmt.peek k a q) = insert (Turing.TM2.Stmt.peek k a q) (Turing.TM2.stmts₁ q)
- Turing.TM2.stmts₁ (Turing.TM2.Stmt.pop k a q) = insert (Turing.TM2.Stmt.pop k a q) (Turing.TM2.stmts₁ q)
- Turing.TM2.stmts₁ (Turing.TM2.Stmt.load a q) = insert (Turing.TM2.Stmt.load a q) (Turing.TM2.stmts₁ q)
- Turing.TM2.stmts₁ (Turing.TM2.Stmt.branch a q₁ q₂) = insert (Turing.TM2.Stmt.branch a q₁ q₂) (Turing.TM2.stmts₁ q₁ ∪ Turing.TM2.stmts₁ q₂)
- Turing.TM2.stmts₁ (Turing.TM2.Stmt.goto a) = {Turing.TM2.Stmt.goto a}
- Turing.TM2.stmts₁ Turing.TM2.Stmt.halt = {Turing.TM2.Stmt.halt}
Instances For
The set of statements accessible from initial set S
of labels.
Equations
- Turing.TM2.stmts M S = Finset.insertNone (S.biUnion fun (q : Λ) => Turing.TM2.stmts₁ (M q))
Instances For
Given a TM2 machine M
and a set S
of states, Supports M S
means that all states in
S
jump only to other states in S
.
Equations
- Turing.TM2.Supports M S = (default ∈ S ∧ ∀ q ∈ S, Turing.TM2.SupportsStmt S (M q))
Instances For
The initial state of the TM2 model. The input is provided on a designated stack.
Equations
- Turing.TM2.init k L = { l := some default, var := default, stk := Function.update (fun (x : K) => []) k L }
Instances For
Evaluates a TM2 program to completion, with the output on the same stack as the input.
Equations
- Turing.TM2.eval M k L = Part.map (fun (c : Turing.TM2.Cfg Γ Λ σ) => c.stk k) (Turing.eval (Turing.TM2.step M) (Turing.TM2.init k L))
Instances For
TM2 emulator in TM1 #
To prove that TM2 computable functions are TM1 computable, we need to reduce each TM2 program to a
TM1 program. So suppose a TM2 program is given. This program has to maintain a whole collection of
stacks, but we have only one tape, so we must "multiplex" them all together. Pictorially, if stack
1 contains [a, b]
and stack 2 contains [c, d, e, f]
then the tape looks like this:
bottom: ... | _ | T | _ | _ | _ | _ | ...
stack 1: ... | _ | b | a | _ | _ | _ | ...
stack 2: ... | _ | f | e | d | c | _ | ...
where a tape element is a vertical slice through the diagram. Here the alphabet is
Γ' := Bool × ∀ k, Option (Γ k)
, where:
bottom : Bool
is marked only in one place, the initial position of the TM, and represents the tail of all stacks. It is never modified.stk k : Option (Γ k)
is the value of thek
-th stack, if in range, otherwisenone
(which is the blank value). Note that the head of the stack is at the far end; this is so that push and pop don't have to do any shifting.
In "resting" position, the TM is sitting at the position marked bottom
. For non-stack actions,
it operates in place, but for the stack actions push
, peek
, and pop
, it must shuttle to the
end of the appropriate stack, make its changes, and then return to the bottom. So the states are:
normal (l : Λ)
: waiting atbottom
to execute functionl
go k (s : StAct k) (q : Stmt₂)
: travelling to the right to get to the end of stackk
in order to perform stack actions
, and later continue with executingq
ret (q : Stmt₂)
: travelling to the left after having performed a stack action, and executingq
once we arrive
Because of the shuttling, emulation overhead is O(n)
, where n
is the current maximum of the
length of all stacks. Therefore a program that takes k
steps to run in TM2 takes O((m+k)k)
steps to run when emulated in TM1, where m
is the length of the input.
The alphabet of the TM2 simulator on TM1 is a marker for the stack bottom, plus a vector of stack elements for each stack, or none if the stack does not extend this far.
Instances For
Equations
- Turing.TM2to1.Γ'.fintype = instFintypeProd Bool ((k : K) → Option (Γ k))
The bottom marker is fixed throughout the calculation, so we use the addBottom
function
to express the program state in terms of a tape with only the stacks themselves.
Equations
- Turing.TM2to1.addBottom L = Turing.ListBlank.cons (true, L.head) (Turing.ListBlank.map { f := Prod.mk false, map_pt' := ⋯ } L.tail)
Instances For
A stack action is a command that interacts with the top of a stack. Our default position is at the bottom of all the stacks, so we have to hold on to this action while going to the end to modify the stack.
- push {K : Type u_1} {Γ : K → Type u_2} {σ : Type u_4} {k : K} : (σ → Γ k) → Turing.TM2to1.StAct k
- peek {K : Type u_1} {Γ : K → Type u_2} {σ : Type u_4} {k : K} : (σ → Option (Γ k) → σ) → Turing.TM2to1.StAct k
- pop {K : Type u_1} {Γ : K → Type u_2} {σ : Type u_4} {k : K} : (σ → Option (Γ k) → σ) → Turing.TM2to1.StAct k
Instances For
Equations
- Turing.TM2to1.StAct.inhabited = { default := Turing.TM2to1.StAct.peek fun (s : σ) (x : Option (Γ k)) => s }
The TM2 statement corresponding to a stack action.
Equations
Instances For
The effect of a stack action on the local variables, given the value of the stack.
Equations
- Turing.TM2to1.stVar v l (Turing.TM2to1.StAct.push f) = v
- Turing.TM2to1.stVar v l (Turing.TM2to1.StAct.peek f) = f v l.head?
- Turing.TM2to1.stVar v l (Turing.TM2to1.StAct.pop f) = f v l.head?
Instances For
The effect of a stack action on the stack.
Equations
- Turing.TM2to1.stWrite v l (Turing.TM2to1.StAct.push f) = f v :: l
- Turing.TM2to1.stWrite v l (Turing.TM2to1.StAct.peek f) = l
- Turing.TM2to1.stWrite v l (Turing.TM2to1.StAct.pop f) = l.tail
Instances For
We have partitioned the TM2 statements into "stack actions", which require going to the end of the stack, and all other actions, which do not. This is a modified recursor which lumps the stack actions into one.
Equations
- Turing.TM2to1.stmtStRec H₁ H₂ H₃ H₄ H₅ (Turing.TM2.Stmt.push k a q) = H₁ k (Turing.TM2to1.StAct.push a) q (Turing.TM2to1.stmtStRec H₁ H₂ H₃ H₄ H₅ q)
- Turing.TM2to1.stmtStRec H₁ H₂ H₃ H₄ H₅ (Turing.TM2.Stmt.peek k a q) = H₁ k (Turing.TM2to1.StAct.peek a) q (Turing.TM2to1.stmtStRec H₁ H₂ H₃ H₄ H₅ q)
- Turing.TM2to1.stmtStRec H₁ H₂ H₃ H₄ H₅ (Turing.TM2.Stmt.pop k a q) = H₁ k (Turing.TM2to1.StAct.pop a) q (Turing.TM2to1.stmtStRec H₁ H₂ H₃ H₄ H₅ q)
- Turing.TM2to1.stmtStRec H₁ H₂ H₃ H₄ H₅ (Turing.TM2.Stmt.load a q) = H₂ a q (Turing.TM2to1.stmtStRec H₁ H₂ H₃ H₄ H₅ q)
- Turing.TM2to1.stmtStRec H₁ H₂ H₃ H₄ H₅ (Turing.TM2.Stmt.branch a q₁ q₂) = H₃ a q₁ q₂ (Turing.TM2to1.stmtStRec H₁ H₂ H₃ H₄ H₅ q₁) (Turing.TM2to1.stmtStRec H₁ H₂ H₃ H₄ H₅ q₂)
- Turing.TM2to1.stmtStRec H₁ H₂ H₃ H₄ H₅ (Turing.TM2.Stmt.goto l) = H₄ l
- Turing.TM2to1.stmtStRec H₁ H₂ H₃ H₄ H₅ Turing.TM2.Stmt.halt = H₅
Instances For
The machine states of the TM2 emulator. We can either be in a normal state when waiting for the next TM2 action, or we can be in the "go" and "return" states to go to the top of the stack and return to the bottom, respectively.
- normal {K : Type u_1} {Γ : K → Type u_2} {Λ : Type u_3} {σ : Type u_4} : Λ → Turing.TM2to1.Λ'
- go {K : Type u_1} {Γ : K → Type u_2} {Λ : Type u_3} {σ : Type u_4} (k : K) : Turing.TM2to1.StAct k → Turing.TM2.Stmt Γ Λ σ → Turing.TM2to1.Λ'
- ret {K : Type u_1} {Γ : K → Type u_2} {Λ : Type u_3} {σ : Type u_4} : Turing.TM2.Stmt Γ Λ σ → Turing.TM2to1.Λ'
Instances For
Equations
- Turing.TM2to1.Λ'.inhabited = { default := Turing.TM2to1.Λ'.normal default }
The program corresponding to state transitions at the end of a stack. Here we start out just after the top of the stack, and should end just after the new top of the stack.
Equations
- One or more equations did not get rendered due to their size.
Instances For
The initial state for the TM2 emulator, given an initial TM2 state. All stacks start out empty except for the input stack, and the stack bottom mark is set at the head.
Equations
- One or more equations did not get rendered due to their size.
Instances For
The translation of TM2 statements to TM1 statements. regular actions have direct equivalents,
but stack actions are deferred by going to the corresponding go
state, so that we can find the
appropriate stack top.
Equations
- Turing.TM2to1.trNormal (Turing.TM2.Stmt.push k a q) = Turing.TM1.Stmt.goto fun (x : Turing.TM2to1.Γ') (x : σ) => Turing.TM2to1.Λ'.go k (Turing.TM2to1.StAct.push a) q
- Turing.TM2to1.trNormal (Turing.TM2.Stmt.peek k a q) = Turing.TM1.Stmt.goto fun (x : Turing.TM2to1.Γ') (x : σ) => Turing.TM2to1.Λ'.go k (Turing.TM2to1.StAct.peek a) q
- Turing.TM2to1.trNormal (Turing.TM2.Stmt.pop k a q) = Turing.TM1.Stmt.goto fun (x : Turing.TM2to1.Γ') (x : σ) => Turing.TM2to1.Λ'.go k (Turing.TM2to1.StAct.pop a) q
- Turing.TM2to1.trNormal (Turing.TM2.Stmt.load a q) = Turing.TM1.Stmt.load (fun (x : Turing.TM2to1.Γ') => a) (Turing.TM2to1.trNormal q)
- Turing.TM2to1.trNormal (Turing.TM2.Stmt.branch a q₁ q₂) = Turing.TM1.Stmt.branch (fun (x : Turing.TM2to1.Γ') => a) (Turing.TM2to1.trNormal q₁) (Turing.TM2to1.trNormal q₂)
- Turing.TM2to1.trNormal (Turing.TM2.Stmt.goto l) = Turing.TM1.Stmt.goto fun (x : Turing.TM2to1.Γ') (s : σ) => Turing.TM2to1.Λ'.normal (l s)
- Turing.TM2to1.trNormal Turing.TM2.Stmt.halt = Turing.TM1.Stmt.halt
Instances For
The set of machine states accessible from an initial TM2 statement.
Equations
- Turing.TM2to1.trStmts₁ (Turing.TM2.Stmt.push k f q) = {Turing.TM2to1.Λ'.go k (Turing.TM2to1.StAct.push f) q, Turing.TM2to1.Λ'.ret q} ∪ Turing.TM2to1.trStmts₁ q
- Turing.TM2to1.trStmts₁ (Turing.TM2.Stmt.peek k f q) = {Turing.TM2to1.Λ'.go k (Turing.TM2to1.StAct.peek f) q, Turing.TM2to1.Λ'.ret q} ∪ Turing.TM2to1.trStmts₁ q
- Turing.TM2to1.trStmts₁ (Turing.TM2.Stmt.pop k f q) = {Turing.TM2to1.Λ'.go k (Turing.TM2to1.StAct.pop f) q, Turing.TM2to1.Λ'.ret q} ∪ Turing.TM2to1.trStmts₁ q
- Turing.TM2to1.trStmts₁ (Turing.TM2.Stmt.load a q) = Turing.TM2to1.trStmts₁ q
- Turing.TM2to1.trStmts₁ (Turing.TM2.Stmt.branch a q₁ q₂) = Turing.TM2to1.trStmts₁ q₁ ∪ Turing.TM2to1.trStmts₁ q₂
- Turing.TM2to1.trStmts₁ x✝ = ∅
Instances For
The TM2 emulator machine states written as a TM1 program.
This handles the go
and ret
states, which shuttle to and from a stack top.
Equations
- One or more equations did not get rendered due to their size.
- Turing.TM2to1.tr M (Turing.TM2to1.Λ'.normal q) = Turing.TM2to1.trNormal (M q)
Instances For
The relation between TM2 configurations and TM1 configurations of the TM2 emulator.
- mk {K : Type u_1} {Γ : K → Type u_2} {Λ : Type u_3} {σ : Type u_4} {q : Option Λ} {v : σ} {S : (k : K) → List (Γ k)} (L : Turing.ListBlank ((k : K) → Option (Γ k))) : (∀ (k : K), Turing.ListBlank.map (Turing.proj k) L = Turing.ListBlank.mk (List.map some (S k)).reverse) → Turing.TM2to1.TrCfg { l := q, var := v, stk := S } { l := Option.map Turing.TM2to1.Λ'.normal q, var := v, Tape := Turing.Tape.mk' ∅ (Turing.TM2to1.addBottom L) }
Instances For
The support of a set of TM2 states in the TM2 emulator.
Equations
- Turing.TM2to1.trSupp M S = S.biUnion fun (l : Λ) => insert (Turing.TM2to1.Λ'.normal l) (Turing.TM2to1.trStmts₁ (M l))