Research source · python
check_ancestral_selectors.py
tools/research/check_ancestral_selectors.py
200 lines. Source is displayed for inspection; it is not executed by this page.
File fingerprint
SHA-256: fd5a922f9b5586855d2d81fc887d63bd6bd73cff0afd9c6f33c3113b4362a9f2
#!/usr/bin/env python3
"""Independent set oracle for bounded snapshot selectors; no native timing claim."""
from collections import Counter
from copy import deepcopy
from itertools import product, permutations, combinations
import random
import json
class Model:
def __init__(self, k=2, K=2):
self.k, self.K = k, K
self.anchors, self.edges, self.codes = [], set(), {}
self.H, self.G = Counter(), Counter()
self.pops, self.cuts, self.late = [], [], set()
self.targets, self.cells = {}, {}
self.invalid = False
def extend(self, code):
h, p = code
return h + (p,) * (len(self.pops) - len(h)), p
def cut(self):
# Finite table reindex, not a scan of literal row receipts.
self.H = Counter({(h + (p,), p): n for (h, p), n in self.H.items() if n})
pop = Counter()
for (h, p), n in self.H.items():
pop[h] += n
self.pops.append(pop)
# Independent oracle snapshots, reconstructed only from raw edges.
rows = {v for v, w in self.edges}
self.cuts.append({v: tuple(sum(1 << j for j, a in enumerate(self.anchors)
if (v, a) in self.edges) for _ in [0])
for v in rows})
def add(self, v, w):
if self.invalid or (v, w) in self.edges:
return
known = w in self.anchors or w in self.targets
if not known and len(self.anchors) < self.k:
self.anchors.append(w)
anchor = w in self.anchors
if not anchor and not self.pops:
self.cut()
if self.pops and v not in self.codes:
self.invalid = True
return
old = self.codes.get(v)
h, p = self.extend(old) if old else ((), 0)
if anchor:
if old:
self.H[h, p] -= 1
p |= 1 << self.anchors.index(w)
self.H[h, p] += 1
self.edges.add((v, w))
if self.pops and w not in self.late:
self.late.add(w)
self.cut()
h += (p,)
self.codes[v] = (h, p)
else:
self.codes[v] = (h, p)
if w not in self.targets:
self.targets[w] = (len(self.pops) - 1, frozenset())
t, mask = self.targets[w]
c = h[:t+1]
if mask:
self.G[t, mask] -= 1
if c not in mask:
self.cells.setdefault((t, c), [self.pops[t][c], 0, 0])[1] += 1
mask = mask | {c}
self.cells[t, c][2] += 1
self.G[t, mask] += 1
self.targets[w] = (t, mask)
self.edges.add((v, w))
def selector(self, w, code):
h, p = code
if w in self.anchors:
return bool(p & (1 << self.anchors.index(w)))
if w not in self.targets:
return False
t, mask = self.targets[w]
c = (h + (p,) * max(0, t+1-len(h)))[:t+1]
return c in mask
def types(self):
atoms = [a for a, n in self.H.items() if n]
masks = {tuple(self.selector(w, a) for a in atoms) for w in self.anchors}
for (t, mask), n in self.G.items():
if n:
masks.add(tuple(h[:t+1] in mask for h, p in atoms))
return len(masks)
def accepted(self):
return not self.invalid and all(V*T == E for V, T, E in self.cells.values()) and self.types() <= self.K
def audit(self):
if self.invalid:
return
profiles = {v: sum(1 << j for j, a in enumerate(self.anchors)
if (v, a) in self.edges) for v in self.codes}
histories = {v: tuple(c[v][0] for c in self.cuts) for v in self.codes}
actualH = Counter((histories[v], profiles[v]) for v in self.codes)
assert +self.H == actualH
for v, code in self.codes.items():
assert self.extend(code) == (histories[v], profiles[v])
for t, pop in enumerate(self.pops):
assert pop == Counter(histories[v][:t+1] for v in self.codes)
for (t, c), (V, T, E) in self.cells.items():
rows = {v for v in self.codes if histories[v][:t+1] == c}
targets = {w for w, (birth, mask) in self.targets.items()
if birth == t and any((v, w) in self.edges for v in rows)}
assert (V, T, E) == (len(rows), len(targets),
sum((v, w) in self.edges for v in rows for w in targets))
valid = all(all((v, w) in self.edges for v in self.codes
if histories[v][:t+1] == histories[x][:t+1])
for w, (t, _) in self.targets.items()
for x in self.codes if (x, w) in self.edges)
assert valid == all(V*T == E for V, T, E in self.cells.values())
if valid:
columns = {frozenset(v for v in self.codes if (v, w) in self.edges)
for w in self.anchors + list(self.targets)}
assert self.types() == len(columns)
for v, code in self.codes.items():
for w in self.anchors + list(self.targets):
assert self.selector(w, code) == ((v, w) in self.edges)
def check_sequence(seq, stats, k=2, K=2):
m = Model(k, K)
retained = []
for edge in seq:
if len(retained) < 5:
retained.append(deepcopy(m))
m.add(*edge)
m.audit()
stats['prefix_checks'] += 1
for old in retained:
old.audit()
stats['retained_versions'] += 1
stats['sequences'] += 1
if m.accepted():
rows = list(m.codes)
for bits in product((0, 1), repeat=len(rows)):
chosen = {v for v, b in zip(rows, bits) if b}
codes = {m.codes[v] for v in chosen}
for w in m.anchors + list(m.targets) + ['missing']:
got = any(m.selector(w, c) for c in codes)
expect = any((v, w) in m.edges for v in chosen)
assert got == expect
stats['product_queries'] += 1
return m
def main():
stats = Counter()
# Exhaustive small orders exercise discovery, duplicates and invalid new rows.
base = [(0,'a'),(1,'a'),(1,'b'),(0,'t'),(1,'t'),(0,'b')]
for seq in permutations(base):
check_sequence(seq, stats)
rng = random.Random(140)
for _ in range(1500):
seq = [(rng.randrange(4), rng.choice('abtu')) for _ in range(rng.randrange(1, 18))]
check_sequence(seq, stats)
for k in (1, 3):
for _ in range(200):
seq = [(rng.randrange(4), rng.choice('abcuv'))
for _ in range(rng.randrange(1, 20))]
check_sequence(seq, stats, k=k, K=4)
m3 = check_sequence([(0,'a'),(1,'b'),(2,'c'),(0,'t'),(1,'t'),(2,'t'),
(1,'a'),(2,'b'),(0,'c'),(2,'a'),(0,'b'),(1,'c')],
stats, k=3, K=4)
assert m3.accepted() and len(m3.pops) == 4
# General family with two final types and arbitrarily many late anchor edges.
for n in range(2, 7):
P, Q = list(range(n)), list(range(n, n+2))
for size in range(1, n):
for U in combinations(P, size):
seq = [(v,'a') for v in P] + [(v,'b') for v in Q]
seq += [(v,'t') for v in P+Q]
seq += [(v,'b') for v in U] + [(Q[0],'a')]
seq += [(v,'u') for v in list(U)+Q] + [(Q[1],'a')]
m = check_sequence(seq, stats)
assert m.accepted() and len(m.pops) == 3
stats['two_type_families'] += 1
# Run139 early split target must still fail despite a later matching profile.
seq = [(0,'a'),(1,'a'),(2,'a'),(2,'b'),(0,'t'),(2,'t'),(0,'b')]
assert not check_sequence(seq, stats).accepted()
# A cut does not mutate untouched row codes; left readout interprets them.
m = check_sequence([(0,'a'),(1,'a'),(2,'b'),(0,'t'),(1,'t'),(2,'t'),
(0,'b'),(2,'a')], stats, K=4)
assert len(m.codes[1][0]) < len(m.pops)
assert m.selector('t', m.codes[1])
stats['native_timing_evidence'] = False
print(json.dumps(stats, indent=2, sort_keys=True))
if __name__ == '__main__':
main()