Research source · python

verify_pumping_certificates.py

site/public/research-artifacts/scaffold-pumping-certificates/verify_pumping_certificates.py

170 lines. Source is displayed for inspection; it is not executed by this page.

File fingerprint

SHA-256: 20494a576a7cbec6fb1dadefa84b05978711b56343efe6a88bfc477760eac2dc

#!/usr/bin/env python3
"""Finite tests for the scoped pumping-certificate note, not a PEG pumping theorem."""

import itertools
import json
from pathlib import Path

# Each immutable node is (label, ordered ports). Node 0 is the native base.
# -1 denotes a missing endpoint; the last allocated node is the current root.
BASE = (("B", (-1,)),)
ROWS = tuple(itertools.product(("0", "1"), ("missing", "self", 0, 1, 2)))


def follow(graph, node, length):
    for _ in range(length):
        node = -1 if node == -1 else graph[node][1][0]
    return node


def append(graph, row):
    label, address = row
    if address == "missing":
        target = -1
    elif address == "self":
        target = len(graph)
    else:
        target = follow(graph, len(graph) - 1, address)
    return graph + ((label, (target,)),)


def bisimilar(left, right):
    pending = [(len(left) - 1, len(right) - 1)]
    seen = set()
    while pending:
        u, v = pending.pop()
        if (u, v) in seen:
            continue
        seen.add((u, v))
        if u == -1 or v == -1:
            if u != v:
                return False
            continue
        lu, pu = left[u]
        lv, pv = right[v]
        if lu != lv or len(pu) != len(pv):
            return False
        pending.extend(zip(pu, pv))
    return True


def trace(graph, node, depth):
    if node == -1:
        return ("missing",)
    if depth == 0:
        return (graph[node][0],)
    return (graph[node][0], trace(graph, graph[node][1][0], depth - 1))


def native_view(graph, node, depth):
    if depth == 0:
        return "bottom" if node == -1 or node == 0 else graph[node][0]
    if node == -1:
        return "bottom"
    return (graph[node][0], native_view(graph, graph[node][1][0], depth - 1))


def main():
    layer = {BASE}
    graphs = set(layer)
    for _ in range(3):
        layer = {append(graph, row) for graph in layer for row in ROWS}
        graphs.update(layer)
    graphs = sorted(graphs, key=repr)
    pairs = equal_pairs = extension_checks = view_checks = 0
    for left in graphs:
        for right in graphs:
            pairs += 1
            equal = bisimilar(left, right)
            # Independent finite unfolding oracle; this depth exceeds the
            # number of states in the disjoint union, including missing sinks.
            depth = len(left) + len(right) + 2
            assert equal == (trace(left, len(left) - 1, depth)
                             == trace(right, len(right) - 1, depth))
            if equal:
                equal_pairs += 1
                for radius in range(5):
                    assert native_view(left, len(left) - 1, radius) == native_view(
                        right, len(right) - 1, radius)
                    view_checks += 1
                for row in ROWS:
                    assert bisimilar(append(left, row), append(right, row))
                    extension_checks += 1

    # Accept-all archival machine: q is constant and each input stores one
    # new 0-node pointing to the previous root. Distinct cuts never bisimulate.
    archive = [BASE]
    for _ in range(32):
        archive.append(append(archive[-1], ("0", 0)))
    archive_pairs = 0
    for i, left in enumerate(archive):
        for right in archive[i + 1:]:
            assert not bisimilar(left, right)
            archive_pairs += 1

    # Arbitrarily deep equal views need not support the same next answer.
    local_counterexamples = 0
    for radius in range(17):
        left = append(BASE, ("0", "missing"))
        right = append(BASE, ("1", "missing"))
        for _ in range(radius + 1):
            left = append(left, ("0", 0))
            right = append(right, ("0", 0))
        assert native_view(left, len(left) - 1, radius) == native_view(
            right, len(right) - 1, radius)
        assert not bisimilar(left, right)
        # A radius-two cursor copies the label and port of the old successor.
        for _ in range(radius + 1):
            def pop(graph):
                child = graph[-1][1][0]
                return graph + ((graph[child][0], graph[child][1]),)
            left, right = pop(left), pop(right)
        assert left[-1][0] != right[-1][0]
        local_counterexamples += 1

    # A concrete radius-two push/pop controller, initialized by '#': E is
    # represented by an E-labelled self-loop. Balanced 'ab' restores its
    # unfolding although allocation history grows. All states are identical;
    # accepting all words suffices to test all-future graph preservation.
    initial = BASE + (("E", (1,)),)
    def step(graph, letter):
        if letter == "a":
            return graph + (("X", (len(graph) - 1,)),)
        child = graph[-1][1][0]
        return graph + ((graph[child][0], graph[child][1]),)
    def run(graph, word):
        for letter in word:
            graph = step(graph, letter)
        return graph
    pumped = 0
    for repeats in range(9):
        repeated = run(initial, "ab" * repeats)
        assert bisimilar(initial, repeated)
        for length in range(7):
            for word in itertools.product("ab", repeat=length):
                assert bisimilar(run(initial, word), run(repeated, word))
                pumped += 1

    result = {
        "status": "pass",
        "scope": "degree-one legal histories through three appends, two labels, path length at most two",
        "graphs": len(graphs), "ordered_pairs": pairs,
        "bisimilar_pairs": equal_pairs, "common_row_extensions": extension_checks,
        "native_view_comparisons": view_checks,
        "archive_distinct_cut_pairs": archive_pairs,
        "local_view_counterexamples": local_counterexamples,
        "pumped_suffix_comparisons": pumped,
        "does_not_verify": [
            "universal existence of pumping certificates",
            "a pumping lemma for all PEGs",
            "a machine-checked unbounded theorem",
            "finite controller state equality is a separate certificate requirement"
        ]
    }
    output = json.dumps(result, indent=2) + "\n"
    Path(__file__).with_name("checker-output.json").write_text(output)
    print(output, end="")


if __name__ == "__main__":
    main()