Research source · python
verify_persistent_horn.py
site/public/research-artifacts/foundations/verify_persistent_horn.py
271 lines. Source is displayed for inspection; it is not executed by this page.
File fingerprint
SHA-256: 24e6ebb45bcc3fec4231c11966b900bc641a7c5409573e37507a0ffa6d118611
#!/usr/bin/env python3
"""Finite model checks, not a microinstruction or asymptotic proof certificate."""
from dataclasses import dataclass
from itertools import combinations
from pathlib import Path
import hashlib
import json
import random
@dataclass(frozen=True)
class Node:
left: object = None
right: object = None
value: object = None
@dataclass
class Cost:
reads: int = 0
alloc: int = 0
gets: int = 0
puts: int = 0
activated: int = 0
occurrences: int = 0
def key(i, width):
return tuple((i >> j) & 1 for j in reversed(range(width)))
def get(root, bits, cost, default=None):
cost.gets += 1
for bit in bits:
cost.reads += 1
if root is None:
return default
root = root.right if bit else root.left
cost.reads += 1
return default if root is None else root.value
def put(root, bits, value, cost):
cost.puts += 1
def rec(node, depth):
cost.reads += 1
cost.alloc += 1
if depth == len(bits):
return Node(value=value)
left, right = (None, None) if node is None else (node.left, node.right)
if bits[depth]:
return Node(left, rec(right, depth+1))
return Node(rec(left, depth+1), right)
return rec(root, 0)
@dataclass(frozen=True)
class State:
truth: object = None
counters: object = None
conflict: bool = False
class Engine:
def __init__(self, n, rules, boundary=(), width=None):
self.n = n
self.rules = tuple((tuple(sorted(set(a))), h) for a, h in rules if h not in a)
self.width = width or max(1, max(n, len(self.rules), 1).bit_length())
self.cost = Cost()
self.incidence = None
self.clauses = None
boundary = set(boundary)
for v in range(n):
self.incidence = put(self.incidence, key(v, self.width), (v in boundary, ()), self.cost)
self.facts = []
for j, (body, head) in enumerate(self.rules):
assert len(body) <= (3 if head is None else 2)
assert all(0 <= v < n for v in body)
assert head is None or 0 <= head < n
self.clauses = put(self.clauses, key(j, self.width), (len(body), head), self.cost)
if not body:
self.facts.append(head)
for v in body:
flag, inc = get(self.incidence, key(v, self.width), self.cost)
self.incidence = put(self.incidence, key(v, self.width), (flag, (j, inc)), self.cost)
self.preparation = vars(self.cost).copy()
def extend(self, old, seeds, initialize=False):
if old.conflict:
return old, ()
truth, counters = old.truth, old.counters
work, delta = (), []
conflict = False
def mark(v):
nonlocal truth, work
flag, _ = get(self.incidence, key(v, self.width), self.cost)
if not get(truth, key(v, self.width), self.cost, False):
truth = put(truth, key(v, self.width), True, self.cost)
work = (v, work)
self.cost.activated += 1
if flag:
delta.append(v)
if initialize:
for head in self.facts:
if head is None:
conflict = True
break
mark(head)
if not conflict:
for v in seeds:
mark(v)
while work and not conflict:
v, work = work
_, inc = get(self.incidence, key(v, self.width), self.cost)
while inc and not conflict:
j, inc = inc
self.cost.occurrences += 1
count = get(counters, key(j, self.width), self.cost, 0) + 1
counters = put(counters, key(j, self.width), count, self.cost)
arity, head = get(self.clauses, key(j, self.width), self.cost)
assert count <= arity
if count == arity:
if head is None:
conflict = True
else:
mark(head)
return State(truth, counters, conflict), tuple(delta)
def model(self, state):
if state.conflict:
return None
return frozenset(v for v in range(self.n)
if get(state.truth, key(v, self.width), Cost(), False))
def check_accounting(self):
c = self.cost
assert c.alloc == c.puts*(self.width+1)
assert c.reads <= (c.gets+c.puts)*(self.width+1)
def oracle(n, rules, seeds):
models = []
for mask in range(1 << n):
model = frozenset(v for v in range(n) if mask & (1 << v))
if set(seeds) <= model and all(not set(body) <= model or head in model for body, head in rules):
models.append(model)
return frozenset.intersection(*models) if models else None
def compose(engines):
states, pending = [], []
for i, engine in enumerate(engines):
state, delta = engine.extend(State(), (), True)
states.append(state)
pending.extend((1-i, v) for v in delta)
notifications = len(pending)
while pending and not any(s.conflict for s in states):
target, v = pending.pop()
states[target], delta = engines[target].extend(states[target], (v,))
notifications += len(delta)
pending.extend((1-target, w) for w in delta)
return states, notifications
def main():
checks = 0
universe = []
for size in range(3):
for body in combinations(range(2), size):
universe.append((body, None))
for head in range(2):
if head not in body:
universe.append((body, head))
# All 256 normalized programs on two variables, every seed set, then every singleton extension.
assert len(universe) == 8
for mask in range(1 << len(universe)):
rules = [r for j, r in enumerate(universe) if mask & (1 << j)]
e = Engine(2, rules)
initial, _ = e.extend(State(), (), True)
for seedmask in range(4):
seeds = {v for v in range(2) if seedmask & (1 << v)}
state, _ = e.extend(initial, seeds)
assert e.model(state) == oracle(2, rules, seeds)
checks += 1
for v in range(2):
saved = repr(state)
child, _ = e.extend(state, (v, v))
assert e.model(child) == oracle(2, rules, seeds | {v})
assert repr(state) == saved
checks += 1
e.check_accounting()
rng = random.Random(3801)
def random_rules(n):
out = []
for _ in range(rng.randrange(11)):
h = rng.choice([None] + list(range(n)))
body = tuple(rng.sample(range(n), rng.randrange(min(n, 3 if h is None else 2)+1)))
out.append((body, h))
return out
forks = 0
for _ in range(200):
rules = random_rules(4)
e = Engine(4, rules, range(2))
initial, _ = e.extend(State(), (), True)
versions = [(initial, frozenset())]
for _ in range(20):
parent, seeds = rng.choice(versions)
v = rng.randrange(4)
snapshot = repr(parent)
child, _ = e.extend(parent, (v,))
seeds = seeds | {v}
assert e.model(child) == oracle(4, rules, seeds)
assert repr(parent) == snapshot
versions.append((child, seeds))
forks += 1
e.check_accounting()
pairs = 0
max_notifications = 0
for _ in range(1000):
rules = [random_rules(4), random_rules(4)]
es = [Engine(4, r, (0, 1), width=4) for r in rules]
states, sent = compose(es)
assert sent <= 4
combined = list(rules[0])
def ren(v):
return v if v is None or v < 2 else v+2
combined += [(tuple(ren(v) for v in body), ren(h)) for body, h in rules[1]]
expected = oracle(6, combined, ())
if any(s.conflict for s in states):
assert expected is None
else:
actual = es[0].model(states[0]) | frozenset(ren(v) for v in es[1].model(states[1]))
assert actual == expected
for e in es:
assert e.cost.activated <= e.n
assert e.cost.occurrences <= sum(len(a) for a, _ in e.rules)
e.check_accounting()
max_notifications = max(max_notifications, sent)
pairs += 1
# Negative three-body rule, duplicates, tautology, and explicit delayed inter-fragment conflict.
r = [((0, 0), 1), ((1,), 1), ((0, 1, 2), None)]
e = Engine(3, r)
s, _ = e.extend(State(), (0, 2), True)
assert e.model(s) == oracle(3, r, (0, 2)) is None
es = [Engine(3, [((0,), 2), ((2,), 1)], (0, 1)),
Engine(2, [((), 0), ((1,), None)], (0, 1))]
ss, sent = compose(es)
assert any(s.conflict for s in ss)
chains = []
for n in (16, 64, 256):
e = Engine(n, [((v,), v+1) for v in range(n-1)])
base, _ = e.extend(State(), (), True)
start = e.cost.activated
for _ in range(3):
child, _ = e.extend(base, (0,))
assert len(e.model(child)) == n
assert e.model(base) == frozenset()
assert e.cost.activated-start == 3*n
e.check_accounting()
chains.append({'variables': n, 'branches': 3, 'activation_events': 3*n})
here = Path(__file__).resolve().parent
result = {'status': 'passed', 'exhaustive_two_variable_programs': 256,
'closure_and_extension_comparisons': checks, 'random_retained_version_extensions': forks,
'random_independent_fragment_pairs': pairs, 'maximum_notifications_b2': max_notifications,
'branch_repeated_work': chains,
'scope': 'Finite high-level immutable-trie/event checks against assignment enumeration; no microinstruction, loader, asymptotic or Lean proof.',
'sha256': {name: hashlib.sha256((here/name).read_bytes()).hexdigest() for name in
('persistent-horn-closure.md', 'verify_persistent_horn.py', 'sat-boundary-dynamics.md')}}
(here/'persistent-horn-output.json').write_text(json.dumps(result, indent=2)+'\n')
print(json.dumps(result, indent=2))
if __name__ == '__main__':
main()