From: Thomas Walker Lynch Date: Mon, 1 Jun 2026 16:47:23 +0000 (+0000) Subject: . X-Git-Url: https://git.reasoningtechnology.com/figure_1.png?a=commitdiff_plain;h=d48e5e1a3b95fd2a24ff450c49b8d5c1708346e6;p=TM-2026 . --- diff --git a/document/TM-2026.html b/document/TM-2026.html new file mode 100644 index 0000000..ab036f2 --- /dev/null +++ b/document/TM-2026.html @@ -0,0 +1,1071 @@ + + + + + TTCA: Chapters 1-8 + + + + + + + + + + +

Introduction

+ +

+ The Turing machine was introduced by Alan Turing in 1936 + + and later refined into the standard textbook formulation given by Lewis and Papadimitriou + . + The definition presented in the first chapter of this book reformulates that classical automaton + with explicit structural components (a single‑ended tape, a read/write head, a read‑value buffer, + and a Moore‑style controller) that anticipate a hardware mapping. This reformulation is + functionally equivalent to the classical machine; the differences are presentational, chosen to + serve the architectural direction of this volume. +

+ +

+ Classical computation theory typically explores the limits of computation, such as the complexity of algorithms and the decidability of questions, while assuming unbounded resources. In this volume we reverse the direction: we begin with a practical, hardware‑oriented reformulation of the Turing machine and work upward, constructing a rigorous bridge from pure theory to a high‑integrity computer architecture. To achieve a faithful physical mapping, we must close the conceptual gap between ideal mathematical machines and the hard spatial realities of memory, finite tapes, and structural constraints. +

+ +

+ A computer architecture is will be Turing Complete, if the only errors that can occur from a faultless realization of the architecture are due to the underlying mathematical properties of the program being run, a program logic flaw, or due to a lack of memory resources. +

+ +

+ Computation theorists have hedged over the infinite nature of the Turing Machine tape. Turing described the tape as “unlimited”. Minsky called it “potentially infinite", and explains that it is finite at any moment but always able to grow . ”. Though Hopcroft, Motwani, and Ullman define a single ended tape that is infinite to the right; however they also note that only a finite prefix is ever used during a halting computation. +

+ +

+ With the aim of building up from Turing Machines toward computer architecture we will start with the Turing Machine and build up to a more hardware like computing model in three steps, first by talking about Turing Machines in a more computer architecture like manner. This is merely a change in nomenclature. Building on that hardware‑terminology Turing machine, we introduce the + TTCA Turing Machine, a bounded variation that replaces the infinite tape with an explicitly finite but extendable tape, enforces a write‑before‑read constraint, and adds explicit append and delete commands. + As the third step, we compound to the read, write, and step commands to create a command language that is under program control. We literally provide this command language as a software library for interfacing programs with memory. +

+ +

+ The changes introduced in the TTCA Turing machine are inconsequential for existence proofs and complexity classes, yet they guarantee that every computational run remains structurally finite, a property that directly mirrors a physical memory system. The practical consequence is that a correctly programmed TTCA machine halts only for reasons of program logic, mathematical impossibility, or genuine resource exhaustion, and even the last of these can be deferred if memory is hot‑swapped at the boundary. By casting the definition in an explicit, hardware‑like specification, we create a framework where the theoretical model and a physical implementation are structurally isomorphic throughout execution. The result is a baseline execution engine that preserves the theoretical power of the Turing machine while embedding the constraints that real hardware demands. Subsequent chapters refine this engine, replace the fixed state controller with a software‑driven command set, and ultimately demonstrate how a carefully chosen theoretical foundation can evolve into a practical, programmable computer architecture. The TTCA Turing Machine is not a mere theoretical object; it is a realizable specification, and thus serves as a computational foundation without the need for the “imagine if …” caveat. +

+ + + +

Introduction

+ +

+ The Turing machine was introduced by Alan Turing in 1936 + + and later refined into the standard textbook formulation given by Lewis and Papadimitriou + . + The definition presented in the first chapter of this book reformulates that classical automaton + with explicit structural components (a single‑ended tape, a read/write head, a read‑value buffer, + and a Moore‑style controller) that anticipate a hardware mapping. This reformulation is + functionally equivalent to the classical machine; the differences are presentational, chosen to + serve the architectural direction of this volume. +

+ +

+ Classical computation theory typically explores the limits of computation, such as the + complexity of algorithms and the decidability of questions, while assuming unbounded resources. + In this volume we reverse the direction: we begin with a practical, hardware‑oriented + reformulation of the Turing machine and work upward, constructing a rigorous bridge from pure + theory to a high‑integrity computer architecture. To achieve a faithful + physical mapping, we must close the conceptual gap between ideal mathematical machines and the + hard spatial realities of memory, finite tapes, and structural constraints. +

+ +

+ Building on that reformulated Turing machine, we introduce the + TTCA Turing Machine, a bounded variation that replaces the infinite tape + with a finite but extendable tape, enforces a write‑before‑read constraint, and + adds explicit append and delete commands. These changes are inconsequential for + existence proofs and complexity classes, yet they guarantee that every computational run remains + structurally finite, a property that directly mirrors a physical memory system. The practical + consequence is that a correctly programmed TTCA machine halts only for reasons of program logic, + mathematical impossibility, or genuine resource exhaustion—and even the last of these can be + deferred if memory is hot‑swapped at the boundary. By casting the definition in an explicit, + hardware‑like specification, we create a framework where the theoretical model and a physical + implementation are structurally isomorphic throughout execution. The result is a baseline + execution engine that preserves the theoretical power of the Turing machine while embedding the + constraints that real hardware demands. Subsequent chapters refine this engine, add higher‑level + abstractions, and ultimately demonstrate how a carefully chosen theoretical foundation can evolve + into a practical, programmable computer architecture. The TTCA Turing Machine is not a mere + theoretical object; it is a realizable specification, and thus serves as a computational + foundation without the need for the “imagine if …” caveat. +

+ +

The TTCA Turing Machine variation

+

The material in this chapter owes a great deal to, Elements of the Theory of Computation.

+ +

However in this volume we are exploring the nature of computation in the other direction. We are trying to work our way up from computation theory to arrive at a practical computer architecture. Towards this end we start with a definition for the Turing Machine that itself resembles a computer architecture specification.

+ +
+ A Turing Machine +
Figure 1 A Turing Machine
+
+ +

Accordingly, our Turing machine consists of a:

+ + +

The values assigned to the variables are excluded from the definition.

+ +

A symbol has permission to be any mathematical object for which there is a probability to be multiple instances, where within a given context, instances compare to be equal among other instances of the same symbol, but compare not equal to instances of other symbols. As a matter of convenience in this document, I use sequences of letters and/or digits for symbol instances.

+ +

The alphabet is a finite set of symbols.

+ +

The distinct empty-symbol has permission to be any symbol that is excluded from the alphabet.

+ +

Like alphabet symbols, this symbol has a probability to appear in the tape sequence.

+ +

Only instances of alphabet symbols or the empty-symbol are permitted to be written to the tape.

+ +

Intuitively we might consider that the alphabet symbols are useful while the empty symbol is merely taking up space while waiting to be displaced, in the same manner that we consider a bookshelf to be empty rather than being full of air. (And if we put a bookshelf underwater is it still empty, or is it full of water?).

+ +

The tape is an infinite sequence.

+ +

We say that the first element in the tape sequence is the leftmost element.

+ +

The second element in the sequence is then the right neighbor of the leftmost element. Etc. this nomenclature is drawn from a left to right writing convention for sequences. The leftmost element has no left neighbor, but it does have a right neighbor. All other sequence elements have both a left neighbor and a right neighbor. A Turing Machine tape has no rightmost element because it is an infinite sequence.

+ +

We notice three special subsequences:

+
    +
  1. a first, leftmost, element which is either an alphabet symbol or the empty-symbol
  2. +
  3. optionally this is followed by a sequence extending to a rightmost alphabet symbol
  4. +
  5. if the second subsequence is finite, it is followed by an infinite sequence of empty-symbols.
  6. +
+ +

