Skip to main content

Property-based testing utilities for finite automata and context-free grammars.

Project description

pbt4automata

Tests Python Version

pbt4automata provides tools to construct finite automata and context-free grammars in Python and validate their behavior with property-based tests powered by Hypothesis.

Installation

pip install -e .

Examples

Deterministic Finite Automata

Testing a DFA against a rule

The following example tests a DFA that accepts all strings that contain the substring "010" or "100".

from pbt4automata import DFA

automaton = DFA(
    states=["q0", "q1", "q2", "q3", "q4", "q5"],
    alphabet="01",
    transition_function={
        ("q0", "0"): "q1",
        ("q0", "1"): "q4",
        ("q1", "0"): "q1",
        ("q1", "1"): "q2",
        ("q2", "0"): "q3",
        ("q2", "1"): "q4",
        ("q3", "0"): "q3",
        ("q3", "1"): "q3",
        ("q4", "0"): "q5",
        ("q4", "1"): "q4",
        ("q5", "0"): "q3",
        ("q5", "1"): "q2",
    },
    start_state="q0",
    accept_states=["q3"],
)

result = automaton.test("[01]*(010|100)[01]*")

if result is True:
    print("Success!")
else:
    print("Counterexample:", result)

You can also pass a function of type Callable[[str], bool] to automaton.test(...) instead of a regex.

Testing two DFAs for equivalence

from pbt4automata import Automaton, DFA

automata1 = DFA(
    ...
)

automata2 = DFA(
    ...
)

result = Automaton.test_equivalence(automata1, automata2)

if result is True:
    print("Success!")
else:
    print("Counterexample:", repr(result))

Nondeterministic Finite Automata

Testing an NFA against a rule

NFAs support nondeterministic transitions (multiple possible next states for a given state and symbol) as well as epsilon (ε) transitions. The transition function maps (state, symbol) pairs to sets of states; use None as the symbol for ε-transitions. Unlike DFAs, the transition function may be partial.

The following example tests an NFA that accepts all strings over {0, 1} that end with the substring "01".

from pbt4automata import NFA

nfa = NFA(
    states=["q0", "q1", "q2"],
    alphabet="01",
    transition_function={
        ("q0", "0"): {"q0", "q1"},  # nondeterministically guess start of "01"
        ("q0", "1"): {"q0"},
        ("q1", "1"): {"q2"},
    },
    start_state="q0",
    accept_states=["q2"],
)

result = nfa.test("[01]*01")

if result is True:
    print("Success!")
else:
    print("Counterexample:", result)

You can also pass a function of type Callable[[str], bool] to nfa.test(...) instead of a regex.

NFA with epsilon transitions

from pbt4automata import NFA

# Accepts "a" or "ab"
nfa = NFA(
    states=["q0", "q1", "q2", "q3"],
    alphabet="ab",
    transition_function={
        ("q0", "a"): {"q1"},
        ("q1", None): {"q2"},   # ε-transition: q1 is also effectively q2
        ("q1", "b"): {"q3"},
    },
    start_state="q0",
    accept_states=["q2", "q3"],
)

Testing an NFA for equivalence with another automaton

Automaton.test_equivalence works with any mix of DFA and NFA objects sharing the same alphabet.

from pbt4automata import Automaton, DFA, NFA

nfa = NFA(...)
dfa = DFA(...)

result = Automaton.test_equivalence(nfa, dfa)

if result is True:
    print("Success!")
else:
    print("Counterexample:", repr(result))

Context-Free Grammars

Testing a CFG against a rule

The following example tests a CFG that generates all non-empty strings of balanced parentheses.

from pbt4automata import Grammar

grammar = Grammar(
    terminals="()",
    nonterminals="S",
    productions={
        "S": ["(S)", "SS", "()"],
    },
    start_symbol="S",
)

def check_balance(input_string: str) -> bool:
    if input_string == "":
        return False
    s = 0
    for c in input_string:
        if c == "(":
            s += 1
        elif c == ")":
            s -= 1
        if s < 0:
            return False
    return s == 0

result = grammar.test(check_balance)

if result is True:
    print("Success!")
else:
    print("Counterexample:", result)

You can also pass a function of type Callable[[str], bool] to grammar.test(...).

Parsing strings

from pbt4automata import Grammar

# Grammar for "a^n b^n" (n ≥ 1): S → aSb | ab
grammar = Grammar(
    terminals="ab",
    nonterminals="S",
    productions={
        "S": ["aSb", "ab"],
    },
    start_symbol="S",
)

print(grammar.parse("ab"))       # True
print(grammar.parse("aabb"))     # True
print(grammar.parse("aaabbb"))   # True
print(grammar.parse("aab"))      # False

Development

Install dev dependencies and run tests:

pip install -e ".[dev]"
pytest -v

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pbt4automata-0.2.1.tar.gz (16.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pbt4automata-0.2.1-py3-none-any.whl (9.9 kB view details)

Uploaded Python 3

File details

Details for the file pbt4automata-0.2.1.tar.gz.

File metadata

  • Download URL: pbt4automata-0.2.1.tar.gz
  • Upload date:
  • Size: 16.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for pbt4automata-0.2.1.tar.gz
Algorithm Hash digest
SHA256 88e09d8e7625e5c14c0bf99dd2ddb19f2534accb9ffcd3005d28923104edd1de
MD5 2e65c75baa20db71747b7e86da1d707b
BLAKE2b-256 5a0e0a38ea874a8a4f75b6ece90aca2059cb32dae260fd6fcd79e18ad3d61311

See more details on using hashes here.

Provenance

The following attestation bundles were made for pbt4automata-0.2.1.tar.gz:

Publisher: python-publish.yml on mrigankpawagi/pbt4automata

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pbt4automata-0.2.1-py3-none-any.whl.

File metadata

  • Download URL: pbt4automata-0.2.1-py3-none-any.whl
  • Upload date:
  • Size: 9.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for pbt4automata-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 4026072772a155b3801219f0b2b57f28c14962abe75c55deddf265c0b6359148
MD5 a8c9396695860824f61311e9bdbe2042
BLAKE2b-256 86c12955d10752a65a9339210bb2ea298c4cf4f712644ffdc6b4dbccb6631fd3

See more details on using hashes here.

Provenance

The following attestation bundles were made for pbt4automata-0.2.1-py3-none-any.whl:

Publisher: python-publish.yml on mrigankpawagi/pbt4automata

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page