Research source · python

check_sparse_age_minimum.py

tools/research/check_sparse_age_minimum.py

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

File fingerprint

SHA-256: cd0e5e9afe8d73ee6916d075f9208fa7001018e28a5274d5418e0f47d72d50b2

#!/usr/bin/env python3
"""Immutable sparse-age minimum worker with no priority or endpoint-order oracle.

Unary age/gap links, task stacks and endpoint digit lists are explicit.
Right-input production and scaffold packing are inherited; no arithmetic
counter, priority selector or endpoint-order primitive occurs in the worker.
Direct integer minima occur only in the independent audit.
"""
from dataclasses import dataclass, replace
from random import Random
import json


@dataclass(frozen=True)
class Unit:
    tail: object = None


ONE = Unit()


@dataclass(frozen=True)
class Digit:
    bit: int
    tail: object = None


def numeral(n):
    # Test preparation, outside worker; one actual digit node per bit.
    head = None
    for c in bin(n)[2:]:
        head = Digit(int(c), head)
    return head


def decode(p):
    n, power = 0, 1
    while p is not None:
        n += power*p.bit
        power *= 2
        p = p.tail
    return n


def depth(p):
    n = 0
    while p is not None:
        n += 1
        p = p.tail
    return n


@dataclass(frozen=True)
class Job:
    endpoint: object
    cursor: object


@dataclass(frozen=True)
class Stack:
    head: object
    tail: object = None


@dataclass(frozen=True)
class Compare:
    candidate: object
    left: object
    right: object
    order: int = 0


@dataclass(frozen=True)
class Bucket:
    age: object = None
    gap: object = None
    jobs: object = None
    comparison: object = None
    best: object = None
    following: object = None


def release(root, endpoint):
    task = Stack(Job(endpoint, endpoint))
    if root is None:
        return Bucket(jobs=task)
    if root.age is None:
        return replace(root, jobs=Stack(task.head, root.jobs))
    return Bucket(gap=root.age, jobs=task, following=root)


def idle(root):
    return root is None or (root.jobs is None and root.comparison is None)


def step(root):
    if idle(root):
        return root
    if root.comparison is not None:
        c = root.comparison
        a = 0 if c.left is None else c.left.bit
        b = 0 if c.right is None else c.right.bit
        order = (1 if a > b else -1) if a != b else c.order
        left = None if c.left is None else c.left.tail
        right = None if c.right is None else c.right.tail
        if left is None and right is None:
            return replace(root, comparison=None,
                           best=c.candidate if order < 0 else root.best)
        return replace(root, comparison=Compare(c.candidate, left, right, order))
    job, rest = root.jobs.head, root.jobs.tail
    cursor = job.cursor.tail
    if cursor is None:
        if root.best is None:
            # A shorter length class dominates the entire following chain.
            return Bucket(root.age, jobs=rest, best=job.endpoint)
        return replace(root, jobs=rest,
                       comparison=Compare(job.endpoint, job.endpoint, root.best))
    advanced = Job(job.endpoint, cursor)
    if root.best is not None:
        # The unfinished endpoint has crossed beyond the best's length class.
        return replace(root, jobs=rest)
    nxt = root.following
    if nxt is not None and root.gap.tail is None:
        promoted = replace(nxt, jobs=Stack(advanced, nxt.jobs))
    else:
        promoted = Bucket(Unit(root.age),
                          None if nxt is None else root.gap.tail,
                          Stack(advanced), following=nxt)
    if rest is None:
        return promoted
    return replace(root, jobs=rest, gap=ONE, following=promoted)


def serve(root, n):
    for _ in range(n):
        if idle(root):
            break
        root = step(root)
    return root


def inspect(root):
    """Independent whole-state invariant checker, not a worker operation."""
    records, completed, ages = [], [], []
    p = root
    while p is not None:
        j = depth(p.age)
        ages.append(j)
        assert p.jobs is not None or p.comparison is not None or p.best is not None
        if p.following is not None:
            assert depth(p.gap) == depth(p.following.age)-j > 0
        else:
            assert p.gap is None
        if p.best is not None:
            b = decode(p.best)
            assert b.bit_length() == j+1
            assert p.following is None
            completed.append(b)
        if p.comparison is not None:
            assert p.best is not None
            records.append(decode(p.comparison.candidate))
        jobs = p.jobs
        while jobs is not None:
            t = jobs.head
            assert decode(t.endpoint).bit_length()-decode(t.cursor).bit_length() == j
            assert t.cursor is not None
            records.append(decode(t.endpoint))
            jobs = jobs.tail
        p = p.following
    assert ages == sorted(set(ages))
    return records+completed


