Research source · python
check_mixed_budget_producer.py
tools/research/check_mixed_budget_producer.py
356 lines. Source is displayed for inspection; it is not executed by this page.
File fingerprint
SHA-256: daf485f2a25deea30953c201b1706fce11b2e3c5a7bc4f6f7a09c767de8f210b
#!/usr/bin/env python3
"""Finite immutable audit of mixed-budget preparation and reuse.
Tuple deques, integer phase counters and Python dictionaries model inherited
native primitives; they are not claimed as constant-time native operations.
Point/merge frames and budget changes are explicit. Direct leaf sets are the
independent semantic oracle. Actual word bits alone provide producer service.
"""
from dataclasses import dataclass, replace
from collections import deque
from random import Random
import json
@dataclass(frozen=True)
class Branch:
left: object
right: object
def branch(t):
return isinstance(t, Branch)
def join(a, b):
return a if isinstance(a, bool) and a == b else Branch(a, b)
def bits(t, h):
if not branch(t):
return (t,) * (1 << h)
return bits(t.left, h - 1) + bits(t.right, h - 1)
def mixed(t):
return 0 if not branch(t) else 1 + mixed(t.left) + mixed(t.right)
@dataclass(frozen=True)
class Budget:
credit: int
debt: int = 0
def adjust(self, delta):
c, d = self.credit, self.debt
if delta == 1:
if c: c -= 1
else: d += 1
elif delta == -1:
if d: d -= 1
else: c += 1
assert not (c and d) and min(c, d) >= 0
return Budget(c, d)
@dataclass(frozen=True)
class Point:
node: object
route: tuple
stack: tuple = ()
phase: str = 'down'
result: object = None
def point_step(s):
delta = 0
if s.phase == 'down':
if not s.route or s.node is True:
return replace(s, phase='up', result=True), delta
direction, rest = s.route[0], s.route[1:]
a, b = (s.node.left, s.node.right) if branch(s.node) else (False, False)
frame = (branch(s.node), b if direction == 0 else a, direction)
return Point(a if direction == 0 else b, rest, s.stack + (frame,)), delta
if not s.stack:
return replace(s, phase='done'), delta
old_mixed, sibling, direction = s.stack[-1]
result = join(s.result, sibling) if direction == 0 else join(sibling, s.result)
delta = int(branch(result)) - int(old_mixed)
return replace(s, stack=s.stack[:-1], result=result), delta
@dataclass(frozen=True)
class Job:
width: int
phase: str = 'ruler'
cursor: int = 0
focus: int = 0
point: object = None
steps: int = 0
@dataclass(frozen=True)
class Producer:
seen: frozenset = frozenset()
queue: tuple = ()
active: object = None
trees: tuple = ()
budgets: tuple = ()
bad: int = 0
# Budgets include active deltas; trees commit only on job completion.
def producer_step(p, r):
if p.active is None:
if not p.queue: return p
return replace(p, active=Job(p.queue[0]), queue=p.queue[1:])
s = replace(p.active, steps=p.active.steps + 1)
j = s.width.bit_length()
if s.phase == 'ruler':
if s.cursor < s.width:
return replace(p, active=replace(s, cursor=s.cursor + 1))
return replace(p, active=replace(s, phase='focus'))
if s.phase == 'focus':
i = s.focus + 1
trees, budgets = p.trees, p.budgets
if i > len(trees): trees += (False,)
if i & (i - 1) == 0 and i.bit_length() > len(budgets):
# r*i units prepared incrementally on prior/current focus steps.
budgets += (Budget(r * i),)
if i == j:
route = tuple(int(x) for x in bin(s.width)[3:])
s = replace(s, phase='point', focus=i, point=Point(trees[i-1], route))
else:
s = replace(s, focus=i)
return replace(p, active=s, trees=trees, budgets=budgets)
if s.phase == 'point':
point, delta = point_step(s.point)
b = j.bit_length() - 1
old = p.budgets[b]
new = old.adjust(delta)
budgets = p.budgets[:b] + (new,) + p.budgets[b+1:]
bad = p.bad + int(bool(new.debt)) - int(bool(old.debt))
if point.phase == 'done':
trees = p.trees[:j-1] + (point.result,) + p.trees[j:]
assert s.steps + 1 <= 16 * (s.width + 1)
return replace(p, active=None, trees=trees, budgets=budgets, bad=bad)
return replace(p, active=replace(s, point=point), budgets=budgets, bad=bad)
raise AssertionError(s.phase)
def word(p, r, n, value, service=64):
for _ in range(n):
for _ in range(service): p = producer_step(p, r)
first_width = not any(k == n for k, _ in p.seen)
seen = p.seen | {(n, value)}
p = replace(p, seen=seen, queue=p.queue + ((n,) if first_width else ()))
return p
def complete(p):
widths = {n for n, _ in p.seen}
return all(sum(k == n for k, _ in p.seen) == 1 << n for n in widths)
def check_quiet(p, r):
assert p.active is None and not p.queue
widths = {n for n, _ in p.seen}
for j, t in enumerate(p.trees, 1):
start = 1 << (j - 1)
assert bits(t, j-1) == tuple(n in widths for n in range(start, 2*start))
bad = 0
for ell, budget in enumerate(p.budgets):
start = 1 << ell
m = sum(mixed(t) for t in p.trees[start-1:2*start-1])
assert budget.credit - budget.debt == r*start - m
assert bool(budget.debt) == (m > r*start)
bad += bool(budget.debt)
assert bad == p.bad
@dataclass(frozen=True)
class Merge:
a: object
b: object
stack: tuple = ()
phase: str = 'call'
result: object = None
def merge_step(s):
if s.phase == 'call':
if s.b is False: out = s.a
elif s.b is True or s.a is True: out = True
elif s.a is False: out = s.b
else:
f = ('right', s.a.right, s.b.right)
return Merge(s.a.left, s.b.left, s.stack + (f,))
return replace(s, phase='return', result=out)
if not s.stack: return replace(s, phase='done')
f = s.stack[-1]
if f[0] == 'right':
return Merge(f[1], f[2], s.stack[:-1] + (('join', s.result),))
return replace(s, stack=s.stack[:-1], result=join(f[1], s.result))
@dataclass(frozen=True)
class Bucket:
tree: object = False
jobs: tuple = ()
active: object = None
continuation: tuple = ()
@dataclass(frozen=True)
class Worker:
prefix: tuple = ()
suffix: tuple = ()
def release(w, row):
if not row: return w
seq = w.prefix + w.suffix
b = seq[0] if seq else Bucket()
return Worker((), (replace(b, jobs=(row,) + b.jobs),) + seq[1:])
def service(w):
if not w.suffix: return w
b, tail = w.suffix[0], w.suffix[1:]
if b.active is not None:
active = merge_step(b.active)
if active.phase != 'done':
return Worker(w.prefix, (replace(b, active=active),) + tail)
if b.continuation:
nxt = tail[0] if tail else Bucket()
tail = (replace(nxt, jobs=(b.continuation,) + nxt.jobs),) + tail[1:]
b = replace(b, tree=active.result, active=None, continuation=())
elif b.jobs:
row = b.jobs[0]
b = replace(b, jobs=b.jobs[1:], active=Merge(b.tree, row[0]), continuation=row[1:])
else:
return Worker(w.prefix + (b,), tail)
return Worker(w.prefix, (b,) + tail)
def drain(w, k):
steps = 0
while w.suffix and len(w.prefix) < k:
w = service(w); steps += 1
assert steps < 100000
return w, steps
def query(w, length, rate):
# Explicit local DFS emission; tuple FIFO models the inherited strict FIFO.
ready, w = w.prefix, Worker((), w.suffix)
stack, out, answer, j, ended = (), deque(), [], 1, False
for _ in range(length):
for _ in range(rate):
if not w.suffix: break
w = service(w)
ready += w.prefix; w = Worker((), w.suffix)
for _ in range(1024):
if not stack:
if ready:
stack = ((ready[0].tree, j-1),); ready = ready[1:]; j += 1
elif not w.suffix: ended = True; break
else: break
t, h = stack[-1]; stack = stack[:-1]
if h == 0: out.append(t is True)
else:
a, b = (t.left, t.right) if branch(t) else (t, t)
stack += ((b, h-1), (a, h-1))
assert out or ended
answer.append(out.popleft() if out else False)
return answer
def main():
rng = Random(201660)
counts = dict(point_cases=0, producer_words=0, complete_boundaries=0,
retained_versions=0, consumer_releases=0, residual_checks=0,
query_prefixes=0, budget_cutoffs=0)
# Exhaustive small trees, plus deep final-hole collapses.
for h in range(4):
width = 1 << h
for mask in range(1 << width):
def build(lo, size):
vals = [(mask >> x) & 1 for x in range(lo, lo+size)]
if all(vals): return True
if not any(vals): return False
return join(build(lo, size//2), build(lo+size//2, size//2))
old = build(0, width)
for x in range(width):
route = tuple((x >> b) & 1 for b in reversed(range(h)))
s, d = Point(old, route), 0
while s.phase != 'done': s, change = point_step(s); d += change
expect = tuple(bool((mask >> i) & 1) or i == x for i in range(width))
assert bits(s.result, h) == expect
assert mixed(s.result) == mixed(old) + d
counts['point_cases'] += 1
# Real complete word sets, arbitrary row interleaving and duplicates.
for _ in range(120):
r = rng.randrange(5); rows = [Producer() for _ in range(3)]
records = []
for row in range(3):
for n in rng.sample(range(1, 7), rng.randrange(1, 5)):
records += [(row, n, value) for value in range(1 << n)]
records += rng.choices(records, k=25); rng.shuffle(records)
retained = []
for row, n, value in records:
if rng.randrange(30) == 0: retained.append(rows[row])
rows[row] = word(rows[row], r, n, value)
counts['producer_words'] += 1
if complete(rows[row]):
check_quiet(rows[row], r); counts['complete_boundaries'] += 1
for p in rows: check_quiet(p, r)
for p in retained:
frozen = repr(p)
q = p
for _ in range(1000):
if q.active is None and not q.queue: break
q = producer_step(q, r)
check_quiet(q, r); assert repr(p) == frozen
counts['retained_versions'] += 1
# Many runs within one band, with complete producer input conceptually
# represented here by independent canonical semantic masks.
pool = []
for _ in range(100):
row = []
for j in range(1, rng.randrange(2, 8)):
vals = [rng.randrange(2) for _ in range(1 << (j-1))]
def make(v):
if all(v): return True
if not any(v): return False
m = len(v)//2; return join(make(v[:m]), make(v[m:]))
row.append(make(vals))
r = max((sum(mixed(t) for t in row[(1 << ell)-1:(1 << (ell+1))-1])
+ (1 << ell)-1)//(1 << ell) for ell in range(len(row).bit_length()))
for k in range(1, len(row)+2):
assert sum(mixed(t) for t in row[:k]) <= 2*r*k
counts['budget_cutoffs'] += 1
pool.append((tuple(row), r))
for _ in range(60):
w, selected = Worker(), set()
r = max(v for _, v in pool); c = 64*(2*r+2)
for _ in range(12):
row, _ = rng.choice(pool)
for _ in range(rng.randrange(60)): w = service(w)
old = w; new = release(old, row)
for k in range(1, 8):
_, before = drain(old, k); _, after = drain(new, k)
assert after-before <= c*k
counts['residual_checks'] += 1
for j, t in enumerate(row, 1):
start = 1 << (j-1)
selected |= {start+i for i, v in enumerate(bits(t, j-1)) if v}
w = new; frozen = repr(w)
got = query(w, 100, 1024*c)
assert got == [n in selected for n in range(1, 101)]
assert repr(w) == frozen
counts['consumer_releases'] += 1; counts['query_prefixes'] += len(got)
print(json.dumps(counts, sort_keys=True))
if __name__ == '__main__': main()