Case Study: Finite Automata

Graphs are a foundational data structure in many areas of computer science. To close this portion of the course, we'll take a look at a particular application of graphs towards an older theme: program correctness. We'll use graphs to develop a simple model from the theory of computation, the finite automata. While simple, the model captures a wide variety of computations that we might consider in a computer program. This model will put together everything we have learned so far towards the task of using mathematics productively in programming and tease at what future courses in the foundations of computer science will cover!

Strings

A finite automata is an abstract machine that consumes strings of characters as input and produces a boolean, a yes or no answer, as output. Before we explore the finite automata itself, we must first formalize the notion of a string.

Definition (String)

A string drawn from an alphabet is a sequence of zero or more characters drawn from , i.e., where .

Note that when we write , this implicitly binds to the individual characters of . Because there are variables bound in this manner, we know that the string has length .

Unlike strings in a programming language, we explicitly define the set of possible characters that make up our strings. We call this set the alphabet under consideration and denote it with (: \Sigma). Here are some examples of alphabets and possible strings drawn from those alphabets:

Examples

Example 1: let be the set of the 26 lowercase English letters and the space characters. Then is a string drawn from .

Example 2: let . Then is a string drawn from . Strings drawn form are binary strings, i.e., sequences of 1s and 0s.

Example 3: note that the definition of string is a sequence of zero or more characters. Therefore, the empty string, written (: \epsilon), is a string drawn from any alphabet, including or as defined above.

In a conventional programming language, we would write the empty string as "". However, since we traditionally do not use quotes to delineate strings, we must rely on a special symbol to denote the empty string.

Overview of Finite Automata

Now let's look at our first finite automata to get a feel for this sort of abstract machine.

    ┌─────────┬────────┐
    │         a        b
    ↓         │        │
-→ (q0) -a→ (q1) -b→ (q2) -a→ [q3]──┐
   │  ↑                         ↑  a,b
   └b-┘                         └───┘

This simple finite automata recognizes strings drawn from the alphabet . We can think of a finite automata as a labeled, directed graph where the nodes are states and the edges are transitions between states.

Informally, a finite automata operates as follows:

  1. The machine begins initially in its start state, denoted in the graph above as the state with an incoming edge but no out-going state.
  2. Next it reads in an input string character-by-character. As it reads each character, the machine transitions from its current state to a new state by moving along the edge annotated with the character that was read in.
  3. Once the input string is completely consumed, we check to see what state the machine ended on. If the state is an accepting state, then the machine accepts the input string, i.e., returns "true." Otherwise, the machine rejects the input string, i.e., returns "false." In our example, we denote a final state in brackets rather than parentheses, so above is an accepting state whereas all the other states are normal.

As an example, here is how the machine operates over the string :

  • The machine initially starts in .
  • The machine reads an and transitions from to .
  • The machine reads a and transitions from to .
  • The machine reads an and transitions from to .
  • The machine reads a and transitions from back to .
  • The machine reads an and transitions from back to .

So when reading the string , the machine ends on state . is not an accepting state, so the automata rejects this string. In contrast, the machine would accept the string .

Exercise (Simulation)

Trace the execution of the finite automata above on the input string to show that the automata accepts this string.

With some effort, we can also verify that the strings:

  • ,
  • , and
  • ,

Are accepted by this automata, whereas the strings

  • ,
  • , and
  • The empty string

Are not accepted by this automata.

What are the set of strings that the automata accepts? It turns out that the automata accepts any string that contains ! We can observe this by inspecting the states and transitions of the automata and ask the question: how do we reach the acceptance state ?

We can only reach this state by moving from through and and finally to . According to the transitions, we can only do so by reading in the characters , , and in order. Also note that:

  • If we have not seen yet, then any character not involved in this pattern returns us to , i.e., our search for this substring resets.
  • Once we have seen and land in , anything that we read keeps us in this acceptance state. In other words, once we reach , we will always accept the string!

Finite Automata Formally Defined

Now that we have a high-level idea of how finite automata operate, let's look at their formal description.

Definition (Finite Automata)

A (deterministic) finite automata is a 5-tuple where:

  • is the set of states.
  • is the alphabet.
  • is the initial state.
  • is the state-transition function.
  • is the set of accepting states.

Note that even though the automata is a directed graph, we do not model it directly as such! Instead, we model it as a collection of five components---the set of states, alphabet, initial state, transition function, and accepting states. The graph-like nature of an automata is inferred by this choice of representation:

  • States are the nodes.
  • The transition function contains the (directed) edges.

In particular, note that the transition function, when viewed as a relation, can be thought of a set of pairs, just like the edges of a graph!

Our example automata from above can be formally represented as follows:

  • .
  • .
  • is the initial state.
  • .

The transition function is defined by the following transition pairs expressed in a state transition table:

 

For example, in state , if the automata reads an , then the automata transitions to state .

Example

Example: define an automata as follows:

  • .
  • .
  • .

Observe that there are two states and two characters of our alphabet. Therefore, we expect there to be state-transition pairs in . We'll, therefore, define the transition function by cases as follows:

Exercise (Formal Picture)

Draw the graph representation of the automata from the example above.

Acceptance

In order to formally verify that an automata is correct, we also need to formalize the notion of acceptance. Acceptance has a somewhat complicated definition; let's take a look:

Definition (Acceptance)

Consider a finite automata and be a string drawn from . We say that automata accepts string if there exists a sequence of states where:

  • and
  • .

Intuitively, this definition says that an automata accepts a string if the string drives the automata from its starting state to a final state.

Exercise (Back and Forth)

Take this intuition about what the formal definition of automata acceptance is saying and try to map the intuition on the symbols. In particular, how does the formal definition capture the idea that the input string "drives the automata from its starting state to a final state?"

Exercise (Scrutinize)

According to the formal definition of acceptance, does an automata accept a string if during execution of that string, the automata enters a final state, but is not in a final state at the execution?

Exercise (Spell It Out, ‡)

Consider the formally defined automata in the example above that reads binary strings. Apply the definition of acceptance to show that accepts the string .

(Hint: what does the formal definition of acceptance say we must construct and what must we show about this construction to show that accepts the string?)