diff --git a/.github/workflows/five-stage-banks.yml b/.github/workflows/five-stage-banks.yml new file mode 100644 index 0000000000..9080083b1a --- /dev/null +++ b/.github/workflows/five-stage-banks.yml @@ -0,0 +1,29 @@ +name: Verify five-stage banks +on: + pull_request: + paths: + - 'research/five-stage-banks/**' + - '.github/workflows/five-stage-banks.yml' + workflow_dispatch: +permissions: + contents: read +jobs: + full: + runs-on: ubuntu-24.04 + timeout-minutes: 60 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 + with: + persist-credentials: false + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 + with: + repository: CrocSwap/integer-mult-bounds + ref: af3fe331ca60c936229e060681ffccbc1c208678 + path: .dependencies/pr234 + persist-credentials: false + - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 + with: + python-version: '3.13' + - run: python -m pip install sympy==1.14.0 + - run: python -B research/five-stage-banks/verify.py --pr234-root .dependencies/pr234 --full + - run: git diff --exit-code diff --git a/research/five-stage-banks/NOTICE b/research/five-stage-banks/NOTICE new file mode 100644 index 0000000000..4c6a53ede5 --- /dev/null +++ b/research/five-stage-banks/NOTICE @@ -0,0 +1,13 @@ +Five-stage completed entrance banks. Apache-2.0. +Prepared by Rohan Arun (rohanarun) with Anthropic Claude assistance. + +Reused, with credit: +- hcg890, PR234 at af3fe331ca60c936229e060681ffccbc1c208678 (ChatGPT assistance): the five-stage bit and + complex suppliers, their fresh verifier, exact moment engine, bootstrap and outer-47 assembly code, all + consumed unchanged from the pinned checkout. Jacob Sussman: the five-stage layout and complex baseline. +- Evan McKinney, PR197 (OpenAI Codex assistance): completed-bank packing, exact gauge charts and the bank + colouring helpers; banks.py is a byte-identical extract. +- Rohan Arun, PR205: completed banks on PR200's diagonal bit word. +- eumemic, PR210: the source471 helper, joint shared gauges and source borrowing consumed by PR234. +- Chafik Boukhalfa, PR200: the bit word, frames and checkers; Avi Eisenberg, PR193: the complex supplier. +- Every contributor credited by those packages. Original notices remain in the pinned checkouts. diff --git a/research/five-stage-banks/PROOF.md b/research/five-stage-banks/PROOF.md new file mode 100644 index 0000000000..07f178d6c2 --- /dev/null +++ b/research/five-stage-banks/PROOF.md @@ -0,0 +1,52 @@ +# Completed width-120 entrance banks on the five-stage bit supplier + +## 1. Inputs + +PR234 at `af3fe331ca60c936229e060681ffccbc1c208678` freshly verifies a five-stage bit supplier on PR210's +source471 helper (m = 120, W = 23,683, deficit 4,400) and a five-stage complex supplier (saving +747454944651775/10^18). Its bit histogram contains one bundled exterior child of rank 5a for each of the 2,279 +genuine independent entrances (2,200 PR200 rank-20 gauges that are not alias recipients and 79 PR210 joint +gauges of ranks 12, 13, 18). PR210's three rank-19 source-owned gauges, and PR210's source-owned rank-2/4 +entrances, are not banked. Every other child is retained unchanged. + +## 2. Charts + +For each of the 231 distinct entrance frames σ = ker A ⊂ Q^24 use G = 9I − J. Rows 15a − (Σa)1 span the +G-orthogonal complement of σ; place them before an exact integer basis of σ to form B. The checker verifies +G-orthogonality, both identities B·C = C·B = d·I, and an independent replay of the elementary factors of B⁻¹. +In this chart the residual projector I − P_σ is diag(I_{24−a}, 0). Every chart integer is below 2^80, so the +charts stay invertible at every retained prime q > 2^80. They are address charts and change no scalar word. + +## 3. Banks and scheduling + +Take twelve data copies of the five-stage construction. Each physical chain then has 60 residual occurrences, +one per (stage j, copy b). Entrance chains have residual rank 24 − a ∈ {4, 12, 11, 6}; the other 14,364 +physical chains have residual rank 24. The occurrences are partitioned into 177,179 banks of width exactly +120 (patterns in README). The chain/bank multigraph has degree 60 at every chain and at most 30 at every bank, +so it has a proper 60-edge-colouring; the checker verifies it at both ends and rejects a duplicated colour. +Colour j + 5b denotes stage j of copy b: every phase-major stage invocation receives every physical chain +exactly once and uses no bank twice, and each data copy keeps PR234's stage order. + +## 4. Bank endpoint + +After the charts, the residual projectors of a bank occupy disjoint coordinate intervals and sum to I_120. +For disjoint projectors D_P D_Q = D_(P+Q) = D_Q D_P, so the bank's weighted residual involutions commute and +multiply to the full weighted endpoint on arbitrary bank contents (PR197 §4, unchanged). The bank endpoint +therefore supplies exactly the correction each bundled rank-5a exterior child supplied, and those 2,279 +children are removed. This is the same mechanism as PR197/PR205 at width 72; only the number of stage windows +(five) and the bank width (120 = 5·24) change. + +## 5. Profile and arithmetic + +Over twelve copies: W = 12·(4·1760) + 177,179 = 261,659, rank mass 12·(2,837,560 − 226,200), deficit +52,800 = 12·4,400. PR234's moment engine certifies the paid coarse saving 709084856078931/10^18 with the +inherited 10^-16·32m bad-class envelope; the next grid point fails, and banking strictly improves the paid +moment at PR234's own coarse saving. Three finite ordinary levels from PR234's retained 384599/10^10 and PR234's +unchanged outer-47 assembly (η = 10^-12, β = 10^-9, same finite bridge) give κ = 177145602695277/250000000000000000. +The extra routing calls are bounded by 12,227,329,044 < 2^40 and absorbed by the inherited ordinary toll exactly +as in PR197. + +## 6. Limits + +Conditional on every interface PR234 retains. No unconditional all-size theorem, Lean build or measured multiplier +performance is claimed. diff --git a/research/five-stage-banks/README.md b/research/five-stage-banks/README.md new file mode 100644 index 0000000000..126b72e086 --- /dev/null +++ b/research/five-stage-banks/README.md @@ -0,0 +1,52 @@ +# Completed entrance banks on the five-stage bit supplier + +Under the retained multiplication interfaces, this finite witness certifies + +$$ +T(n)=O\left(n(\log n)^{1-\kappa}\right),\qquad +\kappa=\frac{177145602695277}{250000000000000000}=0.000708582410781108 . +$$ + +That is **+0.694%** over PR234 (`703701743496697/10^18`, the five-stage construction) and +1.99% over PR230. + +| Construction | Bit coarse saving | Conditional κ | +|---|---:|---:| +| PR234: five-stage suppliers, paid rank-5a exteriors | 7.04197e-4 | 0.000703701743496697 | +| **This: same suppliers, entrance residuals in width-120 banks** | **7.09085e-4** | **0.000708582410781108** | + +The bit side still binds. PR234's five-stage complex supplier (`747454944651775/10^18`) has about 5% slack. + +## What is new + +PR234 completes each of its 2,279 genuine independent auxiliary entrances with one bundled exterior child of rank 5a: 2,200 of rank 100 (a = 20), 18 of rank 60, 48 of rank 65 and 13 of rank 90. Its own ledger notes these entrances are "not banked". + +PR197 and PR205 showed, for the three-stage cover, that such an exterior correction can be replaced by completed banks: route every residual `I − P_σ` of an entrance chart into a full-width bank whose disjoint residual involutions multiply to the full weighted endpoint. This package applies that construction to the five-stage cover, at bank width m = 120. + +- **Charts.** 231 distinct entrance frames (220 PR200 rank-20 frames and 11 PR210 joint frames of ranks 12, 13 and 18), each with the G-orthogonal complement `15a − (Σa)1` for `G = 9I − J` followed by an exact kernel basis. Each chart is inverted fraction-free and its elementary factors replayed; at most 221 factors, numerators ≤ 40, denominators ≤ 36. +- **Banks.** Twelve data copies, so every physical chain has 60 residual occurrences (5 stages × 12 copies). Residual ranks are 4, 12, 11, 6 (entrances) and 24 (the 14,364 other physical chains). They fill exactly 177,179 banks of width 120: + - 360 banks of 8×11 + 8×4, + - 108 banks of 10×12, + - 39 banks of 20×6, + - 4,304 banks of 30×4, + - 172,368 banks of 5×24. +- **Colouring.** The chain/bank multigraph (998,580 incidences, degree 60 at every chain, at most 30 at a bank) gets a proper 60-edge-colouring, checked at both ends; colour j + 5b is stage j of copy b. A duplicated colour is rejected. +- **Profile.** Remove exactly the 2,279 exterior children; keep every other child. Over 12 copies, W = 12·4v + 177,179 = 261,659 at m = 120, with deficit 52,800 = 12·4,400 (unchanged per copy) and largest child 50. +- **Arithmetic.** PR234's own interval moment engine (with the inherited 10⁻¹⁶ bad-class envelope) certifies the coarse saving `709084856078931/10^18`; the next 10⁻¹⁸ point fails. Three finite ordinary levels from PR234's retained ordinary saving, and PR234's unchanged outer-47 assembly with its η = 10⁻¹², β = 10⁻⁹ and bridge, give κ. All 47 strict inequalities and 7 margins pass; the adjacent grid point is rejected. +- **Routing.** The conservative additional selector-call bound is 18·((W−1) + R·120·(221+119)) = 12,227,329,044 < 2⁴⁰, absorbed by the inherited ordinary toll as in PR197. + +## Verify + +```sh +git fetch https://github.com/CrocSwap/integer-mult-bounds pull/234/head +git worktree add --detach ../pr234 af3fe331ca60c936229e060681ffccbc1c208678 +python3 -m pip install sympy==1.14.0 +python3 -B research/five-stage-banks/verify.py --pr234-root ../pr234 --full +``` + +`--full` first runs PR234's own fresh verifier (all six mandatory stages, a few minutes) and requires its freshly computed mathematics to match; then it rebuilds the 231 charts, all banks and the colouring, and recomputes the moments and assembly, comparing with `certificate.json`. Without `--full`, PR234's pinned expected mathematics is used. + +## Scope + +A conditional finite construction and composition, not an unconditional multiplication theorem. Every PR234 dependency remains: the five-stage physical/scalar/prime checks, complex scalar theorems, weighted q-local compiler, complete-stream movement, copied-centre and restored-row interfaces, ordinary leaves, prime supply, precision/recovery and the all-size reduction. The bank endpoint argument is PR197's (disjoint residual involutions commute and multiply to the full endpoint, on arbitrary dirty contents), applied at width 120 with one colour per (stage, copy). Source-owned PR210 gauges are not banked, as in PR234. + +Prepared by Rohan Arun with Anthropic Claude assistance. Apache-2.0. diff --git a/research/five-stage-banks/SOURCE.json b/research/five-stage-banks/SOURCE.json new file mode 100644 index 0000000000..5c2880d6b1 --- /dev/null +++ b/research/five-stage-banks/SOURCE.json @@ -0,0 +1,16 @@ +{ + "pr234_commit": "af3fe331ca60c936229e060681ffccbc1c208678", + "pr234_repository": "CrocSwap/integer-mult-bounds (refs/pull/234/head)", + "pr234_files": { + "research/five-stage-source-bound-v8/expected-math.json": "44426de51a4d096e1987f43faf8e754d1b29abc8b8b5c4c9d81b15db4f73b0bc", + "research/five-stage-source-bound-v8/code/five_stage_bit_cost_20261009.py": "9d465c716c6b910ff66e1453e3bf0057608a787b5f5361be93781f42cb3370f7", + "research/five-stage-source-bound-v8/code/paired_cube_assembly.py": "c1ebb52163a7219504c728a5c2b098bf1e50bf20148a2879c5a74c3cadc7c03c", + "research/five-stage-source-bound-v8/verify.py": "827c559d949df4ec462691a42067355ebd6d6baff55c48410d5f36247f0ca9a9", + "research/five-stage-source-bound-v8/MANIFEST.json": "fc691fdf74a7b06c9290d6e91e6d63dc1c7e20c6556519dcf9b7f53fa02350de", + "research/five-stage-source-bound-v8/loader/source_inputs/base_bit/research/paired-cube-diagonal-bit-168/selected/bit/word_p12.json.gz": "1cb7e8ed45cf542e304fddc4b12247dbb248a0dc84dedbd832f336b2d172793a", + "research/five-stage-source-bound-v8/loader/source_inputs/base_bit/research/paired-cube-diagonal-bit-168/selected/bit/frames_p12.json.gz": "ad8e2705652b68476713d7647a8592505e222f2b3ccd1728dedce153aac6d462", + "research/five-stage-source-bound-v8/loader/source_inputs/pr210/research/coordinated-crossover-pr200/joint/pairs.json": "da54b863601b5cbc21d27fc5cd623bf805f974a3e47b81ed29a85aa5bad0fa05", + "research/five-stage-source-bound-v8/loader/source_inputs/pr210/research/coordinated-crossover-pr200/joint/joint-selection.json": "13f6420d6c341c22235c9d136d5f7c32896974bb165a893fecd7e583b790d133", + "research/five-stage-source-bound-v8/loader/source_inputs/pr210/research/coordinated-crossover-pr200/gaugeb/selection.json": "b0f9f0e67f94a3c21069f393ea27f9483d315bff2f53f77270163065a6345d1c" + } +} diff --git a/research/five-stage-banks/arithmetic5.py b/research/five-stage-banks/arithmetic5.py new file mode 100644 index 0000000000..cfdbf5d1a5 --- /dev/null +++ b/research/five-stage-banks/arithmetic5.py @@ -0,0 +1,92 @@ +"""Exact paid moments, three finite ordinary levels and PR234's outer-47 assembly for the banked five-stage bit +supplier. Uses PR234's own pinned moment, bootstrap and assembly code unchanged. +Prepared by Rohan Arun with Anthropic Claude assistance. Apache-2.0. +""" +from fractions import Fraction as Q +from pathlib import Path +import importlib.util + +PKG = 'research/five-stage-source-bound-v8' +PR234_KAPPA = Q(703701743496697, 10 ** 18) +PR230_KAPPA = Q(694787513005285, 10 ** 18) + + +def load(name, path): + s = importlib.util.spec_from_file_location(name, path); m = importlib.util.module_from_spec(s); s.loader.exec_module(m) + return m + + +def build(P234, pr234_math, physical): + """pr234_math: PR234's freshly computed mathematics (bit_profile, complex_profile, assembly parameters).""" + math = load('pr234_moments', P234 / PKG / 'code/five_stage_bit_cost_20261009.py') + outer = load('pr234_outer47', P234 / PKG / 'code/paired_cube_assembly.py') + bp = pr234_math['bit_profile']; cp = pr234_math['complex_profile'] + assert (bp['m'], bp['W'], bp['deficit'], bp['maxchild']) == (120, 23683, 4400, 100) + H = {int(k): n for k, n in bp['histogram'].items()} + removed = {int(k): n for k, n in physical['removed_exteriors'].items()} + for r, n in removed.items(): + assert H[r] >= n; H[r] -= n + c = physical['copies']; m = 120 + H = {r: c * n for r, n in H.items() if n} + W = physical['W']; mass = sum(r * n for r, n in H.items()); deficit = m * W - mass + assert deficit == c * bp['deficit'], 'banking preserves the telescoping deficit' + assert max(H) < m // 2 + # Coarse paid saving on the 10^-18 grid, PR234's interval engine with the inherited bad-class envelope. + root = Q(math.certify(H, m, W, True)['lower']); den = 10 ** 18 + lo = Q(int(root * den), den) + while math.moment(H, m, W, lo, True)[1] >= 1: lo -= Q(1, den) + hi = lo + Q(1, den) + while math.moment(H, m, W, hi, True)[0] <= 1: lo = hi; hi = lo + Q(1, den) + assert math.moment(H, m, W, lo, True)[1] < 1 < math.moment(H, m, W, hi, True)[0] + coarse = lo + unbanked = {int(k): n for k, n in bp['histogram'].items()} + old_coarse = Q(pr234_math['contract']['assembly']['bit_coarse']) if 'contract' in pr234_math else None + if old_coarse is not None: + assert math.moment(H, m, W, old_coarse, True)[1] < math.moment(unbanked, m, bp['W'], old_coarse, True)[0], \ + 'banking strictly improves the paid moment at PR234 coarse saving' + chain = [Q(384599, 10 ** 10)] + for _ in range(3): + a = (1 - coarse) * coarse + coarse * chain[-1] + assert chain[-1] < a < coarse < 1 - a + chain.append(a) + bit = chain[-1] + b = Q(pr234_math['assembly']['parameters']['a_complex']) + cm = math.moment({int(k): n for k, n in cp['histogram'].items()}, 110, cp['W'], b, False) + assert cm[1] < 1 + eta = Q(1, 10 ** 12); beta = Q(1, 10 ** 9) + assert bit < (1 - beta) * b + bridge = dict(proof='PROOF.md', representation='Exact powers with source-bound finite overcharges', + semantic=dict(G=dict(base=2, exponent=30000), E=dict(base=2, exponent=100000), + B_upper=dict(base=2, exponent=100001), C0=dict(base=2, exponent=210000), C1=1, + strict_literal_gap=1, induction_gap_lower=1), + rows=dict(coefficient=20161, degree=10 ** 6, suffix_slope=4 * 10 ** 6, + degree_gap=Q(10 ** 6) - Q(51 * 20161, 25))) + q = bit * (1 - 2 * eta); minimum = (1 - eta) * q / (1 + q); ticks = minimum * den + kappa = Q(ticks.numerator // ticks.denominator, den) + if kappa == minimum: kappa -= Q(1, den) + result = outer.assembly(bit, b, bridge, kappa, eta=eta, beta=beta) + assert len(result['strict_constraints']) == 47 and len(result['margins']) == 7 + assert all(x > 0 for x in result['strict_constraints'].values()) + try: outer.assembly(bit, b, bridge, kappa + Q(1, den), eta=eta, beta=beta) + except AssertionError: pass + else: raise AssertionError('adjacent kappa grid point admitted') + assert kappa > PR234_KAPPA > PR230_KAPPA + controls = [] + try: profile_check({**H, 1: H[1] + 1}, m, W, c * bp['deficit']) + except AssertionError: controls.append('changed rank mass rejected') + else: raise AssertionError('corrupt rank mass accepted') + try: math.moment({**H, m: 1}, m, W, coarse, True) + except AssertionError: controls.append('full-width child rejected') + else: raise AssertionError('full-width child accepted') + return dict(scope='Conditional finite supplier composition; PR234 physical, scalar, prime and analytic dependencies inherited', + bit_profile=dict(m=m, W=W, copies=c, histogram={str(r): n for r, n in sorted(H.items())}, + calls=sum(H.values()), rank_mass=mass, deficit=deficit, maxchild=max(H)), + bit_coarse_saving=coarse, pr234_bit_coarse_saving=old_coarse, ordinary_chain=chain, bit=bit, + complex_saving=b, complex_moment_interval=cm, assembly=result, kappa=kappa, + comparison_pr234_kappa=PR234_KAPPA, relative_gain_over_pr234=kappa / PR234_KAPPA - 1, + comparison_pr230_kappa=PR230_KAPPA, controls=controls) + + +def profile_check(H, m, W, deficit): + assert m * W - sum(r * n for r, n in H.items()) == deficit + return True diff --git a/research/five-stage-banks/banks.py b/research/five-stage-banks/banks.py new file mode 100644 index 0000000000..6505ebd37e --- /dev/null +++ b/research/five-stage-banks/banks.py @@ -0,0 +1,104 @@ +"""PR197 chart inversion and bank colouring helpers (byte-identical extract of packing.py up to build()). + +Apache-2.0. Prepared with substantial OpenAI Codex assistance. +""" +from array import array +from collections import Counter +from fractions import Fraction as Q +from math import gcd +from pathlib import Path +import hashlib,json,sys + +def inverse_integer(a): + """Fraction-free Gauss-Jordan; return integral C,d with a C = d I.""" + n=len(a) + m=[list(row)+[int(i==j) for j in range(n)] for i,row in enumerate(a)] + previous=1 + factors=[] + for k in range(n): + pivot=next((i for i in range(k,n) if m[i][k]),None) + if pivot is None: + raise ValueError('Singular basis') + m[k],m[pivot]=m[pivot],m[k] + if pivot != k:factors.append(('swap',k,pivot,1,1)) + p=m[k][k] + if p != previous: + ratio=Q(previous,p) + factors.append(('scale',k,k,ratio.numerator,ratio.denominator)) + for i in range(n): + if i==k:continue + u=m[i][k] + if u: + ratio=Q(-u,previous) + factors.append(('add',i,k,ratio.numerator,ratio.denominator)) + for j in range(2*n): + if j==k:continue + value=p*m[i][j]-u*m[k][j] + assert value % previous==0 + m[i][j]=value//previous + m[i][k]=0 + previous=p + d=m[0][0] + assert d and all(m[i][j]==d*int(i==j) for i in range(n) for j in range(n)) + c=[row[n:] for row in m] + common=abs(d) + for row in c: + for x in row:common=gcd(common,x) + d//=common + c=[[x//common for x in row] for row in c] + if d<0:d=-d;c=[[-x for x in row] for row in c] + return c,d,factors + +def color_incidence(left,right,nleft,nright,colors): + """Incremental two-color swaps; parallel edges are identified separately.""" + assert len(left)==len(right) + full=(1< multiplicity); every pattern has at most 60 items. +PATTERNS = { + 'r11x8+r4x8': {11: 8, 4: 8}, + 'r12x10': {12: 10}, + 'r6x20': {6: 20}, + 'r4x30': {4: 30}, + 'r24x5': {24: 5}, +} + + +def banks(entries, physical_roles): + """Allocate every residual occurrence (5 stages x 12 copies per physical chain) to a full bank and colour.""" + residual = {role: H - rank for role, rank, _ in entries} + gauge_roles = sorted(residual) + n_full = physical_roles - len(gauge_roles) + assert n_full == 14364 + # chains: gauge chains first, then anonymous full-residual chains (one per remaining physical role) + chain_res = [residual[r] for r in gauge_roles] + [H] * n_full + by_res = defaultdict(list) + for c, res in enumerate(chain_res): by_res[res].append(c) + occ = {res: [c for c in cs for _ in range(COLORS)] for res, cs in by_res.items()} + counts = {res: len(v) for res, v in occ.items()} + plan = Counter() + plan['r11x8+r4x8'] = counts[11] // 8 + plan['r12x10'] = counts[12] // 10 + plan['r6x20'] = counts[6] // 20 + plan['r4x30'] = (counts[4] - 8 * plan['r11x8+r4x8']) // 30 + plan['r24x5'] = counts[24] // 5 + use = Counter() + for name, n in plan.items(): + for res, k in PATTERNS[name].items(): use[res] += n * k + assert dict(use) == counts, (use, counts) + assert all(sum(r * k for r, k in p.items()) == M and sum(p.values()) <= COLORS for p in PATTERNS.values()) + left = []; right = []; offsets = []; cursor = Counter(); bank = 0 + for name in ('r11x8+r4x8', 'r12x10', 'r6x20', 'r4x30', 'r24x5'): + for _ in range(plan[name]): + off = 0 + for res, k in sorted(PATTERNS[name].items(), reverse=True): + for _ in range(k): + c = occ[res][cursor[res]]; cursor[res] += 1 + left.append(c); right.append(bank); offsets.append(off); off += res + assert off == M + bank += 1 + assert all(cursor[r] == counts[r] for r in counts) + nchains = len(chain_res) + labels, stats = color_incidence(left, right, nchains, bank, COLORS) + check_coloring(left, right, labels, nchains, bank, COLORS, full_left=True) + # A deliberately duplicated colour at one chain must be rejected. + first = {}; rejected = False + for e, r in enumerate(left): + if r not in first: first[r] = e; continue + old = labels[e]; labels[e] = labels[first[r]] + try: check_coloring(left, right, labels, nchains, bank, COLORS, full_left=True) + except AssertionError: rejected = True + finally: labels[e] = old + break + assert rejected + inc = hashlib.sha256() + for r, b, c, o in zip(left, right, labels, offsets): inc.update(f'{r},{b},{c},{o}\n'.encode()) + total_width = sum(r * n for r, n in counts.items()) + assert total_width == M * bank + return dict(copies=COPIES, colors=COLORS, banks=bank, plan=dict(plan), residual_occurrences=counts, + incidences=len(left), incidence_sha256=inc.hexdigest(), color_stats=stats, + conflicting_assignment_rejected=rejected, gauge_chains=len(gauge_roles), full_chains=n_full) + + +def build(P234, physical_roles, v=1760): + entries = entrance_inventory(P234) + chart = charts(entries) + bank = banks(entries, physical_roles) + W = COPIES * (STAGES - 1) * v + bank['banks'] + K = 18 * ((W - 1) + physical_roles * M * (chart['max_chart_factors'] + M - 1)) + return dict(status='PASS exact charts and width-120 bank incidence', entrance_ranks={20: 2200, 12: 18, 13: 48, 18: 13}, + removed_exteriors={100: 2200, 60: 18, 65: 48, 90: 13}, W=W, m=M, **chart, **bank, + conservative_selector_calls=K) + + +if __name__ == '__main__': + import time; t = time.time() + r = build(Path(sys.argv[1]), int(sys.argv[2])) + print(json.dumps(r, indent=1), time.time() - t) diff --git a/research/five-stage-banks/verify.py b/research/five-stage-banks/verify.py new file mode 100644 index 0000000000..231b340565 --- /dev/null +++ b/research/five-stage-banks/verify.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python3 +"""Verify completed width-120 entrance banks on PR234's five-stage bit supplier. + +Usage (repository root, assertions enabled): + python3 -B research/five-stage-banks/verify.py --pr234-root [--full] [--write] + +--full first runs PR234's own fresh verifier (all six mandatory stages) into a new temporary directory and +uses its freshly computed mathematics; without it, PR234's pinned expected-math.json is used. +Prepared by Rohan Arun with Anthropic Claude assistance. Apache-2.0. +""" +from fractions import Fraction as Q +from pathlib import Path +import argparse, hashlib, json, subprocess, sys, tempfile +if sys.flags.optimize: raise SystemExit('Assertions required') +sys.set_int_max_str_digits(0) +HERE = Path(__file__).resolve().parent +sys.path.insert(0, str(HERE)) +import packing5, arithmetic5 + +PKG = 'research/five-stage-source-bound-v8' + + +def serial(x): + if isinstance(x, Q): return str(x) + if isinstance(x, dict): return {str(k): serial(v) for k, v in x.items()} + if isinstance(x, (list, tuple)): return [serial(v) for v in x] + return x + + +def main(): + ap = argparse.ArgumentParser(description=__doc__) + ap.add_argument('--pr234-root', type=Path, required=True) + ap.add_argument('--full', action='store_true') + ap.add_argument('--write', action='store_true') + a = ap.parse_args(); assert not a.write or a.full + P = a.pr234_root.resolve(); source = json.loads((HERE / 'SOURCE.json').read_text()) + head = subprocess.check_output(['git', '-C', str(P), 'rev-parse', 'HEAD'], text=True).strip() + assert head == source['pr234_commit'], 'wrong PR234 checkout' + for name, digest in source['pr234_files'].items(): + assert hashlib.sha256((P / name).read_bytes()).hexdigest() == digest, ('changed PR234 input', name) + expected = json.loads((P / PKG / 'expected-math.json').read_text())['mathematics'] + if a.full: + with tempfile.TemporaryDirectory(prefix='pr234-fresh-') as tmp: + out = Path(tmp) / 'run' + subprocess.run([sys.executable, '-B', 'verify.py', '--output', str(out)], cwd=P / PKG, check=True) + fresh = json.loads((out / 'certificate.json').read_text()) + for key in ('bit_profile', 'complex_profile', 'kappa', 'assembly'): + assert fresh[key] == expected[key], ('PR234 fresh mathematics differ', key) + pr234 = fresh + else: + pr234 = expected + R = pr234['bit_profile']['W'] - 4 * 1760 + physical = packing5.build(P, R) + result = arithmetic5.build(P, pr234, physical) + record = serial(dict(pr234_commit=head, pr234_fresh_run=a.full, physical=physical, arithmetic=result)) + path = HERE / 'certificate.json' + if a.write: path.write_text(json.dumps(record, indent=2, sort_keys=True) + '\n') + else: + old = json.loads(path.read_text()); old['pr234_fresh_run'] = a.full + assert record == old, 'certificate differs' + print('PASS 231 exact charts, 177179 width-120 banks with a proper 60-colouring, paid moments, 3 leaf levels, 47 constraints') + print('conditional kappa = ' + record['arithmetic']['kappa']) + + +if __name__ == '__main__': main()