Research source · python

check_anchor_refinement.py

tools/research/check_anchor_refinement.py

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

File fingerprint

SHA-256: ea9a194fa051f775c860370f0af3cd7e4673dd4571320c19532b220e6b39d198

"""Semantic model for paid anchor refinement and deferred left dispatch.
Python directory maps are bookkeeping, not a native timing implementation.
"""
from dataclasses import dataclass,replace
from itertools import product,permutations
import random,json
from check_frozen_row_composition import B,lookup,insert,view,close
from check_row_typed_composition import flag_change
from check_rectangle_deficit import Token,Fibre,extend,debit,count,frame_count,key
import check_pending_substitution as n

@dataclass(frozen=True)
class Row:
    seen:object=B
    entries:tuple=() # spelling, class offset, current anchor profile
@dataclass(frozen=True)
class Meta:
    anchors:int
    frozen:bool
    hist:tuple
    cells:tuple
    population:tuple
@dataclass(frozen=True)
class State:
    rows:object=B
    targets:object=B # ('anchor', index) or ('mask', mask)
    pairs:object=B
    keys:object=B
    unfinished:object=None
    excessive:object=None

def units(k):
    p=None
    for _ in range(k):p=Token(p)
    return p

def anchor_mask(meta,j):
    return sum(1<<(m-1) for m in range(1,len(meta.hist)) if meta.hist[m] is not None and m&(1<<j))

def freeze(meta,k):
    r=(1<<k)-1
    cells=tuple(Fibre(meta.hist[m],units(m.bit_count() if meta.hist[m] is not None else 0),None) for m in range(1,r+1))
    hist=[None]*(1<<r)
    for j in range(meta.anchors):
        q=anchor_mask(meta,j);hist[q]=Token(hist[q])
    return replace(meta,frozen=True,cells=cells,population=tuple(hist))

def bad(meta,K):
    if meta is None:return False,False
    u=any(f.deficit is not None for f in meta.cells) if meta.frozen else False
    q=sum(x is not None for x in meta.population) if meta.frozen else len({anchor_mask(meta,j) for j in range(meta.anchors)})
    return u,q>K

def step(st,v,w,k=2,d=2,s=1,K=2):
    z=key(w)
    if z is None or abs(n.cls(v)-n.cls(z))>s:return None
    row=lookup(st.rows,v) or Row();slot=lookup(row.seen,z)
    if slot is None:
        if len(row.entries)==d:return None
        idx=len(row.entries);entries=list(row.entries)+[(tuple(z),n.cls(z)-n.cls(v),0)]
        seen=insert(row.seen,z,idx+1)
    else:idx=slot-1;entries=list(row.entries);seen=row.seen
    profile=entries[idx][2]
    r=(1<<k)-1
    meta=lookup(st.keys,z) or Meta(0,False,(None,)*(r+1),(),())
    before=lookup(st.keys,z)
    pairroot=lookup(st.pairs,v) or B;dup=bool(lookup(pairroot,w))
    desc=lookup(st.targets,w)
    if desc is None:
        if meta.anchors<k:
            desc=('anchor',meta.anchors);meta=replace(meta,anchors=meta.anchors+1)
        else:
            if not meta.frozen:meta=freeze(meta,k)
            desc=('mask',0)
    if desc[0]=='anchor':
        if meta.frozen:
            if not dup:return None
        else:
            new=profile|(1<<desc[1])
            if new!=profile:
                hist=list(meta.hist)
                if profile:assert hist[profile] is not None;hist[profile]=hist[profile].tail
                hist[new]=Token(hist[new]);meta=replace(meta,hist=tuple(hist))
                entries[idx]=(entries[idx][0],entries[idx][1],new)
    else:
        if not profile:return None
        c=profile-1;bit=1<<c;mask=desc[1];new=mask|bit
        f=meta.cells[c];debt=f.deficit;tc=f.targets
        if not mask&bit:debt=extend(debt,f.predecessors);tc=Token(tc)
        if not dup:debt=debit(debt)
        cells=list(meta.cells);cells[c]=Fibre(f.predecessors,tc,debt)
        hist=list(meta.population)
        if mask!=new:
            if mask:assert hist[mask] is not None;hist[mask]=hist[mask].tail
            hist[new]=Token(hist[new])
        meta=replace(meta,cells=tuple(cells),population=tuple(hist));desc=('mask',new)
    return State(insert(st.rows,v,Row(seen,tuple(entries))),insert(st.targets,w,desc),
                 insert(st.pairs,v,insert(pairroot,w,True)),insert(st.keys,z,meta),
                 flag_change(st.unfinished,bad(before,K)[0],bad(meta,K)[0]),
                 flag_change(st.excessive,bad(before,K)[1],bad(meta,K)[1]))

