Research source · python

check_tree_witness.py

site/public/papers/residual-counts-and-access/check_tree_witness.py

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

File fingerprint

SHA-256: e8529a9ea77013929c12cfc36c59cba9ebd2769a75814a4f044dee569fb94a93

#!/usr/bin/env python3
"""Independent CFG-derivation counting, tree semantics, and reduction checks.

Finite corroboration only. The universal results require the proofs in the packet.
No third-party dependencies.
"""
from functools import lru_cache
from itertools import product
import json
import random

DATA = 't[]01'
GRAMMAR = {
    'A': [('t', 'A', '0'), ('t', 'T', 'A', '1'), ('[', 'P1', '#')],
    'T': [('t', 'T', 'T'), ('[', 'B', ']')],
    'B': [('0', 'B'), ('1', 'B'), ('0',), ('1',)],
    'R': [(a, 'R') for a in DATA] + [()],
    'P0': [(']', 'R', 'FPRD_READING_BODY#39;), ('0', 'P0', '0'), ('0', 'P0', '1'),
           ('1', 'P0', '0'), ('1', 'P1', '1')],
    'P1': [('0', 'P1', '0'), ('0', 'P1', '1'), ('1', 'P1', '0'),
           ('1', 'P0', '1')],
}


def derivations(word, start='A'):
    """Count parse trees for the literal CFG, capped at 2, for a whole word."""
    @lru_cache(None)
    def nt(name, pos):
        result = {}
        for rhs in GRAMMAR[name]:
            frontier = {pos: 1}
            for symbol in rhs:
                next_frontier = {}
                for at, ways in frontier.items():
                    if symbol in GRAMMAR:
                        endings = nt(symbol, at)
                    elif at < len(word) and word[at] == symbol:
                        endings = {at + 1: 1}
                    else:
                        endings = {}
                    for end, subways in endings.items():
                        next_frontier[end] = min(2, next_frontier.get(end, 0) + ways * subways)
                frontier = next_frontier
                if not frontier:
                    break
            for end, ways in frontier.items():
                result[end] = min(2, result.get(end, 0) + ways)
        return result
    return nt(start, 0).get(len(word), 0)


def read_leaf(data, pos):
    if pos >= len(data) or data[pos] != '[':
        return None
    end = pos + 1
    while end < len(data) and data[end] in '01':
        end += 1
    if end == pos + 1 or end >= len(data) or data[end] != ']':
        return None
    return data[pos + 1:end], end + 1


def skip_tree(data, pos):
    """Iterative prefix-code parser; unrelated to the CFG count routine."""
    pending = 1
    while pending:
        if pos >= len(data):
            return None
        if data[pos] == 't':
            pending += 1
            pos += 1
        else:
            leaf = read_leaf(data, pos)
            if leaf is None:
                return None
            _, pos = leaf
            pending -= 1
    return pos


def selected_leaf(data, path):
    """Follow the externally supplied root-to-leaf path in a tree prefix."""
    pos = 0
    for bit in path:
        if pos >= len(data) or data[pos] != 't':
            return None
        pos += 1
        if bit == '1':
            pos = skip_tree(data, pos)
            if pos is None:
                return None
    leaf = read_leaf(data, pos)
    return None if leaf is None else leaf[0]


def semantic_accept(word):
    if word.count('FPRD_READING_BODY#39;) != 1 or word.count('#') != 1:
        return False
    data, rest = word.split('FPRD_READING_BODY#39;)
    if '#' not in rest or any(a not in DATA for a in data):
        return False
    x, q = rest.split('#')
    if not x or any(a not in '01' for a in x + q):
        return False
    s = selected_leaf(data, q[::-1])
    return s is not None and len(s) == len(x) and sum(int(a) * int(b) for a, b in zip(s[::-1], x)) % 2 == 1


def balanced_tree(rows):
    if len(rows) == 1:
        return '[' + rows[0][::-1] + ']'
    assert len(rows) % 2 == 0
    h = len(rows) // 2
    return 't' + balanced_tree(rows[:h]) + balanced_tree(rows[h:])


def encoded_word(rows, x, index):
    n = len(x)
    assert all(len(row) == n for row in rows)
    m = 1 << (len(rows) - 1).bit_length()
    padded = rows + ['0' * n] * (m - len(rows))
    h = m.bit_length() - 1
    path = '' if h == 0 else format(index, '0%db' % h)
    return balanced_tree(padded) + 'FPRD_READING_BODY#39; + x + '#' + path[::-1]


def main():
    stats = {'lexical_words': 0, 'structured_words': 0, 'hard_encodings': 0,
             'random_mutations': 0, 'accepted': 0, 'max_derivations': 0}
    samples = []

    def check(word, group, expected=None):
        semantic = semantic_accept(word)
        count = derivations(word)
        assert count == int(semantic), (word, count, semantic)
        if expected is not None:
            assert semantic == expected, (word, semantic, expected)
        stats[group] += 1
        stats['accepted'] += semantic
        stats['max_derivations'] = max(stats['max_derivations'], count)

    # Exhaust malformed and partial data prefixes; q may be empty or invalid-depth.
    for size in range(5):
        for letters in product(DATA, repeat=size):
            data = ''.join(letters)
            for x in ('0', '1'):
                for qsize in range(3):
                    for q in product('01', repeat=qsize):
                        check(data + 'FPRD_READING_BODY#39; + x + '#' + ''.join(q), 'lexical_words')

    # Longer trees, arbitrary ignored tails, and different-depth query paths.
    data_cases = ['[1]', 't[1][0]', 't[0][1]', 'tt[1][0][1]',
                  't[1]t[0][1]', 'tt[10][01]t[11][00]',
                  't[1]', 't[0][1][1]', 't[]t[1][1]', 'tt[1][0]']
    for data in data_cases:
        for tail in ('', 't', '][', '[101]', 'tt[]'):
            for x in ('0', '1', '00', '01', '10', '11', '101'):
                for qsize in range(4):
                    for q in product('01', repeat=qsize):
                        check(data + tail + 'FPRD_READING_BODY#39; + x + '#' + ''.join(q), 'structured_words')

    # Exact finite reductions, including non-power-of-two padding.
    for n, row_count in ((1, 1), (1, 2), (1, 4), (2, 3), (2, 4), (3, 2)):
        vectors = [''.join(bits) for bits in product('01', repeat=n)]
        for rows_tuple in product(vectors, repeat=row_count):
            rows = list(rows_tuple)
            for x in vectors:
                for i in range(row_count):
                    expected = bool(sum(int(a) * int(b) for a, b in zip(rows[i], x)) % 2)
                    word = encoded_word(rows, x, i)
                    check(word, 'hard_encodings', expected)
                    if len(samples) < 20 and expected:
                        samples.append(word)

    rng = random.Random(20260904)
    seeds = [encoded_word(['01', '11', '10', '00'], x, i)
             for x in ('00', '01', '10', '11') for i in range(4)]
    alphabet = DATA + '$#z'
    for _ in range(1500):
        word = rng.choice(seeds)
        at = rng.randrange(len(word) + 1)
        kind = rng.randrange(3)
        if kind == 0:
            word = word[:at] + rng.choice(alphabet) + word[at:]
        elif kind == 1 and at < len(word):
            word = word[:at] + word[at + 1:]
        elif at < len(word):
            word = word[:at] + rng.choice(alphabet) + word[at + 1:]
        check(word, 'random_mutations')

    print(json.dumps({'status': 'passed', 'counts': stats, 'accepted_examples': samples,
                      'scope': 'Finite corroboration of grammar unambiguity, semantic equivalence, and hard-word encoding; not a proof of non-PEG.'}, indent=2))


if __name__ == '__main__':
    main()