Research source · python

verify_horn_join.py

site/public/research-artifacts/foundations/verify_horn_join.py

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

File fingerprint

SHA-256: d69823320efffbc61b3f925ecaf48006fa4cf1f08f5783910d49b0205385d964

#!/usr/bin/env python3
"""Semantic reference for retained Join/Pin; not a pointer-cost implementation."""
from dataclasses import dataclass
from pathlib import Path
import hashlib
import json
import random
from verify_persistent_horn import Engine, State

B = frozenset((0,1))

@dataclass(frozen=True)
class Solver:
    z: frozenset
    failed: bool = False
    engine: object = None
    local: object = None
    left: object = None
    right: object = None

@dataclass(frozen=True)
class Term:
    kind: str
    data: object
    left: object = None
    right: object = None

@dataclass
class Events:
    z: int = 0
    k: int = 0
    D: int = 0
    A: int = 0
    E: int = 0
    calls: int = 0


def leaf(rules):
    e = Engine(3,rules,B)
    state,_ = e.extend(State(),(),True)
    z = frozenset() if state.conflict else e.model(state)&B
    return Term('leaf',tuple(rules)), Solver(z,state.conflict,e,state)


def extend(s, incoming, ev, initial=False):
    ev.calls += 1
    if s.failed:
        return s, ()
    if s.engine is not None:
        before = (s.engine.cost.activated,s.engine.cost.occurrences)
        local,delta = s.engine.extend(s.local,incoming)
        ev.A += s.engine.cost.activated-before[0]
        ev.E += s.engine.cost.occurrences-before[1]
        z = s.z | frozenset(delta)
        return Solver(z,local.conflict,s.engine,local),delta
    z = set() if initial else set(s.z)
    pending = []
    delta = []
    def seed(v):
        if v not in z:
            z.add(v);pending.append(v);delta.append(v)
    for v in incoming:
        seed(v)
    left,right = s.left,s.right
    while pending:
        v = pending.pop();ev.D += 1
        left,dl = extend(left,(v,),ev)
        if left.failed:
            return Solver(frozenset(z),True,left=left,right=right),tuple(delta)
        for w in dl:
            seed(w)
        right,dr = extend(right,(v,),ev)
        if right.failed:
            return Solver(frozenset(z),True,left=left,right=right),tuple(delta)
        for w in dr:
            seed(w)
    assert left.z == right.z == frozenset(z)
    return Solver(frozenset(z),left=left,right=right),tuple(delta)


def join(a,b):
    ta,sa=a;tb,sb=b
    term=Term('join',None,ta,tb)
    ev=Events()
    if sa.failed or sb.failed:
        return (term,Solver(frozenset(),True)),ev
    names=tuple(sa.z)+tuple(sb.z);ev.z=len(names)
    s,_=extend(Solver(frozenset(),left=sa,right=sb),names,ev,True)
    return (term,s),ev


def pin(h,names):
    t,s=h
    ev=Events(k=len(names))
    new,_=extend(s,names,ev)
    return (Term('pin',tuple(names),t),new),ev


def flatten(t,path=()):
    if t.kind=='leaf':
        def rename(v):
            return None if v is None else ('B',v) if v in B else ('P',path,v)
        return [(tuple(rename(v) for v in body),rename(head)) for body,head in t.data]
    if t.kind=='pin':
        return flatten(t.left,path)+[((),('B',v)) for v in t.data]
    return flatten(t.left,path+(0,))+flatten(t.right,path+(1,))


def reference(t):
    rules=flatten(t)
    variables=list({v for body,head in rules for v in (*body,head) if v is not None}|{('B',v) for v in B})
    models=[]
    for mask in range(1<<len(variables)):
        truth={v for j,v in enumerate(variables) if mask&(1<<j)}
        if all(not set(body)<=truth or head in truth for body,head in rules):
            models.append(truth)
    if not models:
        return None
    return frozenset(v for v in B if all(('B',v) in m for m in models))