def finite_main():
    rng = Random(1931)
    states = answers = forks = interrupts = 0
    for trial in range(100):
        root, values, saved = None, [], []
        for i in range(70):
            # Small service keeps several ages and interruptions live.
            root = serve(root, rng.randrange(0, 7))
            v = rng.choice((1, 2, 3, 7, 17, rng.randrange(1, 1 << 25), 1 << rng.randrange(30, 250)))
            root = release(root, numeral(v))
            values.append(v)
            assert min(inspect(root)) == min(values)
            saved.append((root, tuple(values)))
            states += 1
        for old, vals in rng.sample(saved, 8):
            before = tuple(inspect(old))
            t = old
            # Complete all remaining work for exact-minimum oracle comparison.
            while not idle(t):
                t = step(t)
                assert min(inspect(t)) == min(vals)
                states += 1
            assert decode(t.best) == min(vals)
            assert tuple(inspect(old)) == before
            forks += 1
            for n in (0, 1, 2, 3, min(vals)-1, min(vals), min(vals)+1):
                assert (decode(t.best) <= n) == any(v <= n for v in vals)
                answers += 1
    # A live age-30 comparison is interrupted at every digit boundary. New
    # lower ages may reach or delete its bucket; same-age promotions preserve it.
    base, candidate = (1 << 30)+27, (1 << 30)+19
    root = serve(release(None, numeral(base)), 10000)
    root = release(root, numeral(candidate))
    while root.comparison is None:
        root = step(root)
    for delay in range(31):
        old = serve(root, delay)
        for v in (2, (1 << 30)+3, (1 << 31)+1):
            t = release(old, numeral(v))
            t = serve(t, 10000)
            assert idle(t) and decode(t.best) == min(base, candidate, v)
            assert decode(old.best) == base
            interrupts += 1
    # Build a long sparse frontier before any numeral completes, then test
    # both a gap split and promotion into an occupied successor bucket.
    deep = None
    for j in range(10, -1, -1):
        deep = release(deep, numeral(1 << 4096))
        deep = serve(deep, (1 << j)-1)
        inspect(deep)
    p, bucket_count = deep, 0
    while p is not None:
        bucket_count += 1
        p = p.following
    assert bucket_count >= 9
    deep_forks = 0
    for delay in range(200):
        frozen = serve(deep, delay)
        old_values = tuple(inspect(frozen))
        for v in (2, 17, 1 << 40):
            t = serve(release(frozen, numeral(v)), 50000)
            assert idle(t) and decode(t.best) == v
            assert tuple(inspect(frozen)) == old_values
            deep_forks += 1

    # Continuous query service and explicit lag repair; no cutoff/age oracle.
    # Reader lengths model the inherited unary rulers, not an order operation.
    phase_answers = 0
    for trial in range(50):
        root, vals = None, []
        for i in range(60):
            name = bin(i+2)[2:]
            root = serve(root, 16*len(name))
            v = rng.choice((2, 3, 127, 4096, 1 << 300))
            root = release(root, numeral(v))
            vals.append(v)
        lower = None
        lag = 0
        aligned = False
        for n in range(1, 257):
            if lower is None:
                root = serve(root, 256)
                if idle(root):
                    lower, lag = decode(root.best), n
            elif aligned:
                lower = max(0, lower-1)
            else:
                lag += 1
            if lower is not None and not aligned:
                for _ in range(4):
                    if lag:
                        lag -= 1
                        lower = max(0, lower-1)
                aligned = lag == 0
            assert (aligned and lower == 0) == (min(vals) <= n)
            phase_answers += 1
    # A partially screened enormous job does not hide a late endpoint 2.
    old = serve(release(None, numeral(1 << 50000)), 1000)
    t = release(old, numeral(2))
    t = serve(t, 256)
    assert idle(t) and decode(t.best) == 2
    assert not idle(old)
    print(json.dumps(dict(invariant_states=states, retained_forks=forks,
        completed_minimum_answers=answers, comparison_interruptions=interrupts,
        query_prefix_answers=phase_answers, deep_frontier_buckets=bucket_count,
        deep_interruption_forks=deep_forks, priority_oracle=False,
        numeral_order_oracle=False, arithmetic_counter_oracle=False),indent=2))


if __name__ == '__main__':
    finite_main()