Research source · python
verify_clocked_horn.py
site/public/research-artifacts/foundations/verify_clocked_horn.py
240 lines. Source is displayed for inspection; it is not executed by this page.
File fingerprint
SHA-256: b45c4f8ae0ef8bd79e176ffe6037eea965a7fd27cdc80f7a1d80ba07fac41506
#!/usr/bin/env python3
"""Specification checks. Does not extract the full Horn primitive program."""
from pathlib import Path
import hashlib
import itertools
import json
import random
DATA = set('u:BFGDRE;01abcd+-')
class Invalid(Exception):
pass
def parse(text):
i = 0
def take(want=None):
nonlocal i
if i == len(text):
raise Invalid('truncated')
c = text[i]
i += 1
if want is not None and c != want:
raise Invalid('symbol')
return c
width = 0
while i < len(text) and text[i] == 'u':
take('u'); width += 1
if not width:
raise Invalid('empty header')
take(':'); take('B')
def key():
out = ''
for _ in range(width):
c = take()
if c not in '01':
raise Invalid('key')
out += c
return out
def declarations(stop):
out = set()
while i < len(text) and text[i] != stop:
name = key(); take(';')
if name in out:
raise Invalid('duplicate declaration')
out.add(name)
return out
boundary = declarations('F')
programs = []
for side, stop in (('F', 'G'), ('G', 'E')):
take(side); take('D')
private = declarations('R')
if boundary & private:
raise Invalid('boundary collision')
take('R')
identifiers = set()
rules = []
def semantic(name):
if name not in boundary | private:
raise Invalid('undeclared')
return ('B' if name in boundary else side, name)
while i < len(text) and text[i] != stop:
ident = key()
if ident in identifiers:
raise Invalid('duplicate rule')
identifiers.add(ident)
arity = take()
if arity not in 'abcd':
raise Invalid('arity')
count = 'abcd'.index(arity)
sign = take()
if sign == '+':
if count > 2:
raise Invalid('positive arity')
head = semantic(key())
elif sign == '-':
head = None
else:
raise Invalid('head')
body = tuple(semantic(key()) for _ in range(count))
take(';')
if len(set(body)) != len(body) or head in body:
raise Invalid('not normalized')
rules.append((body, head))
programs += rules
take('E')
if i != len(text):
raise Invalid('trailing')
return programs
def closure(rules):
truth = set()
while True:
old = set(truth)
for body, head in rules:
if set(body) <= truth:
if head is None:
return False
truth.add(head)
if truth == old:
return True
def brute(rules):
variables = sorted({v for body, h in rules for v in (*body, h) if v is not None})
for mask in range(1 << len(variables)):
true = {v for j, v in enumerate(variables) if mask & (1 << j)}
if all(not set(body) <= true or head in true for body, head in rules):
return True
return False
def encode(width, boundary, left, right):
def k(v):
return format(v, f'0{width}b')
def side(label, item):
private, rules = item
s = label + 'D' + ''.join(k(v)+';' for v in private) + 'R'
for j, (body, head) in enumerate(rules):
s += k(j) + 'abcd'[len(body)] + ('-' if head is None else '+'+k(head))
s += ''.join(k(v) for v in body) + ';'
return s
return 'u'*width + ':B' + ''.join(k(v)+';' for v in boundary) + side('F',left) + side('G',right) + 'E'
def wrapper(word, C, steps, answer):
# Frozen (label,next) cells; fuel shares the original reverse input.
raw = None
phase = 'load'
fuel = None
residue = C
done = 0
for ch in word:
if phase == 'load':
if ch in DATA:
raw = (ch, raw)
elif ch == '#':
fuel = ('sentinel', raw)
phase = 'run'
else:
return False
elif phase == 'run':
if ch == 't':
if fuel is None:
return False
done += done < steps
residue -= 1
if residue == 0:
fuel = fuel[1]
residue = C
elif ch == '!':
if fuel is not None or done < steps or not answer:
return False
phase = 'accept'
else:
return False
else:
return False
return phase == 'accept'
def main():
rng = random.Random(3901)
examples = []
for _ in range(500):
boundary = [0, 1]
sides = []
for _ in range(2):
rules = []
for j in range(rng.randrange(7)):
h = rng.choice([None, 0, 1, 2])
candidates = [v for v in range(3) if v != h]
body = tuple(rng.sample(candidates, rng.randrange(min(len(candidates),3 if h is None else 2)+1)))
rules.append((body,h))
sides.append(([2],rules))
x = encode(3,boundary,*sides)
rules = parse(x)
assert closure(rules) == brute(rules)
examples.append(x)
# Same private spelling has independent meaning on the two sides.
separated = encode(1,[],([0],[((),0)]),([0],[((0,),None)]))
assert closure(parse(separated))
shared = encode(1,[0],([] ,[((),0)]),([],[((0,),None)]))
assert not closure(parse(shared))
empty = encode(1,[],([],[]),([],[]))
assert closure(parse(empty))
malformed = ['', 'u:BFD RGDRE', empty+'0', empty[1:],
encode(1,[0,0],([],[]),([],[])),
encode(1,[0],([0],[]),([],[])),
encode(1,[],([],[((),0)]),([],[])),
encode(2,[0,1],([], [((0,0),1)]),([],[])),
encode(2,[0],([], [((0,),0)]),([],[])),
encode(2,[0,1,2],([], [((0,1,2),0)]),([],[]))]
for x in malformed:
try:
parse(x)
except Invalid:
pass
else:
raise AssertionError(('accepted malformed',x))
prefix_rejections = 0
for x in examples[:20]:
for j in range(len(x)):
try:
parse(x[:j])
except Invalid:
prefix_rejections += 1
else:
raise AssertionError('accepted truncation')
clocks = 0
for M in range(9):
x = 'u'*M
for C in range(1,5):
budget = C*(M+1)
for runtime in range(budget+3):
for count in range(budget+3):
for ans in (False,True):
word = x+'#'+'t'*count+'!'
expected = ans and runtime <= budget and count == budget
assert wrapper(word,C,runtime,ans) == expected
clocks += 1
bad_shapes = ['t#t!', '#!t', 'u##tt!', 'u#utt!', 'u#tt!!', '!']
for w in bad_shapes:
assert not wrapper(w,1,0,True)
# Quadratic runner defeats fixed linear fuel; not a language lower bound.
assert not wrapper('u'*20+'#'+'t'*(3*21)+'!',3,400,True)
here = Path(__file__).resolve().parent
names = ['clocked-horn-transfer.md','verify_clocked_horn.py','persistent-horn-closure.md']
result = {'status':'passed','random_valid_horn_pair_oracle_checks':len(examples),
'malformed_cases':len(malformed), 'rejected_truncated_prefixes':prefix_rejections,
'exact_fuel_wrapper_cases':clocks, 'private_name_separation':'passed',
'quadratic_runtime_negative_control':'passed',
'scope':'Grammar/semantic specification and fuel-wrapper finite checks. No extracted Horn primitive program, decimal clock constant, compiled scaffold, or Lean proof.',
'sha256':{name:hashlib.sha256((here/name).read_bytes()).hexdigest() for name in names}}
(here/'clocked-horn-output.json').write_text(json.dumps(result,indent=2)+'\n')
print(json.dumps(result,indent=2))
if __name__ == '__main__':
main()