def check(h):
    term,s=h
    expected=reference(term)
    assert s.failed == (expected is None)
    if expected is not None:
        assert s.z==expected
    return expected


def leaves(t):
    return 1 if t.kind=='leaf' else leaves(t.left) if t.kind=='pin' else leaves(t.left)+leaves(t.right)


def snapshot(s):
    if s.engine is not None:
        return (s.z,s.failed,repr(s.local))
    return (s.z,s.failed,snapshot(s.left) if s.left else None,snapshot(s.right) if s.right else None)


def main():
    rng=random.Random(4001)
    pool=[]
    for _ in range(30):
        rules=[]
        for j in range(rng.randrange(6)):
            h=rng.choice([None,0,1,2])
            choices=[v for v in range(3) if v!=h]
            body=tuple(rng.sample(choices,rng.randrange(min(len(choices),3 if h is None else 2)+1)))
            rules.append((body,h))
        pool.append(leaf(rules));check(pool[-1])
    joins=pins=0
    for _ in range(500):
        a=rng.choice(pool);saved=snapshot(a[1])
        if rng.randrange(2):
            b=rng.choice(pool)
            if leaves(a[0])+leaves(b[0])>6:
                continue
            saved_b=snapshot(b[1])
            result,ev=join(a,b);joins+=1
            assert saved_b==snapshot(b[1])
        else:
            result,ev=pin(a,[rng.randrange(2) for _ in range(rng.randrange(3))]);pins+=1
        check(result)
        assert snapshot(a[1])==saved
        pool.append(result)
    fact=leaf([((),0)])
    implication=leaf([((0,),1)])
    crossed,ev=join(fact,implication)
    assert check(crossed)==B and ev.A>=1
    # An empty boundary cache prevents descent, even into a deep composite.
    h=leaf([((0,),1)])
    for _ in range(20):
        h,ev=join(h,leaf([]))
        assert ev.z==ev.D==ev.A==ev.E==0
    seeded,ev=pin(h,[0])
    assert check(seeded)==B and ev.D>=20
    deep_events=vars(ev).copy()
    # Reusing a handle creates separate semantic private occurrences.
    private=leaf([((0,),2),((2,),1)])
    double,_=join(private,private)
    assert len({v for body,head in flatten(double[0]) for v in (*body,head) if v and v[0]=='P'})==2
    old=snapshot(private[1])
    double,_=pin(double,[0]);assert check(double)==B
    assert snapshot(private[1])==old
    # Sum counters falsely fires; maximum counters fails to fire.
    one_x={0};one_z={1};body={0,1}
    assert len(one_x&body)+len(one_x&body)==2 and not body<=one_x
    assert max(len(one_x&body),len(one_z&body))==1 and body<=one_x|one_z
    # Conditional state must not masquerade as the unconditioned formula.
    base=leaf([((0,),1)]);negative=leaf([((1,),None)])
    assert check(join(base,negative)[0])==frozenset()
    conditioned,_=pin(base,[0]);assert check(join(conditioned,negative)[0]) is None
    here=Path(__file__).resolve().parent
    names=['compositional-horn-join.md','verify_horn_join.py','verify_persistent_horn.py']
    out={'status':'passed','random_join_operations':joins,'random_pin_operations':pins,
         'retained_snapshots':'unchanged','cross_operand_old_fact':'passed',
         'private_occurrence_separation':'passed','empty_cache_no_descent':'passed',
         'deep_composite_pin_events':deep_events,'counter_sum_and_max_negative_controls':'passed',
         'conditional_assumption_scope':'passed',
         'scope':'Finite semantic Join/Pin histories against truth assignments; recursive sets and existing leaf checker, not pointer-cost or Lean verification.',
         'sha256':{n:hashlib.sha256((here/n).read_bytes()).hexdigest() for n in names}}
    (here/'horn-join-output.json').write_text(json.dumps(out,indent=2)+'\n');print(json.dumps(out,indent=2))

if __name__=='__main__':
    main()