So as to distinguish this tape partitioning from others found in this volume, we give it the name, the Leftmost Rightmost Partitioning, because it emphasizes the location of the leftmost element (alphabet or empty) and the rightmost alphabet element.

+ +

It is because of this third subsequence, the infinite tail, that the empty-symbol became distinct from the alphabet.

+ +

A Turing Machine lacks the capacity to compute an infinite empty tail, so such a tail must come from somewhere else. Here, we provide it by definition.

+ +

Putting together the first two subsequences noted above gives us the active area.

+ +

If there is no alphabet character on the tape, then there is no active area (i.e. the active area has length 0).

+ +

The infinite right going tail of cells holding the empty-symbol is then the inactive area.

+ +

The initial active area provided to a Turing Machine is called the input.

+ +

The final active area, obtained after the Turing Machine halts, is called the output.

+ +

If the input has been computed solely from another Turing Machine, then that input, will necessarily be finite in length.

+ +

If the input is provided as a general mathematical object, perhaps even one abstracted from 'what a Turing Machine computation would produce if it ran to an infinite limit number of steps', then the input has permission to be either finite or infinite.

+ +

The read/write head indicates one of the elements in the tape sequence.

+ +

The head functions differently than a Natural Number, as Thomas is probably accustomed to for sequence element indexes.

+ +

Such a definition would be circular because later we use a Turing Machine to define Natural Numbers and the concept of an address.

+ +

Instead, we can think of the head as partitioning the tape into two subsequences. We will call this the Head Centric partitioning.

+ +

The first subsequence extends from the leftmost element up to the indicated element, inclusively. During computation this first subsequence is necessarily finite. We call it the left-hand side.

+ +

The second subsequence holds the rest of the tape. We call it the right-hand side.

+ +

To step the head to the right we remove the leftmost element from the right-hand side, and then append it to, i.e. make it the new rightmost element of, the left-hand side.

+ +

To step the head left, we remove the rightmost element of the left-hand side, and prepend it, i.e. make it the leftmost element of, the right-hand side.

+ +

In the section, Addresses and cells, we will justify using the term cell in place of element, and then we will be able to say such things as the cell that the head is on, or the indicated cell.

+ +

The controlling state machine is a graph.

+ +

It has permission to be either a Mealy or Moore style state machine graph, as these are equally expressive.

+ +

Only the fixed procedure would change. We describe a Moore style machine and corresponding procedure here.

+ +

The graph is a set containing three elements:

+ + +

A state is a symbol. Instances of state symbols appear in a different context than those of alphabet symbols or the empty-symbol, and thus they do not need to be distinct from them.

+ +

Each state has a command attached to it, <state, command>. Each state appears in exactly one such pairing.

+ +

We have a fixed set of commands: { do-nothing, step-left, step-right, and write(value) }, where the value parameter is set when the controller is defined.

+ +

value must be an instance of an alphabet symbol or an instance of the empty-symbol.

+ +

In addition, we should not forget the implied read command that returns a value from the tape, and is used by the fixed procedure.

+ +

The arcs are triples of symbols, <from-state, to-state, value>.

+ +

The from-states, to-states, and values are set when the controller is defined.

+ +

The value is an instance of either an alphabet symbol or the empty-symbol.

+ + + We require that for each from-state that there exists exactly one arc for each member of the alphabet, one for the empty-symbol, and for states with a step-left command, that there be one arc for the left from leftmost error. + + +

Arcs can be dropped from diagrams if we can prove that they cannot be used.

+ +

As a notational convenience we might also reduce the work of listing so many arcs by adopting the convention of explicitly providing only unique arcs, and then saying the rest of the arcs are identical to a default arc or an error arc.

+ +

Here we present the fixed procedure in the form of a main procedure and two subprocedures, 'Initialize', and 'Take a Step'. To perform a computation follow the steps in Main, and its subprocedures, until being told to halt:

+ + + Deterministic (Uniplex) Procedure + + Initialize: + 1. place the input on the tape + 2. place the head on the leftmost element + 3. set the current state to the start state. + + Take a Step: + 1. read the symbol instance indicated by the head + 2. execute the command corresponding to the current state + 3. select the arc that has a from-state that matches the current-state, and a value that matches the instance just read. Take that arc's next state and make it the machine's next state. + 4. make the next state the current state. + + Main: + 1. Initialize + 2. if the current-state is the halt state, then halt + 3. Take a Step + 4. go to 2. + + +

We initialize the machine by instantiating the input on the tape, putting the head on the leftmost element of the tape, and setting the current state to the start state.

+ +

We take a step by first reading the symbol under the head and storing that value in our read value buffer, executing the command associated with the state, and then following the arc selected by the element we read.

+ +

We must read the element before executing the command, because the command might perform a write to the cell.

+ +

Consequently, as part of the Turing Machine definition we picked up the buffer for holding the read value.

+ +

Walking through this procedure is variously known as running the machine, performing a computation, evaluating the input, evaluating the function, or simply as computing.

+ +

Note that while walking through this procedure we are always in exactly one state. This is because we required that there be one arc for each letter in the alphabet, empty-symbol, and possibly the left from leftmost error. We say that this property means that the machine is deterministic.

+ +

To create a non-deterministic machine we allow multiple arcs to have the same read value as a second member, and we modify the second step of the procedure to follow all arcs that have matches with the symbol returned by read.

+ +

When we do this we might find that zero, one, or many of the arcs are followed in one step, and thus that we have a probability to have zero or more next states.

+ +

Conceptually each of these states corresponds to a separate machine head with its own read buffer.

+ +

At the end of the step we rename our next state set to be the current state set.

+ +

Should the halt state ever be placed in the current state set, then we halt the machine.

+ +

Should the current state set become empty without every having reached a halt state, we say the machine has failed.

+ + + Non-Deterministic (Multiplex) Procedure + + Initialize: + place the input on the tape + for each start state place a <start state, head location on leftmost> pair in the current state set + + Take a Step: + For each <state, head-location> member of the current state set: + read the symbol instance from the corresponding head location + execute the command corresponding to the state + for each arc match, add a <next-state, head-location> pair into the next state set + + Main: + Initialize + if the halt-state is a member of the current-state set, then halt, if current state set is empty, then halt with an error + Take a Step + go to 2 + + +

As reviewed by Papadimitriou, such non-deterministic machines can be converted to deterministic ones. This is done by calling each unique 'state set' a state.

+ +

Thomas might recall from our definition of symbol, that we can use any mathematical object for which instances are comparable.

+ +

That includes sets. When two current state sets have the same members, we say they are instances of the same state. When they have different members we say they are different states.

+ +

Hence existence proofs on non-deterministic machines will turn out the same as for deterministic ones, but the non-deterministic machines might require fewer steps to evaluate.

+ +

If we keep a trace of execution through a non-deterministic machine, and then walk backwards from each halt state, we find paths through the state machine, that, had we taken one of these paths, the machine would have reached the halt state.

+ +

Had we known such a path in advance we could have saved ourselves from doing the work of following the other paths.

+ +

This explains why such machines are called 'non-deterministic' - we don't know which of the paths we are following while running forward will be the path that leads to the halt state first.

+ +

We might arbitrarily pick a path, then we would have some probability of eventually hitting the halt state.

+ +

But notice, that the conversion algorithm going from a non-deterministic machine to a deterministic machine is forward moving. It starts by giving the initial state set a state name, and moves forward by giving each unique set of states a state name, until hitting the halt state.

+ +

No stochastic variables, or “guesses” are involved. Note also, when we run a non-deterministic machine we do so in a deterministic manner. There are no stochastic variables involved.

+ +

Hence, non-deterministic represents an interpretation rather than the machine definition or anything we are asked to do while walking through the fixed procedure.

+ +

So as to avoid any confusion due to this nuance, I am going to introduce a different term for non-deterministic machines. I am going to say they are multiplexed.

+ +

Should the need arise to emphasize that a machine is excluded from multiplexing, I will say such a machine is uniplex.

+ +

This new nomenclature is especially helpful when discussing the TM Library code, where the multiplexing of heads is readily apparent.

+ +
+ +

Orders of analysis

+ +

Turing Machines that halt in finite number of steps for any finite input are computational. I.e. for any given non-computational Turing Machine there will exist at least one input for which computation will never complete.

