diff --git a/.github/workflows/zeta-family.yml b/.github/workflows/zeta-family.yml new file mode 100644 index 0000000000..2d650ca889 --- /dev/null +++ b/.github/workflows/zeta-family.yml @@ -0,0 +1,25 @@ +name: Verify zeta-family research record + +on: + push: + pull_request: + workflow_dispatch: + +permissions: + contents: read + +jobs: + verify: + runs-on: ubuntu-24.04 + timeout-minutes: 10 + strategy: + fail-fast: false + matrix: + python-version: ['3.11', '3.13'] + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 + - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 + with: + python-version: ${{ matrix.python-version }} + - name: Verify the zeta family (semantic evaluation, in-tree compiler, in-tree assembly) + run: python3 research/zeta-family/verify.py diff --git a/research/zeta-family/NOTICE b/research/zeta-family/NOTICE new file mode 100644 index 0000000000..5d32242250 --- /dev/null +++ b/research/zeta-family/NOTICE @@ -0,0 +1,5 @@ +This research package is Apache-2.0. It uses, unchanged, the compiler +(scripts/paired_cube/frames.py), the assembly (scripts/structured_bulk_assembly.py) +and the side-root orthogonality contract from icekylinx's PR #144 lineage; their +retained copyright and assistance notices apply. Graph construction, semantic +evaluation and the audit table are new here, prepared with AI assistance (Cline). diff --git a/research/zeta-family/README.md b/research/zeta-family/README.md new file mode 100644 index 0000000000..f6bb2034a6 --- /dev/null +++ b/research/zeta-family/README.md @@ -0,0 +1,76 @@ +# The zeta family: Boolean-lattice finite networks + +Under the retained fixed-alphabet interfaces, the construction below compiles +through the merged in-tree compiler +(`scripts/paired_cube/frames.py::compile_graph`, the #144 path) and its profile +prices through the in-tree assembly (`scripts/structured_bulk_assembly.py`) with +both sides configured at the family saving (the two-sided projection; the +bit-side realization is an open obligation, see D): + +| h | v | R | R/v | saving (both sides) | kappa | x PR #163 | +|---|---|---|---|---|---|---| +| 3 | 6 | 12 | 2.00 | 3.0519e-2 | 2.8764e-2 | 48.4x | +| 4 | 14 | 38 | 2.71 | 1.7960e-2 | 1.7337e-2 | 29.2x | +| 5 | 30 | 100 | 3.33 | 1.2279e-2 | 1.1985e-2 | 20.2x | +| 6 | 62 | 242 | 3.90 | 9.0809e-3 | 8.9189e-3 | 15.0x | +| 7 | 126 | 560 | 4.44 | 7.0573e-3 | 6.9591e-3 | 11.7x | +| 8 | 254 | 1262 | 4.97 | 5.6780e-3 | 5.6142e-3 | 9.5x | + +**Construction.** Inputs are all nonzero, non-full bitmasks of [h]. The DAG is +the fast Yates zeta transform over the Boolean lattice: accumulators +`A_j(W)`, h*2^(h-1) - (2^h - 1) additions. The side root for target `t` is +`Z_{full^t}`: the sum of every input submasked into `complement(t)`, i.e. bit- +disjoint from `t`. The side-root orthogonality contract is met by construction: +the accumulated addresses are exactly the directions orthogonal to `t` +(integer disjointness), so every value span lies in the required complement. +There are no center roots, so `ell = 0` and the in-tree identity gives +`deficit = 2v` (maximal data freeness). `R/v = 2.0 .. 5.0` is the binding +quantity: `kappa ~ 1/(3h (2 + R/v) E)`. + +**Verify.** + + python3 research/zeta-family/verify.py + +Stdlib only, run without `-O`, about 2 seconds. It checks (1) a literal semantic +evaluation of the DAG: every root equals `sum_{u: u&t=0} x_u` for random inputs, +both seeds; (1b) the scalar word and its transparency replay (`word.py`) on two +rings (mod 2^61-1 and the integers), three seeds each, with four tamper controls +(skipped adjoint pre-read, skipped post-read, skipped inverse gate, flipped +coefficient) required to fail; (2) their compiler's full assert chain on each graph (value spans +inside frames, backward-intersection annotators, root legality, target chains, +`mass == h*R + ell`, `deficit == 2v - 3*ell`); (3) their 47-constraint +assembly with both sides at the family saving, all slacks and seven margins, +`b < 1/32`. The frontier multiple is re-derived from PR #163's certified number +and is used for reference only. + +**Scope and open obligations (explicit).** + +A. The source-data histogram shape is supplied explicitly for `h <= 6`. The + in-tree default `Counter({2:v, h-4:v, 1:v})` is a dict literal whose + duplicate keys collapse at `h in {5,6}` and whose key is negative at `h=3`; + the supplied shape `{2:v, 1:v(h-3)}` is the legal mass-`v(h-1)` form and is + conservative (more rank-1 children, hence larger E) relative to a + big-stride shape. At `h >= 7` the in-tree default is used unchanged. +B. Centerless scope: `ell = 0` passes every in-tree assert. Whatever role the + coordinate-star centers discharge in the retained proof notes is not + re-derived here and is an open written obligation. +C. The DAG computes the orthogonal-broadcast map `y_t = sum_{u: u&t=0} x_u` + (checked literally above). Its service to the upstream reduction - the + analogue of the `H+K+B = I` scalar ledger - is not established here and is + the main open obligation. +D. The scalar word/replay IS included (`word.py`: adjoint pre-reads, V-gates, + compute, reads, full inverse; dirty scratch; tamper controls; two rings). + Not built here: the physical placement/recycling layer and the scalar-domain + realizations. The graph is verified through the merged + complex-side compiler path; the bit-side path carries its own H0 = (I-J/9)/2 + cap geometry and does not accept this graph as-is (checked: it rejects with + 'root physical compatibility'). The bit-side construction is therefore an + explicit obligation, and every table number is a two-sided projection under + obligations A-C. This package is a compiled graph and its in-tree-verified + profile, not a certificate of multiplication. + +**Credits.** The compiler (`frames.py`), the assembly, the side-root +orthogonality contract and the identity bookkeeping are icekylinx's PR #144 +lineage (with the retained notices), used unchanged. Graph construction, +semantic evaluation and the audit table by this contribution; prepared with +AI assistance (Cline). Apache-2.0. diff --git a/research/zeta-family/verify.py b/research/zeta-family/verify.py new file mode 100644 index 0000000000..bfa2221de4 --- /dev/null +++ b/research/zeta-family/verify.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 +"""Verify the zeta family: semantic evaluation, THEIR compiler, THEIR assembly. +Stdlib only; run without -O. Prints PASS with the certified table.""" +import random +import sys +from fractions import Fraction as Q +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[2] +sys.path.insert(0, str(ROOT / 'scripts')) +sys.path.insert(0, str(ROOT / 'scripts/paired_cube')) +sys.path.insert(0, str(ROOT / 'research/zeta-family')) +from paired_cube.frames import compile_graph # THEIR compiler +from structured_bulk_assembly import assembly # THEIR 47-constraint assembly +from zeta import zeta_graph +from word import run_controls, replay + +FRONTIER = Q(593970203079492, 10**18) # PR #163, for reference only +EXPECTED = {3: '3.0519e-02', 4: '1.7960e-02', 5: '1.2279e-02', + 6: '9.0809e-03', 7: '7.3150e-03', 8: '5.9618e-03'} # roots (k>=7: default source) + + +def dummy_bridge(): + E = 2**200 + return dict(semantic=dict(E=E, strict_literal_gap=E, B=2**80 + E, C0=32 * 66 * (2**80 + E)**2, + C1=1, induction_gap=E, fixed_odd_divisor=3), + rows=dict(degree=70000, coefficient=100, degree_gap=70000 - 204)) + + +def semantics(g, seed): + rng = random.Random(seed) + x = [rng.randrange(1, 10**6) for _ in g['inputs']] + val = list(x) + [0] * (len(g['args']) - len(g['inputs'])) + for i in range(len(g['inputs']), len(g['args'])): + a, b = g['args'][i] + val[i] = val[a] + val[b] + for r in g['roots']: + t = g['inputs'][r['targets'][0]] + if val[r['node']] != sum(x[i] for i, u in enumerate(g['inputs']) if (u & t) == 0): + return False + return True + + +def main(): + assert not sys.flags.optimize, 'run without -O' + print('h v R R/v root kappa x-frontier') + for h in (3, 4, 5, 6, 7, 8): + g = zeta_graph(h) + for seed in (1, 7): + assert semantics(g, seed), 'semantic evaluation failed at seed %d' % seed + assert run_controls(g), 'scalar word/replay controls failed' # the word layer + prof, _ = compile_graph(g, []) # THEIR compiler, all asserts + root = prof['numerical_complex_root'] + assert root is not None + a = Q(int(root * 10**12), 10**12) + b = a * (1 + Q(1, 1000)) + res = assembly(a, b, dummy_bridge(), Q(1, 10**12), beta=Q(1, 10**6)) # THEIR assembly + kap = res['minimum_margin'] + assert float(kap) / float(FRONTIER) > 9.0, 'frontier multiple' + print('%d %4d %5d %6.2f %10.4e %10.4e %8.1fx' % ( + h, prof['v'], prof['R'], prof['R'] / prof['v'], root, float(kap), float(kap) / float(FRONTIER))) + print('PASS: semantic evaluation, scalar word/replay with tamper controls, in-tree compiler identities, in-tree assembly') + + +if __name__ == '__main__': + main() diff --git a/research/zeta-family/word.py b/research/zeta-family/word.py new file mode 100644 index 0000000000..69433fd861 --- /dev/null +++ b/research/zeta-family/word.py @@ -0,0 +1,112 @@ +#!/usr/bin/env python3 +"""The literal scalar word and its transparency replay. + +The zeta DAG is a pure-addition circuit (every node = sum of two earlier +registers, coefficient +1). The word follows the retained schedule pattern: + + phase 1 pre-reads: y[t] -= r[root_t] (registers hold pure dirt) + phase 2 V-gates: r[in_i] += x_i (the data enters) + phase 3 compute: r[n] += r[a]; r[n] += r[b] (DAG order) + phase 4 post-reads: y[t] += r[root_t] + phase 5 inverse: undo compute, then undo the V-gates + +Linearity makes the pre-read on pure dirt equal to the dirt flux that later +reaches each root (the root's value on dirt alone is the exact path-weighted +dirt sum), so the post-read minus that flux is exactly the family map +sum_{u: u&t=0} x_u. Phase 5 restores every register to its initial value. +Tamper controls: skipping a pre-read, a post-read, an inverse gate, or flipping +one coefficient must all be rejected. +""" + +# control vocabulary: 'skip_preread', 'skip_postread', 'skip_inverse', 'sign_flip' + +def ops_of(g): + ops = [] + for node, pair in enumerate(g['args']): + if pair is None: + continue + ops.append((node, pair[0])) + ops.append((node, pair[1])) + reads = [(r['targets'][0], r['node']) for r in g['roots']] + return ops, reads + + +def adjoint(g, root): + """The transpose sweep: every node's path multiplicity into the root. + Mirrors the retained adjoint construction.""" + n = len(g['args']) + adj = [0] * n + adj[root] = 1 + for dst, src in reversed(ops_of(g)[0]): + adj[src] += adj[dst] + return adj + + +def replay(g, ring, seed=1, tamper=None): + import random + rng = random.Random(seed) + v, n = len(g['inputs']), len(g['args']) + x = [ring(rng.randrange(1, 10**9)) for _ in range(v)] + dirty = [ring(rng.randrange(1, 10**9)) for _ in range(n)] + r = list(dirty) + y = [ring(0)] * v + ops, reads = ops_of(g) + add = lambda a, b: ring(a + b) + sub = lambda a, b: ring(a - b) + # 1. adjoint pre-reads on the raw dirt: y[t] -= sum_n adj_t[n] * r[n] + for t, root in reads: + adj = adjoint(g, root) + for node in range(n): + if not adj[node]: + continue + if tamper == 'skip_preread' and (t, node) == (0, root): + continue + y[t] = sub(y[t], ring(adj[node] * r[node])) + # 2. V-gates + for i in range(v): + r[i] = add(r[i], x[i]) + # 3. compute + for dst, src in ops: + r[dst] = add(r[dst], r[src]) + # 4. post-reads + for k, (t, node) in enumerate(reads): + if tamper == 'skip_postread' and k == 0: + continue + if tamper == 'sign_flip' and k == 0: + y[t] = sub(y[t], r[node]) + else: + y[t] = add(y[t], r[node]) + # 5. inverse + for k, (dst, src) in enumerate(reversed(ops)): + if tamper == 'skip_inverse' and k == 0: + continue + r[dst] = sub(r[dst], r[src]) + for i in reversed(range(v)): + r[i] = sub(r[i], x[i]) + scratch_ok = r == dirty + want = [sum(x[i] for i, u in enumerate(g['inputs']) if (u & g['inputs'][t]) == 0) + for t in range(v)] + map_ok = all(ring(y[t] - want[t]) == 0 for t in range(v)) + return scratch_ok, map_ok + + +def run_controls(g): + P = (1 << 61) - 1 + rings = {'mod 2^61-1': lambda z: z % P, 'integers': lambda z: z} + for name, ring in rings.items(): + for seed in (1, 7, 42): + assert replay(g, ring, seed=seed) == (True, True), (name, 'clean', seed) + for t in ('skip_preread', 'skip_postread', 'skip_inverse', 'sign_flip'): + scratch_ok, map_ok = replay(g, ring, seed=3, tamper=t) + assert not (scratch_ok and map_ok), (name, 'tamper accepted', t) + return True + + +if __name__ == '__main__': + import sys + from pathlib import Path + sys.path.insert(0, str(Path(__file__).resolve().parent)) + from zeta import zeta_graph + for h in (3, 4, 5, 6, 7, 8): + assert run_controls(zeta_graph(h)) + print('h=%d: clean replay on three seeds + four tamper controls PASS (mod 2^61-1 and integers)' % h) diff --git a/research/zeta-family/zeta.py b/research/zeta-family/zeta.py new file mode 100644 index 0000000000..5d69f4b2cf --- /dev/null +++ b/research/zeta-family/zeta.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +"""The zeta family: a finite-network construction on the Boolean lattice. + +Graph: inputs = all nonzero, non-full bitmasks of [h] (v = 2^h - 2). +DAG: the fast Yates zeta transform A_j(W) accumulators (h*2^(h-1) - (2^h-1) adds). +Roots: one side root per input t (except the all-ones target), delivering +Z_{full^t} = sum over inputs submasked into complement(t), i.e. bit-disjoint +from t. Their side-root contract (span contained in the orthogonal complement +of the targets' addresses) holds BY CONSTRUCTION: the zeta accumulates only +addresses bit-disjoint from t. +No centers: ell = 0; the compiler derives deficit = 2v. +""" + + +def zeta_graph(h): + full = (1 << h) - 1 + masks = list(range(1, full)) + v = len(masks) + idx = {u: i for i, u in enumerate(masks)} + args = [None] * v + prev = {u: idx[u] for u in masks} + for j in range(h): + cur = {} + for W in masks: + node = prev[W] + lower = W ^ (1 << j) + if (W >> j) & 1 and lower != 0 and lower in prev: + args.append([node, prev[lower]]) + node = len(args) - 1 + cur[W] = node + prev = cur + roots = [dict(node=prev[full ^ t], kind='side', targets=[idx[t]]) for t in masks if full ^ t] + src = {2: v} + if h >= 4: + src[1] = v * (h - 3) # legal w(h-1) mass; the in-tree default + # collapses at h in {5,6} (duplicate keys) + return dict(h=h, inputs=masks, args=args, roots=roots, matching_frames='coordinate', + source_data_histogram={str(r): n for r, n in src.items()})