Skip to content

Latest commit

 

History

History
81 lines (66 loc) · 3.09 KB

README.md

File metadata and controls

81 lines (66 loc) · 3.09 KB

Pushdown Automata

This example illustrates the construction of a Pushdown Automaton. Although simplistic, it demonstrates how Pushdown Automata can be seen as Network Automata. A Pushdown Automaton can be thought of as a Network Automaton with a single node.

Pushdown Automata are more powerful than Finite State Machines (though less powerful than Turing Machines). They differ from Finite State Machines by having access to a memory, in the form of a stack.

In this example, a Pushdown Automaton (PDA) is constructed for the language L = {anbn | n > 0}. The PDA is defined by four states: q0 (the initial state), q1, q2, and q3 (the final/accepting state). The input alphabet consists of the symbols a and b. The stack alphabet consists of the symbols a, b, and Z, where Z represents the bottom of the stack.

In the diagram above, the first character in the transition represents the input, while the "X / Y" notation means: replace the symbol "X" on the top of the stack with the symbol "Y". When "Y" is ε, this refers to a stack pop operation. When ε is an input symbol, it represents the end of the input (or the input symbol signalling termination of the string).

import netomaton as ntm

states = {
    'q0': 0,  # initial state
    'q1': 1,
    'q2': 2,
    'q3': 3   # final/accepting state
}

# a Pushdown Automaton can be thought of as a Network Automaton with a single node
network = ntm.topology.from_adjacency_matrix([[1]])

# the Pushdown Automaton starts off in the q0 state
initial_conditions = [states['q0']]

# 'Z' is the symbol representing the bottom of the stack
stack = ['Z']

# '\n' is the symbol representing the end of the input
events = "aaabbb\n"

def pda_rule(ctx):
    current_state = ctx.current_activity
    if current_state == states['q0'] and ctx.input == 'a' and stack[-1] == 'Z':
        stack.append('a')
        return states['q1']
    elif current_state == states['q1'] and ctx.input == 'a' and stack[-1] == 'a':
        stack.append('a')
        return states['q1']
    elif current_state == states['q1'] and ctx.input == 'b' and stack[-1] == 'a':
        stack.pop()
        return states['q2']
    elif current_state == states['q2'] and ctx.input == 'b' and stack[-1] == 'a':
        stack.pop()
        return states['q2']
    elif current_state == states['q2'] and ctx.input == '\n' and stack[-1] == 'Z':
        return states['q3']
    else:
        raise Exception("input rejected")

try:
    trajectory = ntm.evolve(initial_conditions=initial_conditions, network=network,
                            input=events, activity_rule=pda_rule)
    activities = ntm.get_activities_over_time_as_list(trajectory)
    print("'%s' accepted (final state: %s)" % (events.strip(), activities[-1][0]))
except Exception:
    print("'%s' rejected!" % events.strip())

The program above will print 'aaabbb' accepted (final state: 3). If the event variable is changed to aabbb\n, the program will print 'aabbb' rejected!.

The full source code for this example can be found here.