+ +

By analyzing a machine we might learn that some machine, say M, is conditionally computational on a given set of inputs, say I.

+ +

Then in case of the strongest delineation, computation will not halt for members of I, or it might be the case that we don't know about the inputs in I, or that I analysis shows that I can be broken into two sets, inputs for which computation will not stop for, and those for which analysis does not provide an answer.

+ +

To analyze a given First Turing Machine we do not execute it, but instead we place its definition on the tape as input for a second Turing Machine to analyze.

+ +

We might also add on the input tape to the first Turing Machine another Turing Machine that defines the language input to the first Turing Machine, otherwise we have to assume that any input is possible.

+ +

We then run this analysis Turing Machine so as to learn about the first one. (It is often the case that this second Turing Machine which performs the analysis is in a person's brain. Perhaps the person is writing a proof about the Turing Machine that is being studied.)

+ +

Analysis can be applied to both computational and non-computational machines. In many cases it is possible to learn something useful about a non-computational machine, one which we cannot evaluate, by analyzing it.

+ +

The term 'analysis' can be applied more generally. Accordingly, 'first order' analysis is the same as computation. ‘Second order’ analysis is what was described in the prior paragraph.

+ +

When the term 'analysis' is used without further qualification, the order is implied by context, but typically we are referring to second order analysis.

+ +

It is possible to perform higher orders of analysis. Here is a question that belongs to the next higher order: does there exist a Turing Machine A that can analyze a Turing Machine B to decide if B will always halt? We are standing above looking down and asking a question about machines of the second order.

+ +
+ +

Addresses and cells

+ +

Natural Number, address

+ +

We can define a Turing Machine that is identical to the recursive definition of Natural Numbers as given by Peano.

+ +

As the Natural Numbers never end, we cannot run this machine to a halting point, but we can analyze it.

+ +

As such we can assign a Natural Number to each member of the tape sequence. We will call the machine that does this the address machine.

+ +

Since multiple tape Turing Machines are equivalent to a single tape Turing Machines, we begin with the simplification and say that our address machine has two tapes. One we will call the main tape. We will assign addresses to the elements on the main tape.

+ +

The second tape we will call the address tape. It is initially empty.

+ +

The controller makes use of our increment routine for unary numbers which we described earlier.

+ +

We begin with the main tape head on the leftmost element. The address tape reads zero, this is the address for the leftmost element.

+ +

The controller then steps the main tape head right one element, and it runs the increment routine on the address tape. The address tape then reads ‘s’, i.e. one. This is the address for the right neighbor of the leftmost element.

+ +

We then repeat this procedure while assigning addresses to each successive element, two, three, four, etc. We call the set of addresses created in this manner the ‘Natural Numbers’.

+ +

Though we can never run this machine to the point where it halts, we may analyze the Natural Number machine and reason about it.

+ +

As one example, we can take a first given Turing Machine with it’s head somewhere on a tape, encode that machine and its state, and also encode an address machine, and provide these two machines to a third machine, one called address-of.

+ +

The address-of machine can then run the address machine while stepping the head left on the first given machine.

+ +

At the point where the next left step would cause a left of leftmost error, the address will appear on the address tape of the address machine.

+ +

Distance

+ +

The distance between two elements is defined to be the difference in their corresponding addresses.

+ +

Area and length

+ +

An area is a subsequence of cells on a tape. An area has permission to be defined using two addresses. The first address being that of the leftmost cell in the area, and the second address being that of the rightmost cell in the area.

+ +

The length of an area is one greater than the distance between the address of the leftmost cell of the area, and that of the rightmost cell of the area.

+ +

Hence the smallest length that an area has permission to have is 1. This occurs when the address to the leftmost cell is the same as the address to the rightmost cell, i.e. when there is only one cell.

+ +

The concept of area has permission to be abstracted to include a set of cells that share a property.

+ +

This might be a property of the values in the cells, or a property of the addresses of cells.

+ +

An abstract area can be colored by a property detecting machine.

+ +

Such a machine would start at the leftmost cell, check the value in that cell for the given property and mark it accordingly. It would then step right and repeat.

+ +

For such a marker machine to be computational there will have to be a leftmost cell in the area, and upon finding a leftmost cell in the area, a rightmost cell will have to exist. Without either of these a marker machine will never halt.

+ +

Even if a marker machine might not halt, it still might be useful for analysis.

+ +

Such marker machines that color based only the value found in each cell are context free.

+ +

Abstract areas have permission to also be defined based on functions of cell values that include context.

+ +

When the cells in an abstract area are contiguous, we say that the area is compact.

+ +

An example of an area that is excluded from being compact is that of the odd addressed cells.

+ +

The odd and even addressed cells of a tape form two non-overlapping abstract areas.

+ +

Given a cell in either of these areas, its direct neighbors will not be in the same area.

+ +

Zero length is a second order concept

+ +

Suppose we have a Turing Machine that is designed to mark an area based on some property of the symbols.

+ +

Suppose further that starting with the head on the first cell, our Turing Machine will step right zero or more times until it finds a cell that holds a symbol that has the special property.

+ +

Once it finds such a cell it will write an area marker symbol to that cell, step right, and repeat writing area marker cells until it finds a cell that holds a symbol that lacks the area property. At which point the machine halts.

+ +

Once an area is marked, we can go back and run a length measuring machine that counts the sequence of marks. We will call this the length of the area.

+ +

Now suppose we employ a second order analysis.

+ +

Instead of running our area marker Turing Machine, we examine its definition and the definition of the machine that generated the tape data, to learn if such a machine will ever halt.

+ +

Although we know that it is not possible in general to analyze machines to know if they will halt, or not, it is certainly possible to do so in some cases, and this is one of those cases.

+ +

Upon analysis of our area marking machine we make a startling discovery: inputs exist for which the area marker will never halt.

+ +

In one case, if there is no leftmost symbol with the property that defines the area we are looking for, the marker machine will search forever.

+ +

In the second case, once the area is found, it is open on the right and it never ends.

+ +

We might say that inputs that have no leftmost symbol have an area of zero length.

+ +

This is an abstract concept, because our area marking and area measuring machines will never be run then halt and return a length measure of zero.

+ +

Instead we might arrive at this conclusion that a non-existent area has zero length through inductive reasoning: Say we have an area of length i, and then remove one element from the area, then it has length i-1.

+ +

As we repeat this, then eventually we will have an area of length 1 as discovered by our length assigning machine. Now we remove 1 more element.

+ +

Each time we removed an element before, it made the length smaller by 1, so we reason that 1-1 = 0. The area is now length zero.

+ +

We cannot go any further because there are no more elements to be removed.

+ +

In this case we arbitrarily imagined a longer area. No such longer area was stated in the problem formulation.

+ +

In real world programming, this is the difference between having a container that by implication will hold elements, and lacking the container entirely.

+ +

By applying the limit logic, we have implied that an area exists.

+ +

If running the area maker machine is asserting the existence of such an area, then it makes sense to speak of a zero length area.

+ +

However, if we are not making this assertion, then there is no area so the issue of length is irrelevant. This is a nuanced problem.

+ +

Note, then, that zero length is a second order concept. We cannot mark nor measure it, and given our first order definition for an area, nor can we even represent such an area at a first order.

+ +

There must be some external structure present that implies the existence of the area for zero length to even make sense.

+ +

This insight explains a lot of the pain related to the processing of end cases in computing. It will come up again in this volume when we examine the question of the emptiness of containers, see the section .

+ +

It also explains why loops so often need to be primed or given special case guards, which operates identically to adding a layer of analysis. We will discuss this further later when introducing the first-rest pattern see, .

+ +

It is interesting that non-existence has collided with zero length. This seems to be a contradiction, as something that does not exist should not have any length at all.

+ +

All of this happens at the second order, at the order of analysis. At this order we can make a distinction between an area that we have given a name to, and perhaps a location, as compared to an area for which we have done neither of these things.

+ +

Thus for purposes of second order analysis we will say that an area exists if it has a name or a location, even if it has zero length.

+ +

We will say an area does not exist if it has neither a name or a location. Again, execution of our first order area marking and length machines cannot provide us with any such information.

+ +

Need for the concept of cell

+ +

Let us ask a question, what is it that an address is actually locating? Let us consider this question in the light of an example. Suppose we have the tape sequence of:

+ + + a, b , c, ε, ε, ε … + + +

Now consider that we have an address of '2'. If we read tape address 2 we get back the letter 'c'. So the address is locating the 'c'. Now suppose we write at address 2. Say we write 'γ'. Now our sequence is:

+ + + a, b , γ, ε, ε, ε … + + +

Now we write 'Г', resulting in the sequence:

+ + + a, b , Г, ε, ε, ε … + + +

It would appear that the answer to our question is that address 2 is locating different things at different times. First it was, c, then γ, then Г. Yet the address did not change. It feels a little unsatisfactory to suggest that our concept of location depends on the value addressed. Also, notice, that when we created the Natural Number Turing Machine, that the values on the tape that was placed into correspondence with the Natural Numbers were never mentioned. Yet, we can't seem to answer the question of 'what is being addressed' without giving a value.

+ +

Addresses speak to the structure of the tape rather than the values held on the tape. So as to facilitate this interpretation, we note that a sequence consists of a sequence of cells holding elements, rather than being simply a sequence of elements. Given the concept of a cell we can say that an address always locates the same cell, though the contents of that cell can change.

+ +

This operates similarly to a small extension to the already existing concept of a variable in mathematics. In mathematics we allow that a variable can take on different values, though its name never changes. Now we are going to say that a cell can take on different values, though its address never changes. Furthermore, as the cell is part of the sequence, we are going to say the cell itself has a left neighbor or right neighbor, excluding the value in the cell.

+ +

Address of an area

+ +

The address of a cell is the number of steps required to reach the cell when starting from the leftmost cell on the tape. The leftmost cell has an address of zero. It might seem intuitive to set the address of an area on the tape to be that of the first cell in the area. If we require that an area have at least one cell to exist, this approach works even for machines with a cell delete command.

+ +

Suppose that we are deleting the cells in an area. The delete command affects the cell to the right of the cell the head is on. Hence to delete all the cells of an area, the head is placed on the left neighbor cell to the area. Say that we delete an area of three cells. We will call delete three times. It seems unsatisfactory to say the area no longer exists after the three deletes because the head locating the area never moved. I.e. we still have a location marker for the area, so we could, for example, call append and put a cell back into the area. For the area to truly not exist, it would not have a location.

+ +

Thus it seems more satisfactory to locate an area by its left neighbor cell, than to locate it by its leftmost cell.

+ +

The inverse case also suggests that the cell to the left of an area defines its address. I.e. if we call append to grow an area, it grows to the right of the cell the head is on. According to this definition of area location, the rightmost cell locates a currently zero length right tail of the tape that will potentially be grown.

+ +

In a sense what we are doing while exploring the meaning of area, existence, and zero length with these delete and append examples is performing a discrete limit in analogy to a limiting operation in calculous. To support having such a limit operation, the location of an area is that of the left neighbor of the leftmost cell in the area.

+ +

Not all is satisfactory. When using the left neighbor of an area as the location for an area, we cannot locate an area that starts on the leftmost cell of the tape. If we make this a special case, then we have failed to create a first order definition for area, because the special case information will have to be stored in a higher level structure that describes attributes of the tape.

+ +

Nor can this use the left cell approach work in the case of multiple areas.

+ +

Suppose we have two adjacent areas.

+ +
+ Tape with two areas +
Figure 2 Tape with two areas
+
+ +

Here we show a first area, say a0, that includes cells 7, 8 and 9. And a second area, say a1, that includes cells 10, 11, and 12. Thus, by the convention of using the address of the cell to the left of an area as the area’s address, a0 has an address of 6, while area a1 has an address of 9. Because the areas are adjacent, the address of area a1 is the same as the address of the rightmost cell in area a0.

+ +

We know that area a0 is located to the left of area a1 because a0's address is less than a1's. I.e. 6 < 9.

+ +

Suppose we delete cell 10. Though cell 10 is gone, our addresses remain consecutive, so what was cell 11, is now called cell 10, etc. and the diagram appears much as before, though area a1 is now only 2 in length:

+ +
+ Step by step area two becomes shorter +
Figure 3 Step by step area two becomes shorter
+
+ +

Now we delete the cell at address 10 two more times, and all the area a1 is gone. During the deletion, and just after, the head will be on the rightmost cell of area a0, i.e. on cell 9. We can now say we have an area of zero length located at cell 9.

+ +
+ Second area goes to zero length +
Figure 4 Second area goes to zero length
+
+ +

Now suppose after deleting the area a1, as just described, we continue on to delete area a0. Each time we delete the leftmost cell of a1 the rest of area a1 moves left by one. Hence, after the first deletion the address for empty area a1 becomes 8. Area a0 is still located to the left of a1, because 6 < 8:

+ +
+ Limit towards zero length being applied to the first area +
Figure 5 Limit towards zero length being applied to the first area
+
+ +

Finally when all cells in area a0 have been deleted, a1 has collapsed into a0. Both have the address of 6, so the order between them can no longer be derived from looking at the base addresses. Should we attempt to reverse the steps above, and only be given the machine at its final state we would have to begin by guessing what the order was between the two areas, a0 and a1.

+ +

So again, there is either missing information, or some information is not stored on the tape we are discussing.

+ +

As yet another problem case that is either not possible or requires external information, if an area includes the leftmost cell of the tape, then its location cannot be described with an address.

+ +

Hence this approach of using a discrete analogy to limits has led to some unsatisfactory end cases. We will visit this subject of areas on tape in the section, Area as a mapped tape and present a higher order approach for defining area location that avoids such end cases.

+ +
+ +

Multiple machines and sharing tapes

+ +

Suppose we unmount a tape from a halted Turing Machine, say machine T0, and then mount the tape on another Turing Machine as input, say machine T1. Suppose we do this so that machine T1 may calculate the length of the output created by the first machine. When we do this, we run into some problems.

+ +

Firstly, for a conventional Turing Machine, the tape that is mounted on T1 will be infinite, so no computational T1 machine will be able to process it unless there is a message on the tape telling T1 where the end of the input is. For our TTCA machines, if T0 starts with a null tape, and then expands it, and T0 is computational, then at the time T0 halts, the tape will be finite. T1 can then use the continuation of stepping beyond rightmost to know that it has processed all of its input.

+ +

If a Turing Machine does nothing then halts, it will implement an identity relationship between input and output. If we do not want the input given to a machine to 'bleed through', we will have to erase it. A computational TTCA Machine starting from a null tape can only produce finite tapes, so T1 can erase whatever T0 writes to the tape. However, if we are analyzing, instead of running the TTCA machine, we might discover that the tape length would be infinite if the machine could be run. We know that if we pass an infinite tape to a computational TTCA, it would not be able to erase the tape. Fortunately we can derive this fact through analysis and proof without having to run the computational machine to see if it erased the tape.

+ +

Hence if we allow for infinite inputs, we should add an 'erase-to-end-of-tape' command to our machines, so that the computational machines may leave a tape with only their outputs on them. Our erase-to-end-of-tape command will be executed in a single step.

+ +

Once an input tape is mounted, T0 is a computational TTCA machine given a finite tape, its output is a finite tape, and that is taken to be the input for T1.

+ +

T0 is required to provide meta information about the length of the active area. This approach is known as in-band signaling. Because length information is mixed with the data.

+ +

T0 updates a dedicated table where it keeps track of the location of data objects and their lengths. Such a table is called a symbol table, and such a system is called a type system. This is written to a separate tape, or it is inserted at a known location on the main tape. In the case it is written to the front of the tape, the data following will have to be moved when the table grows. In the case it is written at the end of the tape, the table will have to be moved when the data grows. In either case the symbol table entry lengths themselves must also be managed, typically they are either coded into the controller or make use of in band signaling. It is conceivable that a symbol table would describe itself.

+ +

We adopt a convention of maintaining a compact tape. As such we have no embedded empty-symbols in the active area. Then the empty-symbol marks the end of the active area.

+ +

Each machine has two tapes, an input tape, and an output tape.

+ +

A compact area is one that has only alphabet symbols (no empty-symbols). We can extend this concept to say that the density of an area is the ratio of alphabet symbols to empty-symbols.

+ +

The approach we use in modern computing is often the 3nd one listed above, that of the type system. We carefully account for the length of each instance of data. Then we build up each larger instance from smaller ones, and while doing so, we add the lengths of the smaller instances to calculate the length of the larger instance. All programs then specify when they create or compose instances and their types.

+ +

A system for creating the appearance of many tapes over a single tape is called a memory manager or a memory allocation system. It is typically better to use one of these and multiple tape models rather than solving the problem of moving objects around in memory in an ad hoc manner.

+ +

Here is an important question for the TTCA model: is it possible to create a memory manager that creates the appearance of multiple expanding tapes over the top of a single expandable tape? This problem is solved for managing files on a hard disk. However, all current file systems break at some point when pumped. So the question being asked here is equivalent to asking if a files system can be made without such architectural limits. The answer to this is yes, it is possible, as we will see in a later chapter of this book.

+ +

Hence there are multiple conventions we may use for implementing the abstraction of multiple tapes on a tape machine. Multiple tape machine are equivalent in power to one tape machines, but sometimes they are easier to think about.

+ +

Now as we have multiple tapes it is fairly easy to show that having multiple tape machines is equivalent to having one machine. We first view the multiple tapes worked on by the multiple machines as the multiple tapes of one machine. We then compose the state controllers in series, where the halt state of one is the initial state of the next one. Thus we may also conclude that having multiple tape machines is not more expressive than having one tape machine. We may also look at our multiple composed state controllers as one multiplexed controller, and then conclude that having multiple machines will not have an order of speed or space usage advantage.

+ +

The advantage of having multiple tape machines each perhaps having multiple tapes is that sometimes it is easier to think about. It is a method of partitioning the problem.

+ +

Suppose our machines have two tapes where one is called the input tape, and the other tape the output tape. Our procedure for passing tapes between machines will consist of umounting the tape from the source machine, then taking that tape over to the destination machine and mounting it as the input tape.

+ +
+ +

Some properties of Turing Machines

+ +

For a given Turing Machine, the input is the sole determiner of the output. I.e. each time the same input is given, we get the same output. The input and output are mathematical objects, hence Turing Machines are functions. However, it is common in computer science to speak of Turing Machines as 'solving problems' rather than saying they are functions. This is because we often think of the inputs and outputs of Turing Machines as being something other than mathematical objects. For example, when a Turing Machine sorts sequences found on its input, we might say that it solves a sorting problem.

+ +

Turing Machines will differ due to differing alphabets, state controller graphs, associated commands, start, and halt states. The choice of empty-symbol is inconsequential as long as it is distinct from the alphabet. As we will see in the later discussion on variations, the choice of alphabet is not very important. For two alphabets of the same cardinality we can create a one to one mapping, and for those of differing cardinalities we can use sequences of alphabet symbols that map to alphabet symbols. For example, given an alphabet of ‘T’ and ‘F’, and a second alphabet of ’t’, ‘f’, ‘x’, ‘z’ we may make the following map:

+ + + f + | FF + + t + | FT + + x + | TF + + z + | FF + + +

Then given this mapping, we may use two cells for each one cell for any {‘f’, ‘t’, ‘x’, ‘z’} alphabet machine, and then use the only the {‘T’, ‘F’} alphabet.

+ +

There are a countably infinite number of permutations for alphabets, state controller graphs, associated commands, start and halt states, hence there are a countably infinite number of Turing Machines that fit our definition. However, there are an uncountably infinite number of mathematical functions. Consequently, we must expect that some functions cannot be computed with Turing Machines.

+ +

There might be multiple Turing Machines that perform the same function. A set of such machines forms a functional equivalence class. Within a functional class there will be a class of members related in that they all use the smallest number of steps when considered against the limit of input length. We discuss this further in the section on complexity.

+ +

Of special importance to computation theory is the existence among all these infinite Turing Machines of a class of machines that read their state controller definition from the tape as an input. This is the Universal Turing Machine class.

+ +
+ +

Performance Analysis

+ +

An interesting aspect of the Turing Machine procedure is that it introduces the concept of stepping the machine. With the addition of some simple constraints it becomes possible to map the parts of the Turing Machine abstraction to the parts of some real machines. These constraints may take the form of such things as bounds on the length of the inputs, or the addition of out-of-resource errors. Because such constraints do not affect the 'normal' workings of the machine, the derived relationship between a Turing Machine step, and that of a unit of real time might not be that complicated. Indeed, except for some enumerable cases this relationship might even be so simple as to assign to a step an approximately constant amount of time.

+ +

Because of the existence of a relationship between steps and time, particularly when it is a simple one, it is very interesting for us to know how many steps a Turing Machine will take.

+ +

There are many ways to measure the complexity of a Turing Machine. Among these is something called the time complexity, which is a function that relates the length of the input to the number of steps required to reach the halt state.

+ +

To derive time complexity we typically start with a step count formula which maps the length of Turing Machine input to the worst case largest number of steps. We then consider the behavior of this formula as input length goes towards infinity. To get this, we take the highest order term from the step count formula. Conventional results are constant time, polynomial time, or exponential time.

+ +

We can derive the 'worst case length of the area written or read by the machine during computation' function in an analogous manner as for the step count function. This function is known as the space complexity. We may also consider the limiting behavior of this function to derive an order of space complexity.

+ +

The order of time or space complexity will remain the same against certain variations of our Turing Machine definition. For example, if we double all the states by adding a second state that we always visit, where this second state does nothing, the number of steps would double but the functionality would not change. Order of complexity also would not change. A fixed time machine before doubling up on the states would still be a fixed time machine afterward. It is simply that the number of steps would be twice as large, but still a fixed number relative to the size of the input. A polynomial time machine would still be polynomial time, simply with double size constants. We say that changes which do not change order of complexity, nor existence proofs, are inconsequential.

+ +

Suppose we have a complete Turing Machine functionality class. We say that it is complete because all possible machines for implementing the function are in this class. Some machines in this class will have a different order of time complexity than others. Now we consider the set of minimum order of time complexity machines from this class. As the larger set was complete, the set built against this constraint will also be complete relative to the constraint. We then say that this minimum order of time complexity is a property of the problem being solved, rather than being a property of a particular machine.

+ +
+ +

Conventional Turing Machine variations

+ +

In the first section of this chapter we gave a rather conventional definition for a Turing Machine. In the prior section, Complexity, we noted that we can analyze Turing Machines to find their time and space complexities. In this section we will discuss some variations that a person finds in the literature.

+ +

A variation on the conventional Turing Machine definition is allowed when it can be proven that the variation never causes existence, order of time complexity, nor order of space complexity results to change, and in this respect is inconsequential. Earlier we gave the example of doubling up the states as being such a variation, though that is an excluded variation typically.

+ +

Open in both directions tapes

+ +

Some Turing Machine descriptions describe a tape with no end in either the left or right directions, i.e. a tape that is open in both directions.

+ +

This feature adds no richness of expression, because we can get the same behavior from a Turing Machine with a single ended tape. To do this we partition the single ended tape into odd addressed cells and even addressed cells. The odd cells are said to be the right side of the tape, and the even ones the left side. We then rewrite any tape controller based on a bidirectional tape to instead use the 'odd' and 'even' channels instead of the left and right sides of the tape.

+ +

This same approach form can be used to show that multiple tapes, or even multi-dimensional tapes, add no expressive power. The good news is that such variations can be used whenever convenient, and we will get the same results.

+ +

Going in the other direction, the open in both directions tape is not a simplification. There is still a start cell, being the cell that the head is initially placed on. And as noted above, the topology around this start cell is no different, it is simply a question of the adjectives we use for describing it.

+ +

Alphabet replacement

+ +

Without loss of generality, we may replace the alphabet with a single symbol, say 's' (short for successor). This is because symbols in any alphabet can be placed into correspondence with a sequences of 's' symbols. For example, the symbols of the alphabet of {w, x, y, z} can be placed into one to one correspondence with the sequences in the manner of {<w, s>, <x, ss>, <y, sss>, <z, ssss>}. We will need to put the empty-symbol between any such sequences on the tape, so that two sequences can be distinguished from one longer sequence.

+ +

Alternatively we can adopt a two symbol alphabet instead of a single symbol, where the second alphabet symbol is an end of sequence marker.

+ +

In contemporary computing we use an alphabet of two symbols, {0, 1}, and fixed length sequences. No end marker is needed when the sequences to be placed into correspondence are of fixed length. So for example, the symbols in the alphabet {dog, cat, mouse, fish} may have the correspondence of {<dog, 00>, <cat, 01>, <mouse, 10>, <fish 11>}. Conventional fixed sequence lengths are 8, 16, 32, and 64. So for example, when the sequence length is 8, any alphabet of 256 symbols or less may be placed into correspondence. A conventional correspondence table is that of the ASCII code.

+ +

The fixed length to be used can depend on computational context. (In contrast, UTF8 does not use fixed length sequences, so there must exist at least one end of sequence marker.)

+ +

Another alternative to end of sequence markers for variable length sequences is to externally account for sequences lengths. We call such an accounting system a type system.

+ +

Leaving out the empty-symbol

+ +

When we use a fixed sequence of {1, 0} to stand for symbols, it is expensive to reserve a sequence for the empty-symbol. This expense is due both to losing the use of a symbol in the alphabet, and in the complexity of control circuitry when keeping track of it.

+ +

The empty-symbol functions as a property of the machine rather than simply another alphabet member, because the tape initially has an infinite tail of empty-symbols. A computational Turing Machine is limited to taking a finite number of steps. Thus it cannot compute a tape initialized with an empty-symbol (or any other value).

+ +

However we can add a constraint on all Turing Machine controllers that requires of controllers that they never write the empty-symbol, and always write an alphabet symbol to a cell before reading it. Then, because a cell is never read before being written, it does not matter what we write into it for initialization. We may even use an alphabet symbol. Consequently this constraint allows us to eliminate the empty-symbol. This gives us the following Turing Machine variation:

+ + + 1. fixed finite alphabet + 2. fixed write before read constraint + 3. variable single ended tape + 4. variable read/write head + 5. variable read value buffer + 6. fixed controlling state machine + 7. fixed left from leftmost error + 8. fixed start state + 9. fixed halt state + 10. variable current state + 11. fixed procedure for using these. + + +

To validate that this is an inconsequential Turing Machine variation, rather than a description of new abstraction that is not a Turing Machine, we must show two things: Firstly, that any of the now disallowed controllers never compute something that cannot be computed in the presence of the constraint. And secondly, that there are same complexity class alternatives for any disallowed controller.

+ +

No need to step into the inactive area

+ +

1. When attempting to step into the inactive area, instead keep a counter for the number of steps the machine would take. Only allow reads or writes or head movement when the counter is no longer needed due to the head having moved back into the active area.

+ +

2. Write an alphabet character and change the inactive area traversed into an active area.

+ +

The TTCA Turing Machine variation

+ +

The active area on the tape can grow at most by one unit for each machine step. This largest growth occurs when the Turing Machine steps right and writes an alphabet character in every visited state. This means that for computational machines that start with a tape that has a finite input (active area), the output (active area) will be finite. This also means that space complexity can never be larger than time complexity.

+ +

A fixed value is one that is provided with a Turing Machine definition, and does not change while the machine runs. Suppose we chose a fixed length Turing Machine tape. The tape would then have a rightmost cell. That cell would have no right neighbor, but would have a left neighbor. We would also add another error, that of right from rightmost. This error would be invoked when the controller attempted to step right from the rightmost cell.

+ +

Consider a machine that does not step out of the active area, has constant space complexity, and where this space complexity is less than the fixed length for the finite tape; such a machine would never trip the right from rightmost error, and thus there would be no difference between a finite tape and an infinite one.

+ +

Now suppose that we bound the length of the input, and that the maximum space required for such inputs or shorter ones is less than or equal to the length of the tape. Then again, the right from rightmost error would never be taken, and thus the tape would be indistinguishable from an infinite one. (Today we typically pad programs with lots of memory and long address words in hopes this will be the case.) An analogous argument can be made if we bound the number of steps that may be taken.

+ +

Now consider the case where we do not fix the length of the input, nor the number of steps allowed, and that space complexity is such that space usage grows with growing input length, at least for very long inputs. For such machines we can always find an input of sufficient length to trip the right from rightmost error.

+ +

As another approach to finite computing we can run computations twice. For a given input we first run the Turing Machine variation that does not step out of active area but still has an infinite tape. We watch this machine closely while it is running and find the bound on the active area. Now we can create a second machine that has a fixed length tape at least as long as our active area measurement but is otherwise the same. Now with this second machine we can run the same input and there will be no right of rightmost error, and thus there will be no difference between having the finite tape or an infinite one.

+ +

Unlike for the constant space complexity proposal, and the bounded input length proposal, which only work for small subsets of potential inputs, this 'run twice' proposal derives a finite machine that works for any given input which a Turing Machine works for. Though, unfortunately, in all cases the second run will be moot, as we could have simply taken the output from the first run.

+ +

In a variation on the run it twice approach, instead of running the first machine, we might instead analyze it and should we be able to surmise a maximum tape length, we could use that.

+ +

In yet another approach we can extend the finite tape as needed. We create a control layer over the finite tape. When a step right command from the Turing controller invokes the right from rightmost error, the lower layer allocates memory, lengthens the tape, and then performs the requested step right. As long as this occurs in fixed time, (or of sufficient lesser order time than the dominate order of the time complexity), and as long as there is indeed more memory to allocate - this Turing Machine variation will yield the same order of computational complexity as one with an an infinite tape.

+ +

I propose the following Turing Machine variation:

+ + + fixed finite alphabet + write before read constraint + variable and extendable finite tape + variable read/write head + variable read value buffer + fixed controlling state machine + fixed left from leftmost error + fixed right from rightmost error + fixed start state + fixed halt state + variable current state + fixed procedure for using these + + +

In our original Turing Machine model, the controlling state machine commands were limited to, do-nothing, step-left, step-right, write, with reading as an implied command. To this list we add append. The append command may only be called when the head is on the rightmost tape cell. This is not limiting because the command may be called from a state that is at the end of an arc triggered by the right from rightmost error. When we have no empty-symbol, append accepts an alphabet symbol and performs a write into the new cell. This is not limiting because if need be, a person can always perform an extraneous write of an alphabet symbol.

+ +

With this extendable tape model all Turing Machine components remain finite during computation, though some are arbitrarily large. This variation is more suited for creating a mapping between a Turing Machine and a real program running on a real machine. Specifically, a computation requiring N cells maps exactly to a physical machine possessing at least N cells. The theoretical model and the physical implementation remain perfectly isomorphic provided the physical environment does not exhaust its memory bounds. Because a computational TTCA machine demands strictly finite tape extensions, a physical machine that completes the execution maintains exact structural correspondence. This isomorphism holds continuously as long as memory remains available, even accommodating dynamic hardware expansion such as memory hot-swapping. The theoretical correspondence fails solely upon a hard physical limit being reached during execution.

+ +

More about commands

+ +

The Turing Machine state controller has a command symbol tied to each state. The Turing Machine procedure then has us take action based on this symbol. This is our current command set:

+ + + step-right, causes the head to move to the right neighbor cell. + step-left, causes the head to move to the left neighbor cell. + read, returns the symbol under the head. The returned symbol is then used to chose the transition arc. + write(x), causes the symbol x to be written to the cell that the head is currently on. The symbol x is any symbol from the alphabet. + append(x) may only be called when on the rightmost cell. Extends the tape by one cell, and writes the symbol x into that cell, where x is any symbol from the alphabet. + + +

We are going to relieve the constraint that append can only occur from rightmost. Our new append is functionally identical to adding a cell to the rightmost extremity, and then shifting all the symbols over by one cell starting at the new cell and ending when the new rightmost has been written - and then doing the requested write of x on the right neighbor cell.

+ +

We will also include the inverse function for append. delete(append(x)) reads x while deleting the cell that x was in. The current Turing Machine model can emulate this function by shifting all the symbols in cells the right of the head left by one, and then simply not using the rightmost cell.

+ +

We are also going to support multiplexed state controllers. Our multiplexed Turing Machine will have multiple heads. One for each separate thread of execution through the state controller. As explained in the following chapters, supporting multiplexing makes our machine more complex, especially in the presence of the delete command. However, we defer that discussion to the relevant chapters.

+ +

In cases where successive states are visited in a fixed order it is convenient to combine the commands. We have developed the concept of a command statement to support this:

+ + + statement::[direction]command+[modifier][&contract]*[arg]* + direction::- | ε + command::r | w | s | a | d | m | e | ☥ + modifier::◧ ◨ n + command + r read cell under the head + w write cell under the head + s step + a allocate/add/append a new cell + d deallocate/drop/delete cell + m move, no allocation or deallocation of cells, requires fill + e entangled copy + ☥ entangled copy on a new thread + modifier + ◧ operate on leftmost + ◨ operate on rightmost + n repeats n times, n provided through an argument + contracts + h◧ head is at leftmost + h◨ head is at rightmost + examples + -s ; step left + a◧ ; creates a new leftmost cell + a◨ ; append to rightmost + sn ; step n times + to derive a longer command, combine them: + as ; append then step + -a-s ; append to the left, step to the left + + +

The left direction is specified with a minus sign, otherwise the direction is taken as right going. So the letter s is the step-right command, and -s is the step-left command. The command s3 steps right three times.

+ +

The command a appends and writes a new cell to the right of the head. We use two special characters from the UTF character set to signify the rightmost and leftmost of the tape. This one looks like a little tape with its left cell inked in, ◧, so we use it to stand for leftmost. We use this one, ◨, to mean rightmost. Hence a◨ creates a new rightmost cell, and a◧ creates a new leftmost cell.

+ +

In some cases it is possible to implement higher performance implementations for commands when the programmer tells us some additional information. For example a◨&h◨ has identical functionality as a◨, while the programmer also guarantees that we are on the rightmost cell. This saves the function from having to scan to the end of the tape.

+ +

We can concatenate the command letters into a string to summarize what would happen sequentially in adjacent state transitions. If these compound commands need arguments, then they are pulled from the argument list in order as they are needed. For example, as means to append, with the parameter for the append taken from the argument list, and then to step.

+ +

We support multiplexing with the command e, which is short for entangled. This operator returns what appears to be a second independent machine, but this apparently independent second machine actually shares the same tape with the first machine. It is functionally identical to giving one machine two heads, and thus the ability to have state sets.

+ +

The command esr is a compound command referring to sequentially applying three other commands. The e says to make an entangled copy of the head. The s says to step this copy, and the r says to do the read. The analogous esw does a write as the last step. This sort of combination of letters to make more complex commands was inspired by Lisp's car and cdr compositions. Though this is functionally what the command does, its actual implementation might be completely different.

+ +

The combination of multiplexing and cell deletion creates the hazard where one thread can delete a cell the head is on in another thread. We add a collision error continuation to our multiplexed Turing Machine interface because of this situation.

+ +

Chapter conclusion

+ +

The modifications made to the Turing Machine to create our TTCA machine were inconsequential, in that for order of complexity and existence proofs we may swap one machine for the other and the results will be the same.

+ +

The original Turing Machine had an infinite tape. In contrast the TTCA machine has a surprising property: for computational problems all of its components remain finite. This follows from the fact that during computation a machine makes a finite number of steps, so the tape can only be expanded to be a finite size.

+ +
+ +

TTCA Turing Machine in Lisp

+ +

Because our TTCA Turing Machine has finite sized components, we may create a software model for the TTCA Turing Machine without having to make assumptions of the sort that ‘very large approximates the infinite’. Rather we can show a one-to-one mapping of code and data in the software model and the TTCA variation of the Turing Machine. Consequently, the software model and theoretic model are isomorphic.

+ +

It follows that we can use our TTCA Turing Machine software to make theoretical statements about computation in the form of programs. By following this path we will learn some interesting things in this chapter about analysis, the meaning of emptiness, data type, the properties of non-destructive vs destructive programming styles, and multi-threaded programming among other things.

+ +

The Turing Machine, and our variation of it, may be partitioned into two parts. One part of the part consists of the tape head and the tape. We call this the Tape Transport Unit, as that is the name used for the mechanism that does this work on real tape storage units. The other part of the part is the Controller.

+ +

The Tape Transport Unit accepts commands for reading, writing, and moving the head. (In real Tape Transport Units the head is in a fixed position and we move the tape, but the relative affect is the same.) Our modified model adds commands for extending the tape. During normal operation these commands only come from the controller.

+ +

We have two types of controllers. One type of controller is a state machine. Its design is an integral part of the Turing Machine. To step the Turing Machine means to step this state machine to its next state. The state machine definition exists before the Turing Machine takes its first step, and its definition remains intact for as long as said Turing Machine exists.

+ +

When we speak of a Turing Machine without adding further words to the term as qualifiers, we mean that it uses this type of controller. For clarity we can call this a Directly Controlled Turing Machine.

+ +

The second type of controller is the Universal Controller, and a Turing Machine that uses this type of controller is called a Universal Turing Machine. The Universal Controller reads the definition of a state machine controller off of the tape. Hence the Universal Turing Machine may emulate any Directly Controlled Turing Machine. We can also call this an Indirectly Controlled Turing Machine. Here the qualifier ‘Indirectly’ is intended in the sense it is used in assembly languages, meaning the data is not present in the controller, but rather it must be fetched from memory.

+ +

With our Tape Machine library, your program is the controller, and the library implements the Tape Transport Unit. (In the future I will rename this library to ‘TTU’ from the current ‘TM’. Also the use of the term ‘function’ will be changed to ‘routine’.)

+ +

Your program that uses the TTU library is in a sense direct control, because the program is already defined before the first step of the machine, and with the possible exception of self-modifying code, it does not change until the program exits, and thus the process no longer exists. On the other hand, your program is loaded from memory by the processor, thus demonstrating that the processor is an indirect, i.e. universal, controller. Either interpretation works depending on how broad of a view we want to take.

+ +

Installing the library

+ +

TM is presented as an iteration library on the de facto package manager for Lisp quicklisp. Alternatively, a person may clone the repository www.github.com/Thomas-Walker-Lynch/tm and then checkout the latest release tag, which as of this writing is v0.7-alpha. After installing the code cd into the tm directory and run your lisp interpreter. Inside your lisp interpreter type the commands (load “load”) and (test-all). test-all should return with a message that all of the tests passed.

+ +

It is possible that the threading tests, 'ts1-' might fail if your machine is heavily loaded or very slow, as they have timing built into them, but this is unlikely. Then type either (use-package :tm) or (in-package :tm) depending on what your objectives are.

+ +

The examples in this chapter either come from the tm/test directories, or from the tm/docs/examples directory. At the time they were placed in the book, they executed correctly, and I have endeavored to keep the examples up to date.

+ +

This is what it looks like when I follow install using the git clone method:

+ + + > git clone http://www.github.com/Thomas-Walker-Lynch/tm + Cloning into 'tm'... + remote:Counting objects: 3052, done. + remote:Total 3052(delta 0),reused 0(delta 0),pack-reused 3052 + Receiving objects: 100% (3052/3052),2.41 MiB|1.52 MiB/s,done. + Resolving deltas: 100% (2366/2366),done. + > cd tm + > git tag + v0.1-alpha + v0.7-alpha + > git checkout v0.7-alpha + Note: checking out 'v0.7-alpha'. + You are in 'detached HEAD' state ... + > sbcl + This is SBCL 1.3.14.debian ... + * (load "load") + ; compiling file "/home/tm/package-def/conditions.lisp" + ; compiling (IN-PACKAGE #:TM) ... about 10 pages of these + hooking test: TEST-TS1-5 + T + * (test-all) + + TEST-UNWRAP-0 + ... about a page of these tests + + TEST-TS1-5 + all 78 passed + T + * (use-package :tm) + T + * (≠ 1 0) + T + * + + +

Notice I used git tag to see the releases. At this time, v0.7-alpha is the latest, so I checked that out. If you want the unstable latest code rather than the stable latest release, leave out the git checkout command.

+ +

The TM Library makes use of Unicode. There is no getting around it. This is discussed further in the next section. In addition TM defines synonyms for commands such as 'not equal', which is the one command shown at the end of the transcript given above.

+ +

Unicode usage

+ +

For your convenience there is a file "emacs-keys" in the docs directory of the distribution. It sets the C-x g name SPC command to enter one of the Unicode characters that are used in the library. Here 'name' is a nickname.

+ +

So to type the character capital delta after emacs-keys has been loaded, type C-x g D SPC. Actually Δ occurs twice in Unicode, once as capital delta, and once as a symbol for 'increment' in mathematics. We consider the increment version to only be there for typography purposes. We only use capital delta, even when it is for an increment variable.

+ +

I've limited the use of Unicode mostly to things that 'probably would have been this way had Unicode been around before'. This includes conventional notation and a couple of symbol extensions that were needed to facilitate the TM access language.

+ +

In the file src-0/fundamental.lisp find synonym bindings for the usual operators and common symbols such as ∧, ∨, ≥, ≤, λ, ∅, etc.

+ +

Specific to the library we use the character '◧' as shorthand for 'leftmost'. This is because it looks like a little tape with the inked over cell being the leftmost cell. In the same manner the character '◨' is shorthand for rightmost.

+ +

We use '➜' in continuation function names, and '⟳' as a loop operator.

+ +

The ◧ and ◨ symbols are used in compound command names and for access language statements to indicate operation on rightmost or leftmost rather than the cell the head is on, or, to specify contracts with the programmer of the sort: "this function is only called when the cell is on rightmost."

+ +

Since I had symbols for leftmost and rightmost, I started using them generally to mean leftmost or rightmost wherever it was convenient. For example, to shorten up the names of continuations so that parameter lists would fit on a line.

+ +

Synonyms

+ + + (defmacro defsynonym (old-name new-name) + "Define OLD-NAME to be equivalent to NEW-NAME." + `(defmacro ,new-name (&rest args) `(,',old-name ,@args)) + ) + (defconstant ∅ nil) + (defsynonym /= ≠) + (defsynonym <= ≤) + (defsynonym >= ≥) + (defsynonym not ¬) + (defsynonym and ∧) + (defsynonym or ∨) + (defsynonym string/= string≠) + (defsynonym string<= string≤) + (defsynonym string>= string≥) + (defsynonym lambda λ) + + +

Some reader macros

+ +

q – a non literal quote

+ +

In Lisp a quoted list is taken as being literal. However the result of modifying a literal is undefined, and often leads to bad results. Hence we provide the macro q which returns a quoted list which is not a literal.

+ + + * (q a b c) + (A B C) + + +

{…} - unevaluated list

+ +

When a form enclosed in parentheses, ( ... ), is evaluated the head is taken as the name of a function, looked up and called. The list members are also evaluated, and then passed as arguments to said function. If we don't want the head treated specially, but rather want to define a list, we can use a front item of #'list, which is the function to create a list.

+ + + * (list 1 2 (+ 1 2)) + (1 2 3) + + +

We have defined a macro called L that like #'list, creates a list, but which also has some extra functionality.

+ + + * (L 1 2 (+ 1 2)) + (1 2 3) + + +

We also provide a reader macro for L as braces.

+ + + * {1 2 (+ 1 2)} + (1 2 3) + + +

If the apparent function open, #'o, appears inside of a call to L, then the arguments of the #'o function are included directly in the resulting list:

+ + + * (defvar a {1 2 3}) + A + * (defvar b {4 5 6}) + B + * {a (o b)} + ((1 2 3) 4 5 6) + + +

Quoted non-literals can also occur within such an L list:

+ + + * {a (o b) (q a b)} + ((1 2 3) 4 5 6 (A B)) + * {a (o b) (o (q a b))} + ((1 2 3) 4 5 6 A B) + + +

L acts identically to a quasiquote turned inside out. Whereas the default in quasiquote is to quote items, and a comma operator turns that off, the default in L is to evaluate items, and a q operator turns that off. Quasiquote has an @ marker to open up lists, while L has an o operator to open up lists. Inside of a quasiquote we could get in trouble if the name of a variable starts with an @ character; if such a variable appears after a comma, quasi quote will consider the variable name without the @ sign is to be opened and included. There is no analogous problem with the o operator because it only appears in the function channel. (The problem with quasiquote is that it uses 'in-band signaling' which mixes control with data in one channel.).

+ +

[...] - head is variable holding a function name

+ +

In Lisp the head of an evaluated list is taken as a function name. Consider this example that curries a two parameter function into a unary function by replacing one parameter with a constant argument of 3:

+ + + (defun curry-three (f n) (f n 3)) ; has errors + + +

When we compile this function we get two errors:

+ +

The variable F is defined but never used.

+ +

and

+ +

undefined function: F

+ +

The first f is in the parameter list of the function definition, so it is taken as a variable name. In contrast the f in the body is at the head of an evaluated list, so it is taken as a function name. Hence there is a disconnect, and we get error messages describing this disconnect.

+ +

The Lisp operator #' indicates that the symbol that follows is a function name to be taken literally, and not a variable name. This gets our function name into the data space for use as an argument. The Lisp function funcall accepts as a first argument the name of a function to be called, while the remaining arguments are passed through to said function as its arguments:

+ + + (defun curry-three (f n) (funcall f n 3)) + (defun plus (x y) (+ x y)) + (curry-three #'plus 2) + 5 + + +

In the definition for curry-three we do not get interpreter/compiler errors. This is because f is consistently used as a variable name. funcall will use the value of the variable f as the name of a function to call.

+ +

In the second line we define a function to pass into curry-three. I put something simple here for sake of discussion. plus is defined to be a function that takes two arguments and sums them.

+ +

In the third line we use the #' operator to tell Lisp that plus is a function name to be used literally as a value. This will be a value passed into curry-three no differently than had we put a number or string instance as an argument. Inside of curry-three the #'plus becomes the value of the variable f. Then the funcall function will use this value as a function name, and then call it.

+ +

This is how function pointers are handled in Lisp as it does not have an explicit pointer type.

+ +

Actually we didn't need to define the function plus, because '+' is already a function. We don't have reserved operator symbols in Lisp, instead we have loose rules on what can be used for function names.

+ + + (curry-three #'+ 2) + 5 + + +

We introduce a shortcut with the TM Library. Normally a list to be evaluated is in parenthesis, and its head is taken literally as a function name. With the TM Library loaded, when a list in square brackets is evaluated, the head is taken as a variable name, and the value of this variables is the function to be loaded. It is a nice coincidence that square brackets mean indirect addressing in many assembly languages.

+ +

We implemented this feature with a reader macro which simply turns the square bracket list into a regular list and inserts the funcall as its head. This occurs before the Lisp evaluate phase sees the syntax.

+ +

Using the square brackets we may define curry-three as:

+ + + * (defun curry-three (f n) [f n 3]) + CURRY-THREE + * (curry-three #'plus 2) + 5 + + +

Here is another example. Suppose that instead of passing #'plus in as an argument, that we first assigned it to a variable and then pass the variable value as an argument:

+ + + * (defvar our-fun #'plus) + * [our-fun 4 7] + 11 + * (curry-three our-fun 2) + 5 + + +

Here the value assigned to the variable our-fun is a function name. The variable is then used as any other, and its value is passed in as an argument to curry-three. Note, all arguments are evaluated before the function is called, so variables are replaced with their values. And as we know, inside curry-three, funcall will take the function name value from the corresponding parameter and call it as a function.

+ +

Things can become a little confusing when the variable name has the same name as the function.

+ + + * (defvar plus #'plus) + * (curry-three plus 2) + 5 + + +

Here #'plus is the function name as data. plus when it appears as the head of an evaluated list is a function name, otherwise it is a variable name. It is little wonder that the Lisp dialect Scheme put function names and variables name in the same space. However by doing so they had to provide some automatic conversions between variable names and function names.

+ +

Summary

+ +

When evaluated:

+ + + +
+ + +