Research source · python

check_learned_contexts.py

tools/research/check_learned_contexts.py

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

File fingerprint

SHA-256: 948390f122656b2e54d8fa036b5585f27e5fa08cf015540e22fdfe76413b3e97

#!/usr/bin/env python3
"""Learned-context producer and inherited pending-index finite audit.

Immutable trie and pending-reader operations reuse the maintained section15
model. Host tuples, dictionaries, lengths and clocks are semantic stand-ins,
not native instructions. Literal full-word sets form the separate oracle.
"""
import json
from itertools import permutations, product
from random import Random
from check_pending_substitution import (
    B, Bucket, Reader, RightRow, STATS, add_left, close, cls, lookup, walk)


def prepare(records, cap, degree, span):
    contexts, rows = [], {}
    captures = 0
    for v, w in records:
        if not v or not w:
            return None
        remaining, window, fresh = len(v) + 1, (), ()
        cursors = [tuple(p) for p in contexts]
        viable = [True] * len(contexts)
        tails = [() for _ in contexts]
        for bit in w:
            if remaining:
                remaining -= 1
            else:
                fresh += window[:1]
                window = window[1:]
            window += (bit,)
            for j in range(len(contexts)):
                if not viable[j]:
                    continue
                if cursors[j]:
                    viable[j] = cursors[j][0] == bit
                    cursors[j] = cursors[j][1:]
                else:
                    tails[j] += (bit,)
        matches = [j for j in range(len(contexts))
                   if viable[j] and not cursors[j]]
        if matches:
            j = matches[0]
            tail = tails[j]
        else:
            if remaining or len(contexts) == cap:
                return None
            j, tail = len(contexts), window
            contexts.append(fresh)
            captures += 1
        if not tail or abs(cls(v) - cls(tail)) > span:
            return None
        old = rows.get(v, ())
        if (j, tail) not in old:
            old += ((j, tail),)
        if len(old) > degree:
            return None
        rows[v] = old
    frozen = [B] * cap
    for v, targets in rows.items():
        for j in range(cap):
            receipt = RightRow(targets=tuple(
                (tail, cls(tail)-cls(v)) for tag, tail in targets if tag == j))
            frozen[j] = close(walk(frozen[j], v), receipt)
    return tuple(contexts), tuple(frozen), captures


class Lane:
    """Each feed is one actual bit, with no terminal replay."""
    def __init__(self, context, index):
        self.context, self.index = context, index
        self.ok, self.n, self.j = True, 0, 0
        self.current = self.ahead = None
        self.prefix = self.queue = ()

    def feed(self, bit):
        if not self.ok:
            return
        if self.context:
            self.ok = self.context[0] == bit
            self.context = self.context[1:]
            return
        self.n += 1
        if self.n == 1:
            self.current = Reader(self.index.get(0, Bucket()))
            self.current.feed(bit)
            self.prefix = self.queue = (bit,)
            self.ahead = Reader(self.index.get(1, Bucket()))
            return
        self.current.feed(bit)
        self.prefix += (bit,)
        self.queue += (bit,)
        for _ in range(2):
            if self.queue:
                self.ahead.feed(self.queue[0])
                self.queue = self.queue[1:]
        if self.n == 2 ** (self.j + 1):
            assert not self.queue and self.ahead.consumed == self.n
            self.current = self.ahead
            self.j += 1
            self.ahead = Reader(self.index.get(self.j + 1, Bucket()))
            self.queue = self.prefix

    def answer(self):
        return bool(self.ok and not self.context and self.n
                    and self.current.contains())


def main():
    rng = Random(207)
    counts = dict(tables=0, versions=0, prefix_queries=0, negative=0,
                  context_captures=0)
    cases = []
    for stem in ('', '0', '101', '0'*32):
        for perm in permutations(('00', '01', '10')):
            records = [(v, stem + q + b) for v, q in zip(('00','01','10'), perm)
                       for b in '01']
            cases.append((records, 1, 2, 1))
    # Later shorter contexts, empty contexts, arbitrary targets and row overlap.
    cases += [([('0','0100'),('1','000'),('00','11')],3,3,1),
              ([('0','000'),('0','100'),('1','011')],2,2,1)]
    for _ in range(100):
        names = [''.join(rng.choice('01') for _ in range(rng.randint(1,5)))
                 for _ in range(7)]
        records = [(v, ''.join(rng.choice('01') for _ in range(rng.randint(2,12))))
                   for v in names]
        cases.append((records,3,3,2))
    for records, cap, degree, span in cases:
        result = prepare(records + records[-1:], cap, degree, span)
        if result is None:
            continue
        contexts, frozen, captures = result
        counts['tables'] += 1
        counts['context_captures'] += captures
        literal = {}
        for v,w in records:
            literal.setdefault(v,set()).add(w)
        roots, selected, snapshots = (B,)*cap, {}, []
        aliases = list(literal) + ['1111111']
        for t in range(18):
            u, v = ('0','101')[t%2], rng.choice(aliases)
            roots = tuple(add_left(root, table, u, v, degree, span)
                          for root,table in zip(roots,frozen))
            selected = {key:set(value) for key,value in selected.items()}
            selected.setdefault(u,set()).update(literal.get(v,set()))
            snapshots.append((roots,selected))
        # Revisit every old root after all later updates have completed.
        for roots, selected in snapshots:
            counts['versions'] += 1
            queries = {w for _,w in records}
            queries |= {w[:-1] for _,w in records} | {w+'1' for _,w in records}
            queries |= {''.join(bits) for n in range(1,5) for bits in product('01',repeat=n)}
            for u in ('0','101','111'):
                truth = selected.get(u,set())
                for w in queries:
                    lanes = [Lane(p, lookup(roots[j],u) or {})
                             for j,p in enumerate(contexts)]
                    for n,bit in enumerate(w,1):
                        for lane in lanes:
                            lane.feed(bit)
                        got = any(lane.answer() for lane in lanes)
                        assert got == (w[:n] in truth), (contexts,u,w,n)
                        counts['prefix_queries'] += 1
                        counts['negative'] += not got
    assert prepare([],1,2,1) == ((),(B,),0)
    assert prepare([('00','0')],1,2,1) is None
    assert prepare([('0','000'),('1','0')],1,2,1) is None
    assert counts['context_captures'] > counts['tables']
    print(json.dumps({**counts,'inherited_pending_model':STATS},sort_keys=True))


if __name__ == '__main__':
    main()