def oracle(seq,k=2,d=2,s=1):
    groups={};seen=set();rows={}
    for v,w in seq:
        z=key(w)
        if z is None or abs(n.cls(v)-n.cls(z))>s:return None
        rows.setdefault(v,set()).add(z)
        if len(rows[v])>d:return None
        a,closed,edges=groups.setdefault(z,([],False,set()))
        ws={b for x,b in edges}
        if w not in ws:
            if len(a)<k:a.append(w)
            else:closed=True
        if closed and w in a and (v,w) not in edges:return None
        if closed and not any((v,t) in edges for t in a):return None
        edges.add((v,w));groups[z]=(a,closed,edges)
    return groups

def inspect(st,seq,K=2,k=2,d=2,s=1):
    groups=oracle(seq,k,d,s);assert groups is not None
    ok=True;badkeys=0;excesskeys=0
    for z,(anchors,frozen,edges) in groups.items():
        meta=lookup(st.keys,z);assert meta.anchors==len(anchors) and meta.frozen==frozen
        vs={v for v,w in edges};ws={w for v,w in edges}
        profile={v:sum(1<<j for j,w in enumerate(anchors) if (v,w) in edges) for v in vs}
        for v in vs:
            row=lookup(st.rows,v);idx=lookup(row.seen,z)-1
            assert ''.join(row.entries[idx][0])==z and row.entries[idx][2]==profile[v]
        h=[sum(m==a for a in profile.values()) for m in range(1<<k)]
        assert h[0]==0 and [count(x) for x in meta.hist]==h
        hist=[0]*(1<<((1<<k)-1))
        rectangles=True
        for w in ws:
            mask=sum(1<<(m-1) for m in range(1,1<<k) if any(b==w and profile[v]==m for v,b in edges))
            desc=lookup(st.targets,w)
            got=anchor_mask(meta,desc[1]) if desc[0]=='anchor' else desc[1]
            assert got==mask;hist[mask]+=1
        if frozen:
            assert [count(x) for x in meta.population]==hist
            for m in range(1,1<<k):
                es={(v,w) for v,w in edges if profile[v]==m};V={v for v in vs if profile[v]==m};T={w for v,w in es}
                f=meta.cells[m-1];debt=len(V)*len(T)-len(es)
                # Empty profiles have no actual targets even if m has set bits.
                if not V:
                    assert count(f.targets)==0 and f.deficit is None
                    continue
                assert count(f.predecessors)==len(V) and count(f.targets)==len(T) and frame_count(f.deficit)==debt
                rectangles &= not debt
        diversity=len({frozenset(v for v,b in edges if b==w) for w in ws})<=K
        if rectangles:assert diversity==(sum(bool(x) for x in hist)<=K)
        badkeys+=not rectangles
        excesskeys+=sum(bool(x) for x in hist)>K
        ok &= rectangles and diversity
    assert count(st.unfinished)==badkeys and count(st.excessive)==excesskeys
    assert ok==(st.unfinished is None and st.excessive is None)
    return ok

def add_left(roots,frozen,u,v,k=2,d=2,s=1):
    receipt=lookup(frozen.rows,v) or Row();out=[]
    for m,root in enumerate(roots,1):
        outer=n.walk(root,u);index=view(outer[1])[0] or {}
        serviced,j=n.service_for_name(index,v,d,s)
        jobs={}
        for word,offset,profile in receipt.entries:
            if profile==m:jobs.setdefault(j+offset,[]).append(word)
        for bucket,words in jobs.items():
            old=serviced.get(bucket,n.Bucket());assert not old.pending
            serviced[bucket]=n.Bucket(old.committed,tuple(words))
        out.append(close(outer,serviced))
    return tuple(out)

stats=dict(sequences=0,prefixes=0,retained_versions=0,queries=0)
def check(seq,K=2,k=2,d=2,s=1,query=True):
    stats['sequences']+=1;st=State();versions=[st]
    for i,(v,w) in enumerate(seq):
        st=step(st,v,w,k,d,s,K)
        if st is None:assert oracle(seq,k,d,s) is None;return None
        inspect(st,seq[:i+1],K,k,d,s);stats['prefixes']+=1;versions.append(st)
    for i,old in enumerate(versions):inspect(old,seq[:i],K,k,d,s);stats['retained_versions']+=1
    if inspect(st,seq,K,k,d,s) and query:
        roots=(B,)*((1<<k)-1);marks=set();leftversions=[(roots,marks)]
        for i,v in enumerate(sorted({v for v,w in seq})+['111']):
            u=str(i%2);roots=add_left(roots,st,u,v,k,d,s);marks=marks|{(u,v)};leftversions.append((roots,marks))
        for roots,marks in leftversions:
            for u,w in product(('0','1'),sorted({w for v,w in seq})+['0001']):
                desc=lookup(st.targets,w);z=key(w)
                mask=0 if desc is None else (anchor_mask(lookup(st.keys,z),desc[1]) if desc[0]=='anchor' else desc[1])
                got=any(mask&(1<<c) and n.index_contains(lookup(roots[c],u) or {},z) for c in range(len(roots)))
                want=any((u,v) in marks and b==w for v,b in seq)
                assert got==want;stats['queries']+=1
    return st

def main():
    rng=random.Random(138);pool=list(product(['0','1'],['10','010','0010','00010']))
    for bits in range(1<<len(pool)):
        seq=[e for i,e in enumerate(pool) if bits>>i&1]
        mix=seq[:];rng.shuffle(mix)
        if mix:mix.append(mix[0])
        for order in (seq,list(reversed(seq)),mix):check(order,K=1+bits%2)
    anchors=[('00','100'),('01','100'),('00','0100'),('10','0100')]
    rest=[('00','00100'),('01','00100'),('00','000100'),('10','000100')]
    for a in permutations(anchors):
        for b in permutations(rest):
            st=check(list(a+b));assert st is not None and inspect(st,list(a+b))
    for _ in range(300):check([rng.choice(pool) for _ in range(rng.randrange(1,20))])
    # Open keys need no closing record or boundary sweep.
    seq=[('0','10'),('1','010'),('0','010'),('1','10')]
    st=check(seq,K=1);assert inspect(st,seq,K=1)
    # A newly named row after anchor closure and a late new anchor edge reject.
    assert check([('0','10'),('1','010'),('0','0010'),('00','0010')]) is None
    assert check([('0','10'),('1','010'),('0','0010'),('1','10')]) is None
    # Exact anchor duplicates after closure are harmless.
    seq=[('0','10'),('1','010'),('0','0010'),('0','10')]
    assert inspect(check(seq),seq)
    # Interleaved normalized keys and per-key closure.
    for _ in range(100):
        seq=[('0','10'),('1','010'),('0','11'),('1','011')]
        rng.shuffle(seq);seq += [('0','0010'),('1','0011')]
        check(seq)
    # Profile classes and their actual names are determined by the relation.
    profile_assignments=0
    rows=['00','01','10','11'];z='00';ts=['0'*j+'1'+z for j in range(4)]
    for profiles in product((1,2,3),repeat=len(rows)):
        if not all(any(m&(1<<j) for m in profiles) for j in range(2)):continue
        pre=[(v,ts[j]) for v,m in zip(rows,profiles) for j in range(2) if m&(1<<j)]
        tail=[(v,ts[j+2]) for v,m in zip(rows,profiles) for j in range(2) if m&(1<<j)]
        rng.shuffle(pre);rng.shuffle(tail)
        st=check(pre+tail);assert st is not None and inspect(st,pre+tail)
        profile_assignments+=1
    growing=[]
    for L in (3,4,5):
        vs=[''.join(v) for v in product('01',repeat=L)];profiles=[1+i%3 for i in range(len(vs))]
        z='0'*L;ts=['0'*j+'1'+z for j in range(6)]
        pre=[(v,ts[j]) for v,m in zip(vs,profiles) for j in range(2) if m&(1<<j)]
        tail=[(v,ts[j]) for v,m in zip(vs,profiles) for j in range(2,6) if m&(1<<(j%2))]
        rng.shuffle(pre);rng.shuffle(tail)
        st=check(pre+tail,d=1,s=0);assert st is not None and inspect(st,pre+tail,d=1,s=0)
        growing.append(len(vs))
    st=State();excess=[]
    for v,w in anchors+rest:
        st=step(st,v,w);excess.append(count(st.excessive))
    assert max(excess)==1 and excess[-1]==0
    for anchor_budget in (1,3):
        for _ in range(64):
            check([rng.choice(pool) for _ in range(rng.randrange(1,12))],k=anchor_budget,K=2)
    print(json.dumps({**stats,'anchor_and_tail_permutations':576,'profile_assignments':profile_assignments,'growing_row_counts':growing,'transient_excess_keys':excess,'native_timing_evidence':False},indent=2))
if __name__=='__main__':main()