Post Canonical Systems - A Python implementation of Emil Post's formal string rewriting systems
Project description
Post Canonical Systems
A Python implementation of Emil Post's formal string rewriting systems for computation and formal language exploration.
What is this?
Post Canonical Systems are a foundational model of computation developed by mathematician Emil Post in the 1920s. They define formal languages through string rewriting: starting from initial words (axioms) and repeatedly applying production rules to generate new strings. Despite their simplicity, Post systems are Turing-complete and serve as an elegant framework for exploring computability, formal grammars, and proof derivations.
Features
| Feature | Description |
|---|---|
| Pattern Matching | Variable kinds: ANY (empty or more), NON_EMPTY (1+), SINGLE (exactly 1) |
| Multi-Antecedent Rules | Combine multiple words in a single production |
| Derivation Tracking | Full proof traces showing how each word was derived |
| Visualization Exports | DOT/GraphViz, LaTeX, Mermaid diagrams, ASCII trees |
| Interactive CLI | REPL interface via the pcs command |
| JSON Serialization | Save and load system definitions |
| SystemBuilder DSL | Ergonomic fluent API for system construction |
| Preset Systems | MU Puzzle, Binary Doubler, Palindrome Generator |
Installation
pip install post-canonical
Or with uv:
uv add post-canonical
Quick Start
from post_canonical.builder import SystemBuilder
# Build the MU puzzle from Godel, Escher, Bach
system = (SystemBuilder("MIU")
.var("x")
.var("y")
.axiom("MI")
.rule("$xI -> $xIU", name="add_U")
.rule("M$x -> M$x$x", name="double")
.rule("$x III $y -> $x U $y", name="III_to_U")
.rule("$x UU $y -> $x$y", name="delete_UU")
.build())
# Generate all derivable words up to 3 steps. The default `max_steps=10`
# prevents accidental runaway on systems whose word set explodes; raise
# it (or use `iterate()`) for deeper exploration. Results come back
# already ordered by length, then lexicographically.
words = system.generate_words(max_steps=3)
print(words)
# ('MI', 'MII', 'MIU', 'MUI', 'MIIU', 'MIIII', 'MIUIU', 'MIIIIU',
# 'MIIUIIU', 'MIIIIIIII', 'MIUIUIUIU')
# Check if a word is reachable
from post_canonical.query import ReachabilityQuery
query = ReachabilityQuery(system)
print(query.is_derivable("MU", max_words=10000))
# 'MU' NOT_FOUND after exploring 10000 words
Variables use $ prefix in patterns ($x, ${x}). Whitespace is ignored, so $x III $y reads cleanly. Three variable kinds are available:
builder = (SystemBuilder("abc")
.var("x") # ANY: matches "" or "a" or "abc"...
.var("y", kind="non_empty") # NON_EMPTY: matches "a" or "abc"...
.var("z", kind="single") # SINGLE: matches exactly one symbol
)
Pattern syntax
The pattern grammar is intentionally tiny:
pattern ::= element+
element ::= constant | variable
constant ::= [^$]+
variable ::= "${" name "}" (canonical)
| "$" name (shorthand)
name ::= [A-Za-z_][A-Za-z0-9_]*
Pattern.parse accepts only the canonical ${name} form to avoid ambiguity when a variable name could be confused with the trailing constant. The SystemBuilder DSL accepts both $x and ${x} and normalizes them.
CLI
The package includes an interactive REPL for exploring systems without writing code:
$ pcs
Post Canonical Systems REPL vX.Y.Z # the live REPL prints the current __version__
Type 'help' for commands.
pcs> alphabet MIU
Alphabet set: {I, M, U}
pcs> axiom MI
Axiom added: MI
pcs> var x
Variable added: x (ANY)
pcs> rule "$xI -> $xIU"
Rule added: $xI -> $xIU
pcs> rule "M$x -> M$x$x"
Rule added: M$x -> M$x$x
pcs> generate 3
Generated 8 words:
MI, MII, MIU, MIII, MIIU, MIIII, MIIIU, MIUIU
pcs> query "MU"
'MU' NOT_FOUND after exploring 10000 words
pcs> exit
Goodbye.
Visualization
Export derivations as DOT/GraphViz, LaTeX, Mermaid, or ASCII trees:
from post_canonical import create_mu_puzzle
from post_canonical.visualization import to_ascii_tree
system = create_mu_puzzle()
for dw in system.generate(max_steps=3):
if dw.word == "MIIII":
print(to_ascii_tree(dw.derivation))
break
MIIII
+-- MII (double)
+-- MI (axiom)
Preset Systems
| System | Import | Description |
|---|---|---|
| MU Puzzle | create_mu_puzzle() |
Hofstadter's famous puzzle from GEB |
| Binary Doubler | create_binary_doubler() |
Doubles binary strings: 1 -> 11 -> 1111 |
| Palindrome Generator | create_palindrome_generator() |
Generates all binary palindromes |
Requirements
- Python 3.12+
- No external dependencies
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file post_canonical-3.0.0.tar.gz.
File metadata
- Download URL: post_canonical-3.0.0.tar.gz
- Upload date:
- Size: 101.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
93ab7ec914684a0c9700b8e72c919c998e0d6514790ff23352a04e75135aaf17
|
|
| MD5 |
d6d877ea9f47f446536b66eca7070598
|
|
| BLAKE2b-256 |
f8b482278854e1f81baf36e950ee19cef3fb18fdc4a6f2d7d9e8ca06da871105
|
Provenance
The following attestation bundles were made for post_canonical-3.0.0.tar.gz:
Publisher:
publish.yml on niarenaw/post-canonical
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
post_canonical-3.0.0.tar.gz -
Subject digest:
93ab7ec914684a0c9700b8e72c919c998e0d6514790ff23352a04e75135aaf17 - Sigstore transparency entry: 1398116429
- Sigstore integration time:
-
Permalink:
niarenaw/post-canonical@5c0fa7a927b4da178a9491f83af48abbbfc06bef -
Branch / Tag:
refs/tags/v3.0.0 - Owner: https://github.com/niarenaw
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@5c0fa7a927b4da178a9491f83af48abbbfc06bef -
Trigger Event:
release
-
Statement type:
File details
Details for the file post_canonical-3.0.0-py3-none-any.whl.
File metadata
- Download URL: post_canonical-3.0.0-py3-none-any.whl
- Upload date:
- Size: 41.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a79db7030caa4bd3a101466cb9e1923662d26001cce204e1ca1df58e6a8b8ae9
|
|
| MD5 |
95f8304b2178b3d863367a020ef8adf9
|
|
| BLAKE2b-256 |
0555afe013e31795f0296c23ce7eb1bb887d4b1170830f7583d80ec41788b089
|
Provenance
The following attestation bundles were made for post_canonical-3.0.0-py3-none-any.whl:
Publisher:
publish.yml on niarenaw/post-canonical
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
post_canonical-3.0.0-py3-none-any.whl -
Subject digest:
a79db7030caa4bd3a101466cb9e1923662d26001cce204e1ca1df58e6a8b8ae9 - Sigstore transparency entry: 1398116453
- Sigstore integration time:
-
Permalink:
niarenaw/post-canonical@5c0fa7a927b4da178a9491f83af48abbbfc06bef -
Branch / Tag:
refs/tags/v3.0.0 - Owner: https://github.com/niarenaw
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@5c0fa7a927b4da178a9491f83af48abbbfc06bef -
Trigger Event:
release
-
Statement type: