From 84776893bb456bbdae9eeb4586987fad6ecef879 Mon Sep 17 00:00:00 2001 From: wakesync <60821182+wakesync@users.noreply.github.com> Date: Thu, 5 Nov 2026 14:55:33 +0000 Subject: [PATCH 1/6] experiments: preregister Qwen DeltaNet M2 design (Part of #32) --- .../PREREG.md | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 experiments/20261105-32-qwen-deltanet-m2-design/PREREG.md diff --git a/experiments/20261105-32-qwen-deltanet-m2-design/PREREG.md b/experiments/20261105-32-qwen-deltanet-m2-design/PREREG.md new file mode 100644 index 00000000..5046c22d --- /dev/null +++ b/experiments/20261105-32-qwen-deltanet-m2-design/PREREG.md @@ -0,0 +1,51 @@ +# PREREG: 20261105-32-qwen-deltanet-m2-design +Issue: #32 (tracking; operators #35, state-commit #34). Lane: Qwen DeltaNet M2 DESIGN and preregistration (CPU, synthetic, small prototypes only). Lane start 2026-11-05T14:33Z. Base commit: origin/main b6186e6c (includes #63 M1b lookup and #62 MoE top-k). This file is the first commit on the branch. No code for this experiment existed when it was written. + +Inputs read before registration: the prior M2 brief (two-limb delta, lookup range test, "one full layer at exact shape"), #52 RESULTS (M1b two-limb KILLED), #63 PREREG/RESULTS and its independent review (APPROVE; R9 shows `admissible()` is load-bearing and sits outside `lookup_verify`; honest completeness needs 16 x bound <= D = 2^22), #62 review, research/ports/qwen3.8-27b/FEASIBILITY.md (M2 definition, config facts), research/operators/qwen_deltanet{,_lookup}/ (lowering, builder, sizing), blocks/iop/rng.h and lkp.h (shapes, tables, key claims). + +## What this lane decides + +This is a design lane. It does not build M2. It produces DESIGN.md (the M2 statement, lowering, sizing and verifier entry point) and four small CPU prototypes that test the design's load-bearing assumptions. The verdict is a GO / NO-GO for an M2 build lane, under the rule in "Acceptance threshold". + +Registered design decisions (fixed now, before any prototype runs): +- **D1 scope.** "One full layer at exact shape" (FEASIBILITY.md M2) does not fit the research harness. The layer's matrices (in_proj_qkv 5,120 x 10,240, in_proj_z 5,120 x 6,144, in_proj_a/b 5,120 x 48, out_proj 6,144 x 5,120, SwiGLU 3 x 5,120 x 17,408) are about 3.8e8 MACs per token. As affine terms with public weights that is about 70x the whole M1b statement per token, and they are dense-family operators FlashProve already proves (docs/model.md sections 5 and 7). M2 is therefore **one V head of one Qwen DeltaNet layer, end to end from the in_proj outputs to the gated-norm output, at T = 16**, with the 48-head layer cost extrapolated from it (labelled as an extrapolation). The matmuls, pre-norm RMSNorm, residual and MLP stay unproved in M2. +- **D2 statement boundary.** Public: the head's in_proj outputs per token (pre-conv q, k, v channels of its K head and V head, 384 values; a_raw, b_raw scalars; z, 128 values), the conv kernels, A_log, dt_bias, gated-norm gain w, S0, the gated-norm output y (128 per token), and S_T. Private: every state S_t, M, delta, the head output o (private in M2, public in M1/M1b), and the gated-norm intermediates. +- **D3 which of gate / conv / norm M2 proves.** At this boundary, conv1d, SiLU, the q/k L2 norm, the q scale, the decay gate exp(-A softplus(a_raw + dt_bias)) and beta = sigmoid(b_raw) are functions of public integers only. The M2 verifier **recomputes them** from the public integers under the integer spec (DESIGN.md section 3), in O(D T) work, and uses the results only as relation targets and coefficients. They are not proved as in-relation gadgets in M2: proving them would add cost and no soundness. The **gated RMSNorm of o is proved** in-relation (it reads the private o). The in-relation gadget form of conv / SiLU / L2 norm / gates is needed only when the in_proj outputs become committed (M3); DESIGN.md gives its estimated cost, and an optional M2 arm (M2-R) may build it as a rehearsal. +- **D4 range sizing.** Every RNG range cell has an honest contract |x| < 2^18 and sits in a 16-column RNG matrix with window 2D = 2^23 (b0 = 12, b1 = 11), so an honest combination is at most 16 x 2^18 = 2^22 = D and RNG proves |x| < 2^23. Any class whose honest values can reach 2^18 is limb-split at 2^18 (as delta in M1b). Non-negative quantities that need an exact interval (floor remainders, isqrt residuals, divisor-minus-remainder) are not RNG cells; they are exact. +- **D5 remainders.** Two arms. Arm A: exact bit decomposition (as M1b). Arm B: lookup-exact remainders. Each F = 12 bit remainder is one product cell used as a direct LKP key into the 2^12 range table (LkpTablePart with empty rows); a 2F = 24 bit remainder is two such limbs plus one recombination relation. The keys are read from product row a through a LaidBlock region, like RNG's cells. +- **D6 verifier entry point.** The M2 verifier takes the shape (T, D, F, head constants) and the public inputs as canonical 64-bit field words. It decodes each word itself (word < p; signed value within its class bound; a and beta derived, not supplied), derives every relation target and coefficient from the decoded integers, sizes the run, and then runs the proof verifier. There is no separate `admissible()` that a caller can skip, and there is no entry point that accepts caller-built relations. + +## Hypothesis +The registered M2 design (one head, T = 16, F = 12, two-limb delta, private o, proved gated RMSNorm, verifier-recomputed gates / conv / SiLU / L2 norm, verifier-side admission) is buildable on the merged M1b machinery within the research harness. Concretely: (H1) a counting model of the lowering reproduces M1 and M1b exactly; (H2) lookup-exact remainders cut product columns and relations per state cell per token by at least half; (H3) the M2 head statement at T = 16 fits a product table of at most 2^22 under arm B; (H4) every proposed range-cell class and exact limb stays inside its contract on the realistic suite; (H5) a verifier entry point that admits its own integer public inputs rejects every hostile public encoding on its own, including the #63 review's R9 statement that the current caller-side path accepts. + +## Metric +- **H1.** Counting model `count_model.py`: for a lowering spec (D, T, F, arm, gadgets) it returns product columns used, aligned layout length, table log-length, linear relations, relation terms, range cells, RNG cell rows and digit entries, LKP keys. Compared to the measured M1 (#47: 2,338,816 product columns, 4,761,088 relations, 11,430,400 terms, log-length 22) and M1b (#63: 1,141,248 columns, 2,231,808 relations, 5,174,272 terms, 67,072 range cells, log-length 21) from their evidence files. +- **H2.** From the model at the M1b shape (D = 128, T = 4): product columns per state cell per token, relations and terms, arm A vs arm B; ratio (B / A). +- **H3.** From the model: the M2 head statement at T = 16, arm A and arm B: product columns, layout length, log-length, relations, terms, range cells, LKP keys. Also the largest T that fits log-length 22 per arm, and the 48-head layer extrapolation per token. +- **H4.** `head_reference.py`: an integer reference of the whole head (conv1d kernel 4 with causal zero padding, SiLU via sigmoid table, L2 norm with eps, q scale, decay and beta tables, staged-floor recurrence, gated RMSNorm) at D = 128 with synthetic init (conv kernels U(-0.5, 0.5); A = U(1, 16); dt as M1; w = 1; pre-conv inputs R1..R4 as M1, z ~ N(0, 1), a_raw, b_raw ~ N(0, 1)). Per run: the maximum |x| of every RNG cell class (S', M, delta_lo, delta_hi, o, n) and the maximum of every exact limb class (remainders, sigma_o, isqrt residuals, sigma - 1 - r), and the worst float64 divergence of S and y. Suite: R1..R4 x seeds 1..5 x 1,024 steps = 20 runs. +- **H5.** Prototype `research/operators/qwen_deltanet_m2/admit_proto.cpp` on the M1b statement (D = 128, T = 4, merged lookup.h unchanged): entry `verify_from_words(words, proof)` vs the current caller path (`admissible()` in main, then `lookup_verify` with caller-built relations). Per honest seed: accept, decode+derive seconds, verify seconds. Per hostile statement: accept / reject and which stage rejected (decode, or the proof verifier). + +## Acceptance threshold +GO for an M2 build lane only if all hold: +- H1: the model's columns used, relations, terms and range cells equal the measured M1 and M1b values exactly (4 + 4 numbers; log-length too). +- H2: at the M1b shape, arm B / arm A <= 0.5 for product columns per state cell per token AND for relations. +- H3: arm B, one head, T = 16: table log-length <= 22. (Arm A at T = 16 is descriptive; its largest fitting T is reported.) +- H4: in 20/20 runs every RNG cell class has max |x| < 2^18 after the registered limb splits (D4: delta split at 2^18; any other class that reaches 2^18 FAILS H4 unless DESIGN.md registered its split before this run), and every exact limb is in [0, 2^12). +- H5: (a) 10/10 honest seeds accepted by `verify_from_words`; (b) 100% of hostile public encodings rejected by `verify_from_words` alone, at least: for each of the 8 public classes (S0, a, beta, k, v, q, o, S_T) one element set to (p + 1) / 2 and one to its class bound (bound + 1 for one-sided classes), one non-canonical word (honest value + p), one header mismatch, and R9 (o_T[j] replaced by o_T[j] - 2^-F mod p with a fresh hostile proof whose remainder absorbs the shift); (c) control: the same R9 statement and proof are ACCEPTED by the current caller path with `admissible()` skipped (shows the hole is real and the new entry closes it). +Anything else is NO-GO, with the failing item named. A NO-GO that names a smaller buildable scope (e.g. T = 8 arm A) is reported as such. + +## Kill criterion +Hard stop at 2026-11-05T18:33Z (4 h). If H5's prototype has no honest accept by 17:45Z, H5 is reported as not run and the verdict is NO-GO (H5 is load-bearing per the review). If the model cannot reproduce M1b exactly by 16:30Z, H1 fails and H2/H3 are reported as unvalidated estimates. Peak RSS over 12 GB or free disk under 3 GB: stop and report. + +## Runs +- H1 to H3: deterministic counts, n = 1 each (no variance by construction). +- H4: 20 runs (R1..R4 x seeds 1..5), 1,024 steps each, integer reference, CPU. +- H5: honest seeds 1..10 (M1 fixture recipe), one process each, sequential, pinned to 6 cores (taskset -c 0-5); n = 10, median, SD, min/max for decode+derive and verify seconds. Hostile statements: seed 1, one process. Exclusions only for host faults, rerun and reported. Shared loaded VM, timings indicative only. +- Research security settings as #63 (target 10 bits, N = 10 combinations, toy coins). + +## Prior art +- This repository: M1 #47, M1b two-limb range study #52, M1b RNG/LKP composition #63 and its review (R9, completeness bound), MoE top-k #62 (statement-side admission also flagged), FEASIBILITY.md (#28), blocks/iop/rng.h (Lyubashevsky, Nguyen, Seiler, ePrint 2020/1448 sec. 2.7; Gentry, Halevi, Lyubashevsky, ePrint 2021/1397 sec. 3.4), blocks/iop/lkp.h (Papini, Haboeck, ePrint 2023/1284), docs/model.md (isqrt norm, Ts and Sig tables, range conditions). +- Literature: Gated DeltaNet (Yang, Kautz, Hatamizadeh, ICLR 2025); Qwen3.5/3.8 hybrid layout (Transformers `modeling_qwen3_5.py`, cited by line in FEASIBILITY.md); zkLLM (CCS 2024, tlookup for nonlinearities), Jolt / Lasso (lookup-only VMs), EZKL (range and lookup tables in Halo2). Public-input validation as a verifier duty is standard (e.g. Groth16 and PLONK verifiers reject non-canonical public inputs; the "frozen heart" class of Fiat-Shamir bugs, Trail of Bits 2022). What is new here: an M2 statement that moves all public-only nonlinearities to verifier recomputation and keeps the in-relation work on private state, and a measured remainder-cost model for the RNG/LKP composition. + +## Amendments +(empty at registration) From a8cdf660f38b294bde484cbb98564ba492dfcf88 Mon Sep 17 00:00:00 2001 From: wakesync <60821182+wakesync@users.noreply.github.com> Date: Thu, 5 Nov 2026 15:24:16 +0000 Subject: [PATCH 2/6] research: M2 design prototypes: verifier-side admission, counting model, head range reference (Part of #32) --- .../operators/qwen_deltanet_m2/.gitignore | 1 + .../qwen_deltanet_m2/admit_proto.cpp | 198 +++++++++++++++ .../operators/qwen_deltanet_m2/count_model.py | 206 ++++++++++++++++ .../qwen_deltanet_m2/head_reference.py | 184 ++++++++++++++ .../operators/qwen_deltanet_m2/m1b_builder.h | 225 ++++++++++++++++++ research/operators/qwen_deltanet_m2/record.py | 88 +++++++ 6 files changed, 902 insertions(+) create mode 100644 research/operators/qwen_deltanet_m2/.gitignore create mode 100644 research/operators/qwen_deltanet_m2/admit_proto.cpp create mode 100644 research/operators/qwen_deltanet_m2/count_model.py create mode 100644 research/operators/qwen_deltanet_m2/head_reference.py create mode 100644 research/operators/qwen_deltanet_m2/m1b_builder.h create mode 100644 research/operators/qwen_deltanet_m2/record.py diff --git a/research/operators/qwen_deltanet_m2/.gitignore b/research/operators/qwen_deltanet_m2/.gitignore new file mode 100644 index 00000000..9d931c43 --- /dev/null +++ b/research/operators/qwen_deltanet_m2/.gitignore @@ -0,0 +1 @@ +work/ diff --git a/research/operators/qwen_deltanet_m2/admit_proto.cpp b/research/operators/qwen_deltanet_m2/admit_proto.cpp new file mode 100644 index 00000000..9db592ec --- /dev/null +++ b/research/operators/qwen_deltanet_m2/admit_proto.cpp @@ -0,0 +1,198 @@ +// SPDX-License-Identifier: Apache-2.0 +// Research-only PROTOTYPE for the M2 design (experiments/20261105-32-qwen-deltanet-m2-design, H5): +// a verifier entry point that takes the public statement as 64-bit words, decodes and range-checks +// every integer itself, derives every relation target from the decoded integers, and only then runs +// the unchanged proof verifier (lookup.h). There is no separate admissible() a caller can skip, and +// no entry point that accepts caller-built relations. +// The statement is the M1b one (one head, D = 128, T = 4, F = 12). Usage: +// admit_proto.bin honest prove from the fixture; verify_from_words; one JSON line +// admit_proto.bin hostile honest run + every pre-registered hostile encoding +#include "m1b_builder.h" + +// The statement on the wire, in this order: +// T, D, F | S0[D*D] | per token t: a, beta, k[D], v[D], q[D], o[D] | S_T[D*D] +// The verifier fixes the shape (T, D, F) itself; the header must equal it. +struct Shape { int T = 4, D = 128, F = 12; }; +static u64 enc(i64 x) { return fi(x).v; } +static std::vector encode(const Fixture &z) { + std::vector w = {u64(z.T), u64(z.D), u64(z.F)}; + for (auto s : z.S0) w.push_back(enc(s)); + for (int t = 0; t < z.T; t++) { + w.push_back(enc(z.a[t])); w.push_back(enc(z.beta[t])); + for (auto x : z.k[t]) w.push_back(enc(x)); + for (auto x : z.v[t]) w.push_back(enc(x)); + for (auto x : z.q[t]) w.push_back(enc(x)); + for (auto x : z.o[t]) w.push_back(enc(x)); + } + for (auto s : z.S[z.T - 1]) w.push_back(enc(s)); + return w; +} +static size_t w_S0(const Shape &, size_t c) { return 3 + c; } +static size_t w_tok(const Shape &s, int t) { return 3 + size_t(s.D) * s.D + size_t(t) * (2 + 4 * s.D); } +static size_t w_a(const Shape &s, int t) { return w_tok(s, t); } +static size_t w_beta(const Shape &s, int t) { return w_tok(s, t) + 1; } +static size_t w_k(const Shape &s, int t, int i) { return w_tok(s, t) + 2 + i; } +static size_t w_v(const Shape &s, int t, int i) { return w_tok(s, t) + 2 + s.D + i; } +static size_t w_q(const Shape &s, int t, int i) { return w_tok(s, t) + 2 + 2 * s.D + i; } +static size_t w_o(const Shape &s, int t, int i) { return w_tok(s, t) + 2 + 3 * s.D + i; } +static size_t w_ST(const Shape &s, size_t c) { return w_tok(s, s.T) + c; } + +enum class Verdict { ACCEPT, DECODE, PROOF }; +static const char *stage_name(Verdict x) { return x == Verdict::ACCEPT ? "accept" : x == Verdict::DECODE ? "decode" : "proof"; } +struct VerifyTiming { double decode_s = 0, derive_s = 0, verify_s = 0; std::string why; }; + +// One word as a signed integer of a class: canonical (< p), then lo <= x <= hi. A word above p/2 +// reads as negative. +static bool decode(u64 w, i64 lo, i64 hi, i64 &out) { + if (w >= P) return false; + const i64 x = w > P / 2 ? -(i64)(P - w) : (i64)w; + if (x < lo || x > hi) return false; + out = x; return true; +} +// Sizing as in the M1b test, a function of the derived statement only (the M1b schedule held at its +// first try in #63; a schedule search would also be a pure function of the shape). +struct Sized { LookupRun run; unsigned L = 0; }; +static Sized size_run(const Built &b) { + unsigned L = 1; while ((u64(1) << L) < b.cols.size()) L++; + { + Setting s0 = setting_goldilocks(); s0.target = 10; s0.set_work(ErrorKind::QUERY, Scope::REQUEST, 6); + const unsigned N0 = rng_combinations(s0, Scope::REQUEST); + const u64 dc = pow2_at_least(b.rc_rows), np = pow2_at_least(N0); + RngShape probe; probe.digit_bits[0] = 12, probe.digit_bits[1] = 11, probe.combinations = N0, probe.digit_columns = dc; + const LkpPublic lk = rng_lookup(probe); + while ((u64(1) << L) < std::max(dc * np, rng_planes_places(lk))) L++; + } + Setting set = setting_goldilocks(); set.hash_block_bytes = HASH_BLOCK_BYTES; set.target = 10; set.set_work(ErrorKind::QUERY, Scope::REQUEST, 6); + RegionSpec rs; rs.rows = {b.rc_rows}; rs.first_place = {b.rc_base}; rs.columns = RCOLS; + const std::vector further = {{1, 1, L, L}, {0, 0, L, L}}; + Sized s{lookup_size(set, L, L + 1, L + 1, further, b.rel, rs), L}; + check(s.run.product.plan->holds(), "plan holds (M1b schedule)"); + return s; +} + +// THE ENTRY POINT. Everything a caller supplies is untrusted: the statement words and the proof bytes. +// (1) fix the shape; (2) decode every word as a canonical field element and read it as an integer of +// its class, rejecting anything outside; (3) derive every relation target from the decoded integers +// (relation-only build); (4) size and run the unchanged proof verifier. +// Class bounds (the M1b admit, now inside the verifier): |S0|, |S_T| < 2^18; a, beta in [0, 2^F]; +// |k|, |q| <= 2^F; |v|, |o| < 2^31. +static Verdict verify_from_words(const Shape &sh, const std::vector &w, const u8 *proof, size_t bytes, VerifyTiming *tm = nullptr) { + auto t0 = std::chrono::steady_clock::now(); + const size_t DD = size_t(sh.D) * sh.D, want = 3 + DD + size_t(sh.T) * (2 + 4 * sh.D) + DD; + if (w.size() != want) { if (tm) tm->why = "statement length"; return Verdict::DECODE; } + if (w[0] != u64(sh.T) || w[1] != u64(sh.D) || w[2] != u64(sh.F)) { if (tm) tm->why = "header differs from the verifier's shape"; return Verdict::DECODE; } + const i64 one = i64(1) << sh.F; + Fixture z; z.T = sh.T, z.D = sh.D, z.F = sh.F; + size_t p = 3; bool ok = true; + auto take = [&](size_t n, i64 lo, i64 hi) { std::vector r(n, 0); for (size_t i = 0; i < n && ok; i++) ok = decode(w[p++], lo, hi, r[i]); return r; }; + z.S0 = take(DD, -(BOUND - 1), BOUND - 1); + for (int t = 0; t < sh.T && ok; t++) { + z.a.push_back(take(1, 0, one)[0]); z.beta.push_back(take(1, 0, one)[0]); + z.k.push_back(take(sh.D, -one, one)); z.v.push_back(take(sh.D, -(VBOUND - 1), VBOUND - 1)); + z.q.push_back(take(sh.D, -one, one)); z.o.push_back(take(sh.D, -(VBOUND - 1), VBOUND - 1)); + } + std::vector ST = ok ? take(DD, -(BOUND - 1), BOUND - 1) : std::vector(); + if (!ok) { if (tm) tm->why = "a public word is non-canonical or outside its class"; return Verdict::DECODE; } + if (tm) tm->decode_s = secs(t0); + // The builder reads z.S[T-1] as the public final state and z.o as targets; z.M, z.delta, z.S[< T-1] + // are not read by the relation list. Shapes only. + z.S.assign(sh.T, std::vector(DD, 0)); z.S[sh.T - 1] = ST; + auto t1 = std::chrono::steady_clock::now(); + g_relonly = true; Built b = build(z); g_relonly = false; + Sized s = size_run(b); + if (tm) tm->derive_s = secs(t1); + auto t2 = std::chrono::steady_clock::now(); + const char *why = nullptr; + const bool acc = lookup_verify(s.run, proof, bytes, &why); + if (tm) { tm->verify_s = secs(t2); tm->why = acc ? "" : (why ? why : "proof verifier"); } + return acc ? Verdict::ACCEPT : Verdict::PROOF; +} + +static Table table_of(const Built &b, unsigned L) { + Table tab(1, 3, u64(1) << L); + for (size_t c = 0; c < b.cols.size(); c++) for (int r = 0; r < 3; r++) tab.set(r, c, E::from(b.cols[c][r])); + return tab; +} + +int main(int argc, char **argv) { + check(argc == 3, "usage: admit_proto.bin honest|hostile"); + const std::string mode = argv[2]; + const Shape sh; + auto z = load(argv[1]); + check(z.T == sh.T && z.D == sh.D && z.F == sh.F, "fixture shape"); + // Prover side, unchanged from M1b: witness from the fixture, cell by cell against reference.py. + Built b = build(z); + for (int t = 0; t < z.T; t++) check(b.M[t] == z.M[t] && b.delta[t] == z.delta[t] && b.S[t] == z.S[t] && b.o[t] == z.o[t], "C++ lowering equals reference.py"); + Sized s = size_run(b); + Table tab = table_of(b, s.L); + check(products_hold_all(tab) && violated(b.rel, tab) == 0, "honest table"); + auto tp = std::chrono::steady_clock::now(); Writer proof = prove(s.run, b, tab, false); const double prove_s = secs(tp); + const std::vector words = encode(z); + auto pb = proof.bytes(); + VerifyTiming tm; const Verdict st = verify_from_words(sh, words, pb.data(), pb.size(), &tm); + std::printf("{\"mode\":\"honest\",\"accept\":%s,\"stage\":\"%s\",\"statement_words\":%zu,\"proof_bytes\":%zu,\"prove_s\":%.3f,\"decode_s\":%.4f,\"derive_s\":%.3f,\"verify_s\":%.3f,\"peak_rss_kb\":%ld}\n", + st == Verdict::ACCEPT ? "true" : "false", stage_name(st), words.size(), pb.size(), prove_s, tm.decode_s, tm.derive_s, tm.verify_s, rss_kb()); + std::fflush(stdout); + check(st == Verdict::ACCEPT, "honest accepted by verify_from_words"); + if (mode != "hostile") return 0; + + int as_expected = 0, total = 0; + auto report = [&](const char *cls, const std::string &what, Verdict got, Verdict want) { + total++; const bool good = got == want; as_expected += good; + std::printf("{\"mode\":\"hostile\",\"class\":\"%s\",\"attack\":\"%s\",\"rejected\":%s,\"stage\":\"%s\",\"expected_stage\":\"%s\",\"as_expected\":%s}\n", + cls, what.c_str(), got != Verdict::ACCEPT ? "true" : "false", stage_name(got), stage_name(want), good ? "true" : "false"); + std::fflush(stdout); + }; + auto with = [&](size_t idx, u64 word) { auto w = words; w[idx] = word; return w; }; + const u64 HALF = (P + 1) / 2; // the huge field element: about 2^63 in magnitude as a signed read + const i64 one = i64(1) << sh.F; + struct Cls { const char *name; size_t idx; i64 out_pos; i64 out_neg; }; + // One element per class; out_pos / out_neg = the first integers outside the class on each side. + const std::vector cls = { + {"S0", w_S0(sh, 0), BOUND, -BOUND}, {"a", w_a(sh, 1), one + 1, -1}, {"beta", w_beta(sh, 1), one + 1, -1}, {"k", w_k(sh, 1, 0), one + 1, -one - 1}, + {"v", w_v(sh, 1, 0), VBOUND, -VBOUND}, {"q", w_q(sh, 1, 0), one + 1, -one - 1}, {"o", w_o(sh, sh.T - 1, 0), VBOUND, -VBOUND}, {"S_T", w_ST(sh, 0), BOUND, -BOUND}}; + for (const Cls &c : cls) { + report("huge-element", std::string(c.name) + " = (p+1)/2", verify_from_words(sh, with(c.idx, HALF), pb.data(), pb.size()), Verdict::DECODE); + report("class-bound", std::string(c.name) + " = first value outside class (+)", verify_from_words(sh, with(c.idx, enc(c.out_pos)), pb.data(), pb.size()), Verdict::DECODE); + report("class-bound", std::string(c.name) + " = first value outside class (-)", verify_from_words(sh, with(c.idx, enc(c.out_neg)), pb.data(), pb.size()), Verdict::DECODE); + } + { + const size_t idx = w_a(sh, 0); check(words[idx] < (u64(1) << 32), "a is small"); + report("non-canonical", "a_0 word = a_0 + p (second encoding of the same element)", verify_from_words(sh, with(idx, words[idx] + P), pb.data(), pb.size()), Verdict::DECODE); + const size_t io = w_o(sh, 0, 0); + if (words[io] < (u64(1) << 32)) report("non-canonical", "o_0[0] word = o_0[0] + p", verify_from_words(sh, with(io, words[io] + P), pb.data(), pb.size()), Verdict::DECODE); + } + report("header", "T word = 5", verify_from_words(sh, with(0, 5), pb.data(), pb.size()), Verdict::DECODE); + report("header", "F word = 10", verify_from_words(sh, with(2, 10), pb.data(), pb.size()), Verdict::DECODE); + { auto w = words; w.push_back(0); report("length", "one trailing word", verify_from_words(sh, w, pb.data(), pb.size()), Verdict::DECODE); } + { auto w = words; w.pop_back(); report("length", "one word short", verify_from_words(sh, w, pb.data(), pb.size()), Verdict::DECODE); } + // Controls: in-class but wrong public values pass decode and the proof verifier rejects them. + report("in-class", "o_T[0] += 1 (valid integer, wrong)", verify_from_words(sh, with(w_o(sh, sh.T - 1, 0), enc(z.o[sh.T - 1][0] + 1)), pb.data(), pb.size()), Verdict::PROOF); + report("in-class", "S0[0] += 1 (valid integer, wrong)", verify_from_words(sh, with(w_S0(sh, 0), enc(z.S0[0] + 1)), pb.data(), pb.size()), Verdict::PROOF); + + // R9 (#63 review): o_T[j]' = o_T[j] - 2^-F (mod p), a field element that is not a small integer. + // 2^F o' = 2^F o - 1, so the output floor's relation holds with remainder r + 1. Pick j with + // r < 2^F - 1, re-decompose r + 1 into F honest bits, and prove the changed relation HONESTLY: + // 0 violated relations, a complete proof. Control: the current path (caller-built relations, + // admissible() skipped, lookup_verify) accepts it. Test: verify_from_words rejects it at decode. + { + int j = -1; for (int x = 0; x < sh.D; x++) if (b.o_rem_last[x] < one - 1) { j = x; break; } + check(j >= 0, "an output remainder below 2^F - 1"); + Built bad = b; Table tbad = tab; + const i64 r1 = b.o_rem_last[j] + 1; + for (int i = 0; i < sh.F; i++) { const u64 c = b.o_rbits_last[j][i]; const E x = E::from(fi((r1 >> i) & 1)); tbad.set(0, c, x); tbad.set(1, c, x); tbad.set(2, c, x * x); } + bad.rel[b.o_rel_last[j]].target = bad.rel[b.o_rel_last[j]].target + F{1}; // -2^F o' = -2^F o + 1 + check(products_hold_all(tbad), "R9 table products"); + const size_t viol = violated(bad.rel, tbad); + Sized sbad = size_run(bad); + Writer p9 = prove(sbad.run, bad, tbad, false); + auto b9 = p9.bytes(); + const bool caller_path = lookup_verify(sbad.run, b9.data(), b9.size()); + const u64 oprime = (fi(z.o[sh.T - 1][j]) - inv(F{u64(one)})).v; + std::printf("{\"mode\":\"r9-detail\",\"j\":%d,\"o\":%lld,\"o_prime_word\":\"%llu\",\"violated_relations\":%zu}\n", j, (long long)z.o[sh.T - 1][j], (unsigned long long)oprime, viol); + report("r9-control", "current path: lookup_verify with caller-built relations, admissible() skipped", caller_path ? Verdict::ACCEPT : Verdict::PROOF, Verdict::ACCEPT); + report("r9", "verify_from_words on the R9 statement and R9 proof", verify_from_words(sh, with(w_o(sh, sh.T - 1, j), oprime), b9.data(), b9.size()), Verdict::DECODE); + } + std::printf("{\"mode\":\"hostile-summary\",\"as_expected\":%d,\"total\":%d,\"peak_rss_kb\":%ld}\n", as_expected, total, rss_kb()); + return as_expected == total ? 0 : 1; +} diff --git a/research/operators/qwen_deltanet_m2/count_model.py b/research/operators/qwen_deltanet_m2/count_model.py new file mode 100644 index 00000000..a4d2c83f --- /dev/null +++ b/research/operators/qwen_deltanet_m2/count_model.py @@ -0,0 +1,206 @@ +#!/usr/bin/env python3 +"""Counting model of the Qwen DeltaNet lowering on the wide product oracle (M2 design, H1-H3). + +It walks the SAME emission order as the builders in research/operators/qwen_deltanet/test.cpp (M1, arm +'m1') and research/operators/qwen_deltanet_lookup/test.cpp (M1b, arm 'm1b'), and counts product +columns, relations, relation terms, RNG range cells and LKP keys. No proof is built. H1 checks the +counts against the measured #47 / #63 statements. Arms 'A' and 'B' are the M2 lowering (DESIGN.md): + A remainders as exact bits (as M1b) + B remainders lookup-exact: each 12-bit remainder limb is ONE product cell that is a direct LKP key + into the 2^12 range table; a 24-bit remainder is two such limbs (coefficients 1 and 2^12) in the + floor relation itself, so no recomposition relation is needed. +M2 adds (DESIGN.md section 4): private head output o (RNG cell), the proved gated RMSNorm of o +(o*o and o*Rc gates, isqrt and reciprocal scalars with exact limbs, w RNG cell, y floor), T = 16. +Usage: count_model.py [--json out.json] +""" +import argparse, json, math + +M1_MEASURED = dict(columns_used=2_338_816, log_length=22, relations=4_761_088, terms=11_430_400, range_cells=0) +M1B_MEASURED = dict(columns_used=1_310_720, columns_nonregion=1_074_176, log_length=21, relations=2_231_808, terms=5_174_272, range_cells=67_072) +P2 = lambda n: 1 << max(0, math.ceil(math.log2(n))) if n > 0 else 0 + +class C: + """Counters; the methods mirror the builder's lambdas col/rel/bits/rcell, plus key() for arm B.""" + def __init__(s, arm): + s.arm, s.cols, s.rels, s.terms, s.rcells, s.keys = arm, 0, 0, 0, 0, 0 + def col(s, n=1): s.cols += n + def rel(s, nterms, n=1): s.rels += n; s.terms += nterms * n + def rcell(s, n=1): s.rcells += n + def key(s, n=1): s.keys += n + def bits(s, width, n=1): + """width-bit unsigned decomposition: width [x,x,x] columns, 2 relations of 2 terms each; returns + the number of terms it adds to the caller's relation.""" + s.cols += width * n; s.rels += 2 * width * n; s.terms += 4 * width * n + return width + def rem(s, width, n=1): + """An exact remainder in [0, 2^width), width in {12, 24}. Returns the terms added to the + caller's relation. Arm m1/m1b/A: bits. Arm B: width/12 key cells (each a product column [r,0,0] + laid in the key region; counted as keys, not as gate columns).""" + if s.arm in ('m1', 'm1b', 'A'): + return s.bits(width, n) + assert width % 12 == 0 + s.key(width // 12 * n) + return width // 12 + +def head(arm, D=128, T=4, F=12, o_private=False, norm=False): + c = C(arm) + q19 = arm == 'm1' # M1: 19-bit range decomposition of S', M, delta (no RNG) + for t in range(T): + # g1 (decay) and g2 (memory read), per state cell + c.col(2 * D * D) + c.rel(1 if t == 0 else 2, D * D) # g1.a = S0 target (t = 0) or = canonical previous cell + c.rel(1, D * D) # g1.b = a + c.rel(2, D * D) # g2.a = canonical + c.rel(1, D * D) # g2.b = k[i] + for _ in range(D): # per value column j + c.col(1); c.rel(D + 1) # g5 = P_j * a, with P_j = sum of g2.c + c.rel(1) # g5.b = a + c.col(1) # g6 = (v - M) * beta + ft = 2 + c.rem(2 * F); c.rel(ft) # 2^2F (v - g6.a) + r - g5.c = 0 + if q19: + c.rel(1 + c.bits(19)) # M + 2^18 = bits (M = v - g6.a) + else: + c.rcell(); c.rel(2) # M (range cell) + g6.a = v + c.rel(1) # g6.b = beta + c.col(1); c.rel(1) # dcell = k[0] * delta; .a = k[0] + dt = 2 + c.rem(F); c.rel(dt) # 2^F delta + r - g6.c = 0 + if q19: + c.rel(1 + c.bits(19)) # delta + 2^18 = bits + else: + c.rcell(2); c.rel(3) # delta = 2^18 hi + lo + # update + output gate, per state cell + c.col(D * D) # g4 = S' * q[i] + c.col((D - 1) * D); c.rel(1, (D - 1) * D); c.rel(2, (D - 1) * D) # g3 for i > 0: .a = k[i], .b = dcell + c.rel(1, D * D) # g4.b = q[i] + w = c.rem(F, D * D); c.rel(3 + w, D * D) # 2^F g4.a + r - g1.c - g3.c = 0 (w terms per remainder) + if q19: + c.bits(19, D * D); c.rel(20, D * D) # bits(S' + 2^18, 19), then g4.a + 2^18 = bits + else: + c.rcell(D * D); c.rel(2, D * D) # g4.a - range cell = 0 + # output o[j] = floor(sum_i g4.c / 2^F) + for _ in range(D): + r = c.rem(F) + if o_private: + c.rcell(); c.rel(D + r + 1) # -2^F o (cell) + sum g4.c - r = 0 + else: + c.rel(D + r) # target -2^F o + if norm: + gated_norm(c, D, F) + if t == T - 1: + c.rel(1, D * D) # S_T target + return c + +def gated_norm(c, D, F): + """Proved gated RMSNorm of the private o (DESIGN.md section 4.4). Per token: + Vx = sum o^2; sigma = isqrt(Vx + eps); Rc = floor(2^36 / sigma); w = floor(o Rc / 2^24); + y = floor(cg w / 2^F) with cg public (verifier-derived from gain, SiLU(z), sqrt(D)); y public.""" + # scalars: sigma (2 exact 12-bit limbs), e1 = Vx + eps - sigma^2 (2 limbs), e2 = sigma^2 + 2 sigma - Vx - eps (2), + # Rc (3 limbs), rr = 2^36 - Rc sigma (2), sigma - 1 - rr (2) + if c.arm == 'B': + c.key(2 + 2 + 2 + 3 + 2 + 2) + else: + c.bits(24 + 24 + 24 + 36 + 24 + 24) + c.col(2) # gates sigma*sigma, Rc*sigma + c.rel(3, 2); c.rel(4, 2) # their operands tied to the limb sums (2 ties each) + c.rel(D + 4 if c.arm == 'B' else D + 2 + 24) # e1 relation: sum o^2 + eps - sigma^2 - e1 = 0 + c.rel(D + 5 if c.arm == 'B' else D + 3 + 24) # e2 relation + c.rel(4 if c.arm == 'B' else 2 + 36 + 0) # rr = 2^36 - Rc sigma (limbs) + c.rel(6 if c.arm == 'B' else 2 + 48) # sigma - 1 - rr = limbs + for _ in range(D): + c.col(2) # o*o and o*Rc + c.rel(2, 2) # o*o: .a = o, .b = o + c.rel(2, 1); c.rel(4 if c.arm == 'B' else 4, 1) # o*Rc: .a = o, .b = Rc limbs + c.rcell() # w (|w| <= 2^12 honest) + r = c.rem(2 * F); c.rel(2 + r) # 2^24 w + r - (o Rc) = 0 + r = c.rem(F); c.rel(1 + r) # 2^F y + r - cg w = 0, target y (public) + +def layout(c, arm, T): + """Table length. m1: the columns, padded to 2^L. m1b: one aligned RNG region after the gates (as the + builder). A/B: RNG cells as one 16-column matrix per power-of-two piece (RngShape takes several + matrices), LKP keys as parts (LkpPublic::parts, one function), regions first in decreasing size, + each aligned to its size, then the gates.""" + out = {} + if arm == 'm1': + used = c.cols; L = max(1, math.ceil(math.log2(used))) + return dict(columns_used=used, layout=used, log_length=L) + if arm == 'm1b': + rows = P2(math.ceil(c.rcells / 16)); size = rows * 16 + base = math.ceil(c.cols / size) * size; total = base + size + return dict(columns_used=total, columns_nonregion=c.cols, rng_rows=rows, log_length=math.ceil(math.log2(total))) + def pieces(n, quantum): + # binary decomposition of n rounded up to a multiple of quantum + n = math.ceil(n / quantum) * quantum; out = [] + b = 1 << n.bit_length() + while n: + b >>= 1 + if n & b: out.append(b); n -= b + return out + rng_rows = pieces(math.ceil(c.rcells / 16), 1024) + key_parts = pieces(c.keys, 16384) if c.keys else [] + blocks = sorted([r * 16 for r in rng_rows] + key_parts, reverse=True) + pos = 0 + for b in blocks: pos = math.ceil(pos / b) * b + b + total = pos + c.cols + L = math.ceil(math.log2(total)) + # RNG digits must fit one row: digit_columns (pow2 of cell rows) x Np (16 at N = 10) + dc = P2(sum(rng_rows)) if rng_rows else 0 + L = max(L, math.ceil(math.log2(dc * 16)) if dc else 0) + return dict(columns_used=total, columns_nonregion=c.cols, rng_rows=rng_rows, key_parts=key_parts, digit_entries=2 * sum(rng_rows) * 10, log_length=L) + +def summary(arm, **kw): + c = head(arm, **kw); D, T = kw.get('D', 128), kw.get('T', 4) + lay = layout(c, arm, T) + cells = D * D * T + per_cell = (c.cols + c.rcells + c.keys) / cells + return dict(arm=arm, D=D, T=T, o_private=kw.get('o_private', False), norm=kw.get('norm', False), + gate_columns=c.cols, range_cells=c.rcells, lkp_keys=c.keys, relations=c.rels, terms=c.terms, + product_columns_per_state_cell_per_token=round(per_cell, 3), relations_per_state_cell_per_token=round(c.rels / cells, 3), **lay) + +def estimate_prove_s(log_length, relations): + """Indicative only: linear in the table length from the two measured points (#47: 2^22, 51.4 s; + #63: 2^21, 39.2 s). Not a measurement.""" + a = (51.43 - 39.18) / (2 ** 22 - 2 ** 21); b = 39.18 - a * 2 ** 21 + return round(a * 2 ** log_length + b, 1) + +def main(): + ap = argparse.ArgumentParser(); ap.add_argument('--json'); a = ap.parse_args() + res = {} + m1 = summary('m1'); m1b = summary('m1b') + h1 = { + 'm1': {k: (m1[k], v, m1[k] == v) for k, v in M1_MEASURED.items() if k in m1 or k == 'range_cells'}, + 'm1b': {k: (m1b.get(k), v, m1b.get(k) == v) for k, v in M1B_MEASURED.items()}, + } + h1['m1']['terms'] = (m1['terms'], M1_MEASURED['terms'], m1['terms'] == M1_MEASURED['terms']) + h1['m1']['relations'] = (m1['relations'], M1_MEASURED['relations'], m1['relations'] == M1_MEASURED['relations']) + h1_pass = all(x[2] for d in h1.values() for x in d.values()) + res['H1'] = dict(checks=h1, pass_=h1_pass, model_m1=m1, model_m1b=m1b) + A4 = summary('A', T=4); B4 = summary('B', T=4) + ratio_cols = B4['product_columns_per_state_cell_per_token'] / A4['product_columns_per_state_cell_per_token'] + ratio_rels = B4['relations'] / A4['relations'] + res['H2'] = dict(armA_m1b_shape=A4, armB_m1b_shape=B4, ratio_columns_per_cell=round(ratio_cols, 3), ratio_relations=round(ratio_rels, 3), + ratio_terms=round(B4['terms'] / A4['terms'], 3), pass_=ratio_cols <= 0.5 and ratio_rels <= 0.5, + note='arm A at the M1b shape equals M1b (same lowering); arm B swaps exact bits for direct LKP keys') + m2 = dict(D=128, o_private=True, norm=True) + A16 = summary('A', T=16, **m2); B16 = summary('B', T=16, **m2) + maxT = {} + for arm in ('A', 'B'): + T = 1 + while summary(arm, T=T + 1, **m2)['log_length'] <= 22 and summary(arm, T=T + 1, **m2)['relations'] <= 2 ** 23: T += 1 + maxT[arm] = T + layer = {arm: dict(relations_per_token_per_layer=48 * s['relations'] // 16) for arm, s in (('A', A16), ('B', B16))} + for arm, s in (('A', A16), ('B', B16)): + layer[arm]['columns_per_token_per_layer'] = 48 * (s['gate_columns'] + s['range_cells'] + s['lkp_keys']) // 16 + layer[arm]['columns_per_token_48_deltanet_layers'] = 48 * layer[arm]['columns_per_token_per_layer'] + s['prove_s_estimate'] = estimate_prove_s(s['log_length'], s['relations']) if s['log_length'] <= 22 else None + res['H3'] = dict(armA_T16=A16, armB_T16=B16, max_T_log22=maxT, layer_extrapolation=layer, pass_=B16['log_length'] <= 22, + note='prove_s_estimate is linear in table length from #47/#63 points; indicative only. Layer extrapolation = 48 V heads x per-head-per-token; DeltaNet recurrence + gated norm only (no matmuls).') + # remainder savings (hard rule c), at the M2 shape + res['remainder_savings'] = dict( + armA_T16_columns=A16['gate_columns'] + A16['range_cells'], armB_T16_columns=B16['gate_columns'] + B16['range_cells'] + B16['lkp_keys'], + relations_A=A16['relations'], relations_B=B16['relations'], terms_A=A16['terms'], terms_B=B16['terms']) + js = json.dumps(res, indent=1, default=str) + print(js) + if a.json: open(a.json, 'w').write(js + '\n') + +if __name__ == '__main__': + main() diff --git a/research/operators/qwen_deltanet_m2/head_reference.py b/research/operators/qwen_deltanet_m2/head_reference.py new file mode 100644 index 00000000..7abd09ba --- /dev/null +++ b/research/operators/qwen_deltanet_m2/head_reference.py @@ -0,0 +1,184 @@ +#!/usr/bin/env python3 +"""Integer reference for ONE V head of a Qwen3.8 Gated DeltaNet layer, end to end from the in_proj +outputs to the gated-norm output (the M2 statement of DESIGN.md), plus a float64 twin. + +Chosen research contract, not the publisher's numerics. The public-only stage (conv1d, SiLU, q/k L2 +norm, q scale, decay and beta) is what the M2 verifier RECOMPUTES from public integers; the private +stage (recurrence, o, gated RMSNorm) is what the M2 relation proves. This file measures the ranges +the DESIGN's cell classes must hold (PREREG H4). + +Scales: every activation integer is at 2^F, F = 12. Per token t, public: + xq, xk, xv in_proj_qkv outputs of this head's channels (128 each), pre-conv, rounded at 2^F + ar, br in_proj_a / in_proj_b outputs (scalars), at 2^F + z in_proj_z outputs (128), at 2^F +Per head, public: conv kernels (3 x 128 x 4 taps, at 2^12), A = exp(A_log) and dt_bias (float64), gain. +Usage: head_reference.py --suite [--json out.json] [--kernel random|delta] [--steps N] +""" +import argparse, json, math, sys, time, pathlib +import numpy as np +sys.path.insert(0, str(pathlib.Path(__file__).resolve().parents[1] / 'qwen_deltanet')) +import reference as R # M1: step() (the staged-floor recurrence, unchanged), Head, softplus, silu, sigmoid + +D, F = 128, 12 +ONE = 1 << F +BOUND = 1 << 18 +EPS_L2 = 17 # rne(1e-6 * 2^2F), l2norm eps of the FLA kernel +EPS_NORM = int(round(D * 1e-6 * 2 ** 24)) # rms_norm_eps 1e-6, times D (sum, not mean), at 2^2F: 2147 +SQRT_D_Q12 = int(round(math.sqrt(D) * 4096)) # 46341 +RC_SHIFT = 36 # Rc = floor(2^36 / sigma) + +def isqrt_arr(x): + return np.array([math.isqrt(int(v)) for v in np.asarray(x).reshape(-1)], dtype=object).reshape(np.shape(x)) + +def conv_int(hist, w): + """Causal depthwise conv, kernel 4, zero padding: hist[tau] = x_{t - tau} (tau = 0..3), w[ch, tau] at 2^12.""" + acc = sum(w[:, tau] * hist[tau] for tau in range(4)) + return acc // 4096 + +def silu_int(c): + """Verifier-recomputed SiLU at 2^F: rne(2^F silu(c / 2^F)) in float64 (no table needed: public).""" + x = c.astype(np.float64) / ONE + return np.rint(ONE * (x / (1.0 + np.exp(-x)))).astype(np.int64) + +def l2n_int(u, extra=None): + """|u|-normalised at 2^F, round half up: floor((2 u 2^F S + s) / (2 s)), s = isqrt(sum u^2 + eps) (x sqrt(D) for q).""" + n2 = int((u.astype(object) ** 2).sum()) + s = math.isqrt(n2 + EPS_L2) + num = u.astype(object) * ONE + if extra: # q: also divide by sqrt(D) (s2 = s * round(sqrt(D) 2^12), numerator x 2^12) + s = s * extra; num = num * 4096 + out = (2 * num + s) // (2 * s) + return np.array(out, dtype=np.int64) + +def gates_int(ar, br, head): + a = int(np.rint(ONE * math.exp(-head.A * float(R.softplus(ar / ONE + head.dt_bias))))) + beta = int(np.rint(ONE * float(R.sigmoid(br / ONE)))) + return a, beta + +def gated_norm_int(o, z, gain): + """The PROVED part after the recurrence (DESIGN.md 4.4). Returns y and the exact quantities.""" + oo = o.astype(object) + Vx = int((oo ** 2).sum()) + sigma = math.isqrt(Vx + EPS_NORM) + e1 = Vx + EPS_NORM - sigma * sigma # >= 0 + e2 = sigma * sigma + 2 * sigma - Vx - EPS_NORM # >= 0 (so sigma = isqrt exactly) + Rc = (1 << RC_SHIFT) // sigma + rr = (1 << RC_SHIFT) - Rc * sigma # in [0, sigma) + gap = sigma - 1 - rr # >= 0 + prod = oo * Rc + w = np.array([p // (1 << 24) for p in prod], dtype=object) + rw = prod - w * (1 << 24) + cg = np.rint(ONE * gain * math.sqrt(D) * (z / ONE) / (1.0 + np.exp(-(z / ONE)))).astype(np.int64) # public, verifier-derived + num = cg.astype(object) * w + y = np.array([v // ONE for v in num], dtype=object) + ry = num - y * ONE + ex = dict(sigma=sigma, e1=e1, e2=e2, Rc=Rc, rr=rr, gap=gap, rw_max=int(max(rw)), rw_min=int(min(rw)), ry_max=int(max(ry)), ry_min=int(min(ry)), + w_absmax=int(max(abs(v) for v in w)), cg_absmax=int(np.abs(cg).max()), y_absmax=int(max(abs(v) for v in y)), Vx=Vx) + return np.array(y, dtype=np.int64), np.array(w, dtype=np.int64), ex + +def gen(seed, dist, T, kernel): + """Float in_proj outputs for one head. Pre-conv xq, xk ~ N(0,1); xv from M1's R1..R4 base (so the + heavy tail of R3 reaches the conv); z, ar, br ~ N(0,1); conv taps U(-0.5, 0.5) or a delta kernel.""" + rng = np.random.default_rng(1000 + seed); head = R.Head(rng); st = {} + if kernel == 'random': + W = rng.uniform(-0.5, 0.5, size=(3, D, 4)) + else: # 'delta': out_t = x_t, a pass-through stress kernel (unregistered, secondary) + W = np.zeros((3, D, 4)); W[:, :, 0] = 1.0 + toks = [] + for _ in range(T): + xq = rng.standard_normal(D); xk = rng.standard_normal(D) + b = dist[1] + if b == '1': xv = rng.standard_normal(D) + elif b == '2': xv = 2.0 * rng.standard_normal(D) + elif b == '3': xv = 2.0 * rng.standard_t(3, D) + else: + prev = st.get('ar'); e = rng.standard_normal(D) + z1 = e if prev is None else 0.9 * prev + math.sqrt(1 - 0.81) * e + st['ar'] = z1; xv = 2.0 * z1 + toks.append(dict(xq=xq, xk=xk, xv=xv, z=rng.standard_normal(D), ar=float(rng.standard_normal()), br=float(rng.standard_normal()))) + return head, W, toks + +def run(seed, dist, T, kernel, gain=1.0, q_unscaled=False): + """q_unscaled (secondary, unregistered option): drop q's 1/sqrt(D) in the integer path. The gated + RMSNorm is scale-invariant in o up to eps, so the factor can move into eps (x D); o is then about + sqrt(D) = 11.3x larger in integer units, i.e. finer.""" + global EPS_NORM + eps_saved = EPS_NORM + if q_unscaled: EPS_NORM = eps_saved * D + try: + return _run(seed, dist, T, kernel, gain, q_unscaled) + finally: + EPS_NORM = eps_saved + +def _run(seed, dist, T, kernel, gain, q_unscaled): + head, W, toks = gen(seed, dist, T, kernel) + Wi = np.rint(W * 4096).astype(np.int64) + q = lambda x: np.rint(np.asarray(x) * ONE).astype(np.int64) + S = np.zeros((D, D), dtype=np.int64); Sf = np.zeros((D, D)) + hist_i = [[np.zeros(D, dtype=np.int64)] * 4 for _ in range(3)] + hist_f = [[np.zeros(D)] * 4 for _ in range(3)] + mx = dict(S=0, M=0, delta=0, delta_lo=0, delta_hi=0, o=0, w=0, v=0, k_hat=0, q_hat=0, xin=0, y=0, cg=0, + sigma=0, e1=0, e2=0, Rc=0, rr=0, gap=0, Vx=0) + neg = dict(e1=0, e2=0, rr=0, gap=0, rw=0, ry=0) + div_S = div_y = 0.0 + for t, tk in enumerate(toks): + xs_i = [q(tk['xq']), q(tk['xk']), q(tk['xv'])] + mx['xin'] = max(mx['xin'], max(int(np.abs(x).max()) for x in xs_i)) + cs = [] + for c in range(3): + hist_i[c] = [xs_i[c]] + hist_i[c][:3] + cs.append(silu_int(conv_int(hist_i[c], Wi[c]))) + qh, kh, v = l2n_int(cs[0], None if q_unscaled else SQRT_D_Q12), l2n_int(cs[1]), cs[2] + a, beta = gates_int(q(tk['ar']), q(tk['br']), head) + tok = dict(a=a, beta=beta, k=kh, v=v, q=qh) + M, delta, S, o = R.step(S, tok, F, check=False) + dhi = delta // BOUND; dlo = delta - dhi * BOUND + z_i = q(tk['z']) + y, w, ex = gated_norm_int(o, z_i.astype(np.float64), gain) + for k_, arr in (('S', S), ('M', M), ('delta', delta), ('delta_lo', dlo), ('delta_hi', dhi), ('o', o), ('w', w), ('v', v), ('k_hat', kh), ('q_hat', qh), ('y', y)): + mx[k_] = max(mx[k_], int(np.abs(arr).max())) + for k_ in ('sigma', 'e1', 'e2', 'Rc', 'rr', 'gap', 'Vx'): mx[k_] = max(mx[k_], ex[k_]) + mx['cg'] = max(mx['cg'], ex['cg_absmax']) + for k_ in ('e1', 'e2', 'rr', 'gap'): neg[k_] = min(neg[k_], ex[k_]) + neg['rw'] = min(neg['rw'], ex['rw_min']); neg['ry'] = min(neg['ry'], ex['ry_min']) + # float twin + csf = [] + for c, key in enumerate(('xq', 'xk', 'xv')): + hist_f[c] = [np.asarray(tk[key])] + hist_f[c][:3] + u = sum(W[c][:, tau] * hist_f[c][tau] for tau in range(4)) + csf.append(u / (1 + np.exp(-u))) + qf = csf[0] / math.sqrt((csf[0] ** 2).sum() + 1e-6) / math.sqrt(D) + kf = csf[1] / math.sqrt((csf[1] ** 2).sum() + 1e-6) + af = math.exp(-head.A * float(R.softplus(tk['ar'] + head.dt_bias))); bf = float(R.sigmoid(tk['br'])) + Sf = Sf * af; df = bf * (csf[2] - kf @ Sf); Sf = Sf + np.outer(kf, df); of = qf @ Sf + zf = tk['z']; yf = gain * of / math.sqrt((of ** 2).mean() + 1e-6) * zf / (1 + np.exp(-zf)) + div_S = max(div_S, float(np.abs(S / ONE - Sf).max())); div_y = max(div_y, float(np.abs(y / ONE - yf).max())) + fit = dict( + rng_cells=all(mx[k] < BOUND for k in ('S', 'M', 'delta_lo', 'delta_hi', 'o', 'w')), + delta_split_needed=mx['delta'] >= BOUND, + sigma_24=mx['sigma'] < 2 ** 24, e1_24=mx['e1'] < 2 ** 24 and neg['e1'] >= 0, e2_24=mx['e2'] < 2 ** 24 and neg['e2'] >= 0, + Rc_36=mx['Rc'] < 2 ** 36, rr_24=mx['rr'] < 2 ** 24 and neg['rr'] >= 0, gap_24=mx['gap'] < 2 ** 24 and neg['gap'] >= 0, + rw_24=neg['rw'] >= 0, ry_12=neg['ry'] >= 0, + public_v_31=mx['v'] < 2 ** 31, public_y_31=mx['y'] < 2 ** 31, public_cg_24=mx['cg'] < 2 ** 24, public_x_31=mx['xin'] < 2 ** 31) + return dict(seed=seed, dist=dist, steps=T, kernel=kernel, max=mx, min_exact=neg, fit=fit, all_fit=all(v for k, v in fit.items() if k != 'delta_split_needed'), + div_S=div_S, div_y=div_y) + +def main(): + ap = argparse.ArgumentParser(); ap.add_argument('--suite', action='store_true'); ap.add_argument('--json') + ap.add_argument('--kernel', default='random'); ap.add_argument('--steps', type=int, default=1024) + ap.add_argument('--q-unscaled', action='store_true') + a = ap.parse_args() + t0 = time.time(); runs = [] + for dist in ('R1', 'R2', 'R3', 'R4'): + for seed in range(1, 6): + r = run(seed, dist, a.steps, a.kernel, q_unscaled=a.q_unscaled); runs.append(r) + print(json.dumps(dict(dist=dist, seed=seed, all_fit=r['all_fit'], S=r['max']['S'], delta=r['max']['delta'], o=r['max']['o'], w=r['max']['w'], + sigma=r['max']['sigma'], Rc=r['max']['Rc'], div_S=round(r['div_S'], 4), div_y=round(r['div_y'], 4))), flush=True) + agg = {k: max(r['max'][k] for r in runs) for k in runs[0]['max']} + res = dict(kernel=a.kernel, q_unscaled=a.q_unscaled, steps=a.steps, n=len(runs), fit=sum(r['all_fit'] for r in runs), max=agg, + worst_div_S=max(r['div_S'] for r in runs), worst_div_y=max(r['div_y'] for r in runs), seconds=round(time.time() - t0, 1), runs=runs) + print(json.dumps({k: v for k, v in res.items() if k != 'runs'})) + if a.json: pathlib.Path(a.json).write_text(json.dumps(res, indent=1) + '\n') + +if __name__ == '__main__': + main() diff --git a/research/operators/qwen_deltanet_m2/m1b_builder.h b/research/operators/qwen_deltanet_m2/m1b_builder.h new file mode 100644 index 00000000..fb1c91e1 --- /dev/null +++ b/research/operators/qwen_deltanet_m2/m1b_builder.h @@ -0,0 +1,225 @@ +// SPDX-License-Identifier: Apache-2.0 +// Research-only. The M1b statement builder and prover (research/operators/qwen_deltanet_lookup/ +// test.cpp, PR #63), split out as a header for the M2 design prototype admit_proto.cpp. Unchanged +// except: a relation-only switch (g_relonly) that turns off the honest-witness assertions when the +// verifier builds the relations (the column allocation and the relation list do not depend on it), +// and indices of the last token's output floors for the R9 attack. +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include "engine/engine_cpu.h" +#include "toy/toy_coins.h" +#include "../qwen_deltanet_lookup/lookup.h" +using namespace blk; + +static void check(bool ok, const char *s) { if (!ok) { std::fprintf(stderr, "FAIL: %s\n", s); std::exit(1); } } +static F fi(i64 x) { return x >= 0 ? F{u64(x)} : F{P - u64(-x)}; } +static i64 fdiv(i64 a, i64 b) { i64 q = a / b; if ((a % b) != 0 && ((a < 0) != (b < 0))) --q; return q; } +constexpr i64 BOUND = i64(1) << 18; // honest contract for S', M, delta limbs +constexpr u64 RTAG = u64(1) << 40; +constexpr u64 RCOLS = 16; +constexpr i64 VBOUND = i64(1) << 31; // public v and o admissibility (widened; no-wrap argument in PREREG) + +struct Fixture { + int T, D, F; + std::vector S0; // D*D, [i*D+j] + std::vector a, beta; // T + std::vector> k, v, q; // T x D + std::vector> M, delta, S, o; // reference values per token +}; +static Fixture load(const char *path) { + std::ifstream f(path, std::ios::binary); check(bool(f), "open fixture"); + std::vector w; i64 x; while (f.read((char *)&x, 8)) w.push_back(x); + size_t p = 0; auto take = [&](size_t n) { check(p + n <= w.size(), "fixture short"); std::vector r(w.begin() + p, w.begin() + p + n); p += n; return r; }; + Fixture z; auto h = take(3); z.T = h[0]; z.D = h[1]; z.F = h[2]; + const size_t D = z.D; z.S0 = take(D * D); + for (int t = 0; t < z.T; t++) { auto ab = take(2); z.a.push_back(ab[0]); z.beta.push_back(ab[1]); z.k.push_back(take(D)); z.v.push_back(take(D)); z.q.push_back(take(D)); } + for (int t = 0; t < z.T; t++) { z.M.push_back(take(D)); z.delta.push_back(take(D)); z.S.push_back(take(D * D)); z.o.push_back(take(D)); } + check(p == w.size(), "fixture trailing data"); + return z; +} +// Public admission: every public value is visibly inside its declared range. +static bool admissible(const Fixture &z) { + const i64 one = i64(1) << z.F; + for (auto s : z.S0) if (s <= -BOUND || s >= BOUND) return false; + for (int t = 0; t < z.T; t++) { + if (z.a[t] < 0 || z.a[t] > one || z.beta[t] < 0 || z.beta[t] > one) return false; + for (int i = 0; i < z.D; i++) { + if (std::llabs(z.k[t][i]) > one || std::llabs(z.q[t][i]) > one || std::llabs(z.v[t][i]) >= VBOUND || std::llabs(z.o[t][i]) >= VBOUND) return false; + } + } + for (auto s : z.S[z.T - 1]) if (s <= -BOUND || s >= BOUND) return false; + return true; +} + +// Witness + relation builder. Relations depend only on public values (shape is fixed by T, D, F). +struct Built { + std::vector> cols; + std::vector rel; + // indices for attacks + std::vector> state_col; // [t] -> column of canonical S_{t+1}[0..D*D) (row 0 of g4) + std::vector> state_rbits; // [t] -> low remainder bit column of the S_{t+1}[0] floor + std::vector> state_qbits; // [t] -> range bit columns of S_{t+1}[0] + std::vector g1_first, g6_first, m_rbit0, d_rbit0, d_cell; + std::vector> rel_a, rel_beta, rel_k0, rel_q0; + std::vector rel_v0_floor, rel_v0_range, rel_o0; + u64 rel_S00 = 0, rel_S4_00 = 0; + std::vector o_rbit0_last, o_rel_last; std::vector> o_rbits_last; std::vector o_rem_last; // last token: o remainder bit-0 column and remainder, per j + std::vector> M, delta, S, o; + std::vector rc; // columns of the range cells, in range-cell order + std::vector rcv; // their honest values + u64 rc_base = 0, rc_rows = 0; // region: product columns [rc_base, rc_base + 16 rc_rows) + std::vector rcell_state, rcell_M, rcell_dlo, rcell_dhi; // [t] -> range-cell index of S_{t+1}[0,0], M[0], delta_lo[0], delta_hi[0] +}; +static bool g_relonly = false; // relation-only build (verifier side): witness checks off, columns allocated identically +static Built build(const Fixture &z) { + Built b; const int D = z.D, FB = z.F; const i64 one = i64(1) << FB; + auto col = [&](i64 x, i64 y) { b.cols.push_back({fi(x), fi(y), fi(x) * fi(y)}); return u64(b.cols.size() - 1); }; + auto rel = [&](std::vector t, i64 target) { b.rel.push_back({std::move(t), fi(target)}); return u64(b.rel.size() - 1); }; + // Unsigned width-bit decomposition of n (honest n must fit; builder asserts). Returns the + // bit columns; each bit column is [x,x,x] with relations a-b=0, c-a=0, so x*x=x (boolean). + auto bits = [&](i64 n, unsigned width, std::vector &terms, i64 coef) { + check(g_relonly || (n >= 0 && n < (i64(1) << width)), "honest value outside its declared bit width"); + std::vector out; + for (unsigned i = 0; i < width; i++) { + i64 x = (n >> i) & 1; u64 c = col(x, x); + rel({{0, c, F{1}}, {1, c, fi(-1)}}, 0); rel({{2, c, F{1}}, {0, c, fi(-1)}}, 0); + terms.push_back({0, c, fi(coef * (i64(1) << i))}); out.push_back(c); + } + return out; + }; + // A range cell: column [x, 0, 0], range-checked by RNG (|x| < 2^23 proved); returns its range-cell index. + // Range cells live in one aligned region after all other columns (placed at the end of build()): + // range cell k is product column base + k, laid for RNG as cell (k mod rows, k div rows) of a + // rows x 16 matrix. Until then a term names it by RTAG + k. + auto rcell = [&](i64 x) { check(g_relonly || (x > -BOUND && x < BOUND), "honest range cell outside its contract"); b.rcv.push_back(x); b.rc.push_back(RTAG + b.rcv.size() - 1); return u64(b.rcv.size() - 1); }; + std::vector S = z.S0; std::vector canon(D * D); + b.state_col.resize(z.T); b.state_rbits.resize(z.T); b.state_qbits.resize(z.T); b.rcell_state.resize(z.T); b.rcell_M.resize(z.T); b.rcell_dlo.resize(z.T); b.rcell_dhi.resize(z.T); + b.rel_a.resize(z.T); b.rel_beta.resize(z.T); b.rel_k0.resize(z.T); b.rel_q0.resize(z.T); + for (int t = 0; t < z.T; t++) { + const i64 a = z.a[t], beta = z.beta[t]; + // g1 = S*a (decay), g2 = S*k[i] (memory read) + std::vector g1(D * D), g2(D * D); + for (int i = 0; i < D; i++) for (int j = 0; j < D; j++) { + const int c = i * D + j; + g1[c] = col(S[c], a); + if (t == 0) { canon[c] = g1[c]; u64 r = rel({{0, g1[c], F{1}}}, S[c]); if (c == 0) b.rel_S00 = r; } + else rel({{0, g1[c], F{1}}, {0, canon[c], fi(-1)}}, 0); + b.rel_a[t].push_back(rel({{1, g1[c], F{1}}}, a)); + g2[c] = col(S[c], z.k[t][i]); + rel({{0, g2[c], F{1}}, {0, canon[c], fi(-1)}}, 0); + u64 r = rel({{1, g2[c], F{1}}}, z.k[t][i]); if (i == 0) b.rel_k0[t].push_back(r); + } + b.g1_first.push_back(g1[0]); + std::vector M(D), delta(D); std::vector dcell(D); + for (int j = 0; j < D; j++) { + i64 Pj = 0; std::vector pt; + for (int i = 0; i < D; i++) { Pj += S[i * D + j] * z.k[t][i]; pt.push_back({2, g2[i * D + j], fi(-1)}); } + u64 g5 = col(Pj, a); pt.insert(pt.begin(), AffineTerm{0, g5, F{1}}); rel(pt, 0); + b.rel_a[t].push_back(rel({{1, g5, F{1}}}, a)); + const i64 aP = Pj * a; M[j] = fdiv(aP, i64(1) << (2 * FB)); const i64 rM = aP - M[j] * (i64(1) << (2 * FB)); + check(g_relonly || (M[j] > -BOUND && M[j] < BOUND), "honest M out of range (statement rejected)"); + u64 g6 = col(z.v[t][j] - M[j], beta); if (j == 0) b.g6_first.push_back(g6); + // 2^2F * (v - g6.a) + r - g5.c = 0 + std::vector ft = {{0, g6, fi(-(i64(1) << (2 * FB)))}, {2, g5, fi(-1)}}; + auto mb = bits(rM, 2 * FB, ft, 1); if (j == 0) b.m_rbit0.push_back(mb[0]); + u64 rf = rel(ft, -(i64(1) << (2 * FB)) * z.v[t][j]); + // M + 2^18 = bits, M = v - g6.a + // M (range cell) + g6.a = v + const u64 mc = rcell(M[j]); if (j == 0) b.rcell_M[t] = mc; + u64 rr = rel({{0, b.rc[mc], F{1}}, {0, g6, F{1}}}, z.v[t][j]); + if (j == 0) { b.rel_v0_floor.push_back(rf); b.rel_v0_range.push_back(rr); } + b.rel_beta[t].push_back(rel({{1, g6, F{1}}}, beta)); + const i64 num = (z.v[t][j] - M[j]) * beta; delta[j] = fdiv(num, one); const i64 rd = num - delta[j] * one; + const i64 dhi = fdiv(delta[j], BOUND), dlo = delta[j] - dhi * BOUND; // two limbs, dlo in [0, 2^18) + // delta lives in g3[0][j].b; created below, so reserve now + dcell[j] = col(z.k[t][0], delta[j]); + u64 r0 = rel({{0, dcell[j], F{1}}}, z.k[t][0]); b.rel_k0[t].push_back(r0); + std::vector dt = {{1, dcell[j], fi(one)}, {2, g6, fi(-1)}}; + auto db = bits(rd, FB, dt, 1); if (j == 0) { b.d_rbit0.push_back(db[0]); b.d_cell.push_back(dcell[j]); } + rel(dt, 0); + // delta = 2^18 delta_hi + delta_lo, both limbs range cells + const u64 lo = rcell(dlo), hi = rcell(dhi); if (j == 0) { b.rcell_dlo[t] = lo; b.rcell_dhi[t] = hi; } + rel({{1, dcell[j], F{1}}, {0, b.rc[hi], fi(-BOUND)}, {0, b.rc[lo], fi(-1)}}, 0); + } + // update: S'[i,j] = floor((a*S + k[i]*delta[j]) / 2^F); output gate g4 = S' * q[i] + std::vector Sn(D * D); std::vector g4(D * D); + for (int i = 0; i < D; i++) for (int j = 0; j < D; j++) { + const int c = i * D + j; + u64 g3 = dcell[j]; + if (i > 0) { + g3 = col(z.k[t][i], delta[j]); + rel({{0, g3, F{1}}}, z.k[t][i]); + rel({{1, g3, F{1}}, {1, dcell[j], fi(-1)}}, 0); + } + const i64 num = a * S[c] + z.k[t][i] * delta[j]; Sn[c] = fdiv(num, one); const i64 r = num - Sn[c] * one; + + g4[c] = col(Sn[c], z.q[t][i]); + u64 rq = rel({{1, g4[c], F{1}}}, z.q[t][i]); if (i == 0) b.rel_q0[t].push_back(rq); + std::vector ft = {{0, g4[c], fi(one)}, {2, g1[c], fi(-1)}, {2, g3, fi(-1)}}; + auto rb = bits(r, FB, ft, 1); rel(ft, 0); + const u64 sc = rcell(Sn[c]); rel({{0, g4[c], F{1}}, {0, b.rc[sc], fi(-1)}}, 0); + if (c == 0) { b.state_rbits[t] = rb; b.rcell_state[t] = sc; } + } + std::vector o(D); + for (int j = 0; j < D; j++) { + i64 sum = 0; std::vector ot; + for (int i = 0; i < D; i++) { sum += Sn[i * D + j] * z.q[t][i]; ot.push_back({2, g4[i * D + j], fi(-1)}); } + o[j] = fdiv(sum, one); auto ob = bits(sum - o[j] * one, FB, ot, 1); + if (t == z.T - 1) { b.o_rbits_last.push_back(ob); b.o_rbit0_last.push_back(ob[0]); b.o_rem_last.push_back(sum - o[j] * one); } + u64 r = rel(ot, -one * z.o[t][j]); if (j == 0) b.rel_o0.push_back(r); + if (t == z.T - 1) b.o_rel_last.push_back(r); + } + if (t == z.T - 1) for (int c = 0; c < D * D; c++) { u64 r = rel({{0, g4[c], F{1}}}, z.S[t][c]); if (c == 0) b.rel_S4_00 = r; } + for (int c = 0; c < D * D; c++) canon[c] = g4[c]; + b.state_col[t] = g4; + S = Sn; b.M.push_back(M); b.delta.push_back(delta); b.S.push_back(Sn); b.o.push_back(o); + } + // Place the range region: rows a power of two, base aligned to the region's size. + b.rc_rows = pow2_at_least((b.rcv.size() + RCOLS - 1) / RCOLS); + const u64 size = b.rc_rows * RCOLS; b.rc_base = (b.cols.size() + size - 1) / size * size; + b.cols.resize(b.rc_base + size, {F{0}, F{0}, F{0}}); + for (u64 k = 0; k < b.rcv.size(); k++) b.cols[b.rc_base + k] = {fi(b.rcv[k]), F{0}, F{0}}; + for (auto &c : b.rc) c = c - RTAG + b.rc_base; + for (auto &r : b.rel) for (auto &t : r.terms) if (t.column >= RTAG) t.column = t.column - RTAG + b.rc_base; + return b; +} + +static E eval(const WideRelation &a, const Table &t) { E x = E::zero(); for (auto z : a.terms) x += E::from(z.coefficient) * t.at(z.row, z.column); return x; } +static size_t violated(const std::vector &rel, const Table &t) { size_t n = 0; for (auto &a : rel) if (!(eval(a, t) == E::from(a.target))) n++; return n; } +static bool products_hold_all(const Table &t) { for (size_t i = 0; i < t.len; i++) if (!(t.at(2, i) == t.at(0, i) * t.at(1, i))) return false; return true; } + +// Prover. hostile: each linear row's target is replaced by its true local value on the (tampered) +// committed table, as in research/operators/glm_kda_fixedpoint/test.cpp. Unchanged verifier. +// The RNG cells, read back from row a of the (possibly tampered) table: a field element above +// p/2 is read as negative. cells[0][i * 16 + c] = product column base + c * rows + i. +static std::vector> range_cells(const Built &b, const Table &t) { + std::vector> cells(1, std::vector(b.rc_rows * RCOLS)); + for (u64 i = 0; i < b.rc_rows; i++) for (u64 c = 0; c < RCOLS; c++) { + const u64 v = t.at(0, b.rc_base + c * b.rc_rows + i).c[0].v; + cells[0][i * RCOLS + c] = v > P / 2 ? -(i64)(P - v) : (i64)v; + } + return cells; +} +// rmode: rng_prove_research mode (0 honest digits, 1 wrap out-of-window combinations, 2 one digit moved). +static u64 last_wrapped = 0; +static Writer prove(const LookupRun &run, const Built &bt, const Table &t, bool hostile, int rmode = 0) { + EngineCpu engine; ToyCoins coins(100); Writer proof; Binder b(LOOKUP_LABEL, run.product.plan); auto in = lookup_bind(b, run); + ProverRounds ch(b, random_digest(coins), proof, [&](const ChainPrefix &p, unsigned bits) { return engine.search_nonce(p, bits); }); + ProverContext pr(in.product.codes, ch, engine, coins); FoldMask mask; + auto g = toy_prove_preamble(pr, in.product, t, mask); auto oracle = pr.st.group(g).members[0]; + for (const auto &a : ch.get(in.relations)) { auto row = wide_row(oracle, a); if (hostile) row.target = eval(a, t); pr.st.add_row(g, std::move(row)); } + RngOutput out; last_wrapped = 0; + check(rng_prove_research(pr, in.range, ch.get(in.weights), range_cells(bt, t), g, out, rmode, &last_wrapped), "range check: a combination leaves its window (use rmode 1)"); + E x; pr.st.add_row(g, rng_row(pr, in.range, out, x)); + iopp_prove(pr, g, in.product.stages, in.product.choice, std::move(mask)); ch.finish(); pr.st.require_all_tested(); return proof; +} +static double secs(std::chrono::steady_clock::time_point a) { return std::chrono::duration(std::chrono::steady_clock::now() - a).count(); } +static long rss_kb() { rusage u; getrusage(RUSAGE_SELF, &u); return u.ru_maxrss; } + diff --git a/research/operators/qwen_deltanet_m2/record.py b/research/operators/qwen_deltanet_m2/record.py new file mode 100644 index 00000000..dea1d91b --- /dev/null +++ b/research/operators/qwen_deltanet_m2/record.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python3 +"""CPU receipt for the Qwen DeltaNet M2 design prototypes (experiments/20261105-32-qwen-deltanet-m2-design). + +Usage: record.py [build] [honest] [hostile] [model] [ranges] + build fixtures (M1 reference.py, seeds 1..10) + compile admit_proto.cpp + honest H5a: one honest proof per seed through verify_from_words (pinned to 6 cores) + hostile H5b/c: hostile public encodings + R9 on seed 1 + model H1-H3: count_model.py + ranges H4: head_reference.py range suite +Writes evidence.json here and raw lines to the experiment's runs/. +""" +import hashlib, json, os, pathlib, platform, statistics, subprocess, sys, time +here = pathlib.Path(__file__).resolve().parent +root = here.parents[2] +sys.path.insert(0, str(root / 'research/operators/qwen_deltanet')) +import reference as R +rel = str(here.relative_to(root)) +exp = root / 'experiments/20261105-32-qwen-deltanet-m2-design/runs' +work = here / 'work'; work.mkdir(exist_ok=True); exp.mkdir(parents=True, exist_ok=True) +SEEDS = list(range(1, 11)) +CORES = os.environ.get('M2_CORES', '0-5') +only = sys.argv[1:] + +def sh(argv, timeout=7200): + t = time.monotonic(); p = subprocess.run(argv, cwd=root, capture_output=True, text=True, timeout=timeout) + return dict(argv=[str(a) for a in argv], exit=p.returncode, seconds=round(time.monotonic() - t, 3), stdout=p.stdout, stderr=p.stderr[-4000:]) +def st(x): + return dict(n=len(x), median=statistics.median(x), sd=statistics.stdev(x) if len(x) > 1 else 0, min=min(x), max=max(x)) + +ev_path = here / 'evidence.json' +ev = json.loads(ev_path.read_text()) if ev_path.exists() and only else {} +ev.update(platform=platform.platform(), python=sys.version.split()[0], cores=CORES, + compiler=subprocess.check_output(['g++', '--version'], text=True).splitlines()[0], + base=subprocess.check_output(['git', 'merge-base', 'HEAD', 'origin/main'], cwd=root, text=True).strip(), + sources={str(p.relative_to(root)): hashlib.sha256(p.read_bytes()).hexdigest() for p in + sorted(here.glob('*.py')) + sorted(here.glob('*.cpp')) + sorted(here.glob('*.h')) + + [root / 'research/operators/qwen_deltanet_lookup/lookup.h', root / 'research/operators/qwen_deltanet/reference.py']}) +def save(): ev_path.write_text(json.dumps(ev, indent=1) + '\n') +binary = work / 'admit_proto.bin' +def run_bin(fx, mode): + return sh(['taskset', '-c', CORES, str(binary.relative_to(root)), str(fx.relative_to(root)), mode]) + +if not only or 'build' in only: + fx = {} + for s in SEEDS: + out = work / f'seed{s}.bin' + meta = R.write_fixture(str(out), s) + fx[s] = dict(meta=meta, sha256=hashlib.sha256(out.read_bytes()).hexdigest()) + ev['fixtures'] = fx + c = sh(['g++', '-std=c++17', '-O2', '-Wall', '-Wextra', '-fno-ipa-modref', '-DBLK_FIELD=2', '-DBLK_HASH=1', '-I', 'blocks', + f'{rel}/admit_proto.cpp', '-o', str(binary.relative_to(root))]) + ev['compile'] = c; save(); assert c['exit'] == 0, c['stderr'] + +if not only or 'honest' in only: + runs = [] + for s in SEEDS: + c = run_bin(work / f'seed{s}.bin', 'honest') + line = json.loads(c['stdout'].splitlines()[0]) if c['stdout'] else {} + runs.append(dict(seed=s, exit=c['exit'], wall=c['seconds'], **line, stderr=c['stderr'])) + print(json.dumps({k: v for k, v in runs[-1].items() if k != 'stderr'}), flush=True) + (exp / 'h5-honest.jsonl').write_text(''.join(json.dumps(r) + '\n' for r in runs)) + ok = [r for r in runs if r.get('accept')] + ev['honest'] = dict(accepted=len(ok), of=len(runs), prove_s=st([r['prove_s'] for r in ok]), decode_s=st([r['decode_s'] for r in ok]), + derive_s=st([r['derive_s'] for r in ok]), verify_s=st([r['verify_s'] for r in ok]), + proof_bytes=st([r['proof_bytes'] for r in ok]), peak_rss_kb=st([r['peak_rss_kb'] for r in ok])) + save() + +if not only or 'hostile' in only: + c = run_bin(work / 'seed1.bin', 'hostile') + (exp / 'h5-hostile-seed1.jsonl').write_text(c['stdout']) + lines = [json.loads(l) for l in c['stdout'].splitlines() if l.startswith('{')] + hs = [l for l in lines if l.get('mode') == 'hostile'] + by = {} + for l in hs: by.setdefault(l['class'], [0, 0]); by[l['class']][0] += l['as_expected']; by[l['class']][1] += 1 + ev['hostile'] = dict(exit=c['exit'], seconds=c['seconds'], as_expected=sum(l['as_expected'] for l in hs), total=len(hs), by_class=by, stderr=c['stderr']) + print(json.dumps(ev['hostile']), flush=True); save() + +if not only or 'model' in only: + c = sh([sys.executable, f'{rel}/count_model.py', '--json', str((exp / 'model.json').relative_to(root))]) + ev['model'] = dict(exit=c['exit'], seconds=c['seconds'], stdout=c['stdout'][-6000:]); print(c['stdout']); save() + +if not only or 'ranges' in only: + c = sh(['taskset', '-c', CORES, sys.executable, f'{rel}/head_reference.py', '--suite', '--json', str((exp / 'ranges-head.json').relative_to(root))]) + ev['ranges'] = dict(exit=c['exit'], seconds=c['seconds'], stdout=c['stdout'][-6000:]); print(c['stdout']); save() + # secondary, unregistered: q unscaled (o finer); and a delta conv kernel (pass-through stress) + for tag, extra in (('q-unscaled', ['--q-unscaled']), ('delta-kernel', ['--kernel', 'delta'])): + c = sh(['taskset', '-c', CORES, sys.executable, f'{rel}/head_reference.py', '--suite', *extra, '--json', str((exp / f'ranges-head-{tag}.json').relative_to(root))]) + ev[f'ranges_{tag}'] = dict(exit=c['exit'], seconds=c['seconds'], stdout=c['stdout'][-3000:]); print(c['stdout'][-1500:]); save() From e08e5ce2603bc442361822c55880ea870b4a9292 Mon Sep 17 00:00:00 2001 From: wakesync <60821182+wakesync@users.noreply.github.com> Date: Thu, 5 Nov 2026 15:30:41 +0000 Subject: [PATCH 3/6] experiments: Qwen DeltaNet M2 design, results and DESIGN.md (Part of #32) --- .../DESIGN.md | 219 ++++ .../ENV.json | 25 + .../RESULTS.md | 163 +++ .../runs/h5-honest.jsonl | 10 + .../runs/h5-hostile-seed1.jsonl | 37 + .../runs/model.json | 236 ++++ .../runs/ranges-head-delta-kernel.json | 1134 +++++++++++++++++ .../runs/ranges-head-q-unscaled.json | 1134 +++++++++++++++++ .../runs/ranges-head.json | 1134 +++++++++++++++++ research/operators/qwen_deltanet_m2/README.md | 20 + .../operators/qwen_deltanet_m2/evidence.json | 346 +++++ 11 files changed, 4458 insertions(+) create mode 100644 experiments/20261105-32-qwen-deltanet-m2-design/DESIGN.md create mode 100644 experiments/20261105-32-qwen-deltanet-m2-design/ENV.json create mode 100644 experiments/20261105-32-qwen-deltanet-m2-design/RESULTS.md create mode 100644 experiments/20261105-32-qwen-deltanet-m2-design/runs/h5-honest.jsonl create mode 100644 experiments/20261105-32-qwen-deltanet-m2-design/runs/h5-hostile-seed1.jsonl create mode 100644 experiments/20261105-32-qwen-deltanet-m2-design/runs/model.json create mode 100644 experiments/20261105-32-qwen-deltanet-m2-design/runs/ranges-head-delta-kernel.json create mode 100644 experiments/20261105-32-qwen-deltanet-m2-design/runs/ranges-head-q-unscaled.json create mode 100644 experiments/20261105-32-qwen-deltanet-m2-design/runs/ranges-head.json create mode 100644 research/operators/qwen_deltanet_m2/README.md create mode 100644 research/operators/qwen_deltanet_m2/evidence.json diff --git a/experiments/20261105-32-qwen-deltanet-m2-design/DESIGN.md b/experiments/20261105-32-qwen-deltanet-m2-design/DESIGN.md new file mode 100644 index 00000000..cadbf9cf --- /dev/null +++ b/experiments/20261105-32-qwen-deltanet-m2-design/DESIGN.md @@ -0,0 +1,219 @@ +# Qwen DeltaNet M2: design + +Issue #32. PREREG: [PREREG.md](PREREG.md) (first commit 84776893). Results and verdict: [RESULTS.md](RESULTS.md). +This is a design. Nothing in it is proved, and no M2 proof exists. + +The numbers are counts from `research/operators/qwen_deltanet_m2/count_model.py`: +- The recurrence part of the model reproduces the measured M1 and M1b statements exactly (RESULTS.md H1). +- The gated-norm part and the arm B layout are design counts. No builder exists to check them against. The norm is about 0.8% of the M2 head's columns (about 780 of about 100,000 per token), so an error there cannot move the verdict. + +Timings marked "estimate" are linear interpolations, not measurements. + +## 1 What M2 proves + +**One V head of one Qwen3.8 Gated DeltaNet layer, end to end, T = 16, F = 12**, on synthetic inputs of the published shapes. The statement runs from that head's in_proj outputs to its gated-norm output. All 48 V heads of a layer have the same statement with different public inputs, so the layer cost is 48 times the head cost (section 6, an extrapolation). + +The old M2 line in FEASIBILITY.md ("one full layer at exact shape, d 5,120, SwiGLU 17,408") is out of reach of the research harness. That layer is about 3.8e8 multiply-adds per token in its matrices: +- in_proj_qkv 5,120 x 10,240 +- in_proj_z 5,120 x 6,144 +- in_proj_a and in_proj_b 5,120 x 48 +- out_proj 6,144 x 5,120 +- MLP 3 x 5,120 x 17,408 + +As affine terms with public weights, that is about 70 times the whole M1b statement per token. It is also a dense matmul, an operator family FlashProve already proves in its main prover (docs/model.md sections 5 and 7). M2's job is the part that is new, so the matrices stay out (section 5). + +## 2 Statement + +Per token t < 16, the head's public inputs (all integers at 2^F unless noted): + +| name | count | what | class (verifier-enforced) | +|---|---|---|---| +| xq, xk, xv | 3 x 128 | in_proj_qkv outputs of this head's channels, before the conv | \|x\| < 2^31 | +| ar, br | 2 | in_proj_a and in_proj_b outputs | \|x\| < 2^20 | +| z | 128 | in_proj_z outputs | \|x\| < 2^20 | +| y | 128 | the gated-norm output, the statement's result | \|y\| < 2^31 | + +Per head, public and fixed by the shape, not per token: +- conv kernels (3 x 128 x 4 taps, at 2^12) +- A_log and dt_bias (folded into the decay table, section 3) +- the gated-norm gain (128) +- S0 (128 x 128, \|S0\| < 2^18) +- S_T (128 x 128, \|S_T\| < 2^18) + +Private, and committed in the product oracle: +- every state S_t +- M, delta (two limbs) +- the head output o +- the norm's scalars and w + +In M1/M1b, o was public. In M2 it is private, because the result is now y. + +## 3 Gate, conv and norm: which are proved + +| step | in M2 | why | +|---|---|---| +| causal conv1d, kernel 4, zero padding (on xq, xk, xv) | **verifier recomputes** | a function of public integers only | +| SiLU on the conv outputs | **verifier recomputes** | a function of public integers only | +| q/k L2 norm, q scale 1/sqrt(128) | **verifier recomputes** | a function of public integers only | +| decay a = exp(-A softplus(ar + dt_bias)), beta = sigmoid(br) | **verifier recomputes** | a function of public integers only | +| recurrence (M, delta, S', o) | **proved** | private state | +| gated RMSNorm of o, times SiLU(z) and gain | **proved** (norm), **verifier recomputes** cg = round(2^F gain SiLU(z) sqrt(128)) | the norm reads private o; cg is public-only | +| in_proj, out_proj, pre-norm RMSNorm, residual, MLP | **not proved in M2** | dense family, proved elsewhere; inputs to M2 are public | + +**Why recomputation is enough at this boundary.** At this statement boundary every input to conv, SiLU, L2 norm and the gates is public. A relation over them would prove that the verifier's own computation is right, and the verifier can just do that computation. The cost is O(T x 128 x 4) integer operations, about 0.2 ms at T = 16. The results enter the proof only as relation coefficients and targets (k, q, a, beta), as public values did in M1b. + +**What changes at M3.** When the in_proj outputs become private (committed by the matmul sub-proof), these steps must become in-relation gadgets: +- conv: 4 product gates per channel per token +- SiLU: a Ts-style table lookup, as docs/model.md section 7 +- L2 norm: isqrt and reciprocal, as in section 4.4 below +- gates: two table lookups per token + +By the same counting rules, that is under 10 columns per channel per token, about 4,000 per head per token. That is under 1% of the head's state work (sections 5 and 6). An optional build arm M2-R may prove them in-relation as a rehearsal. It is not needed for the M2 claim. + +**Determinism of recomputation.** The prover and verifier must derive identical integers. The design pins: +- **conv**: integer taps at 2^12; acc = sum of w_tau x_(t - tau); c = floor(acc / 2^12) +- **SiLU**: a public table on clamped keys (docs/model.md Ts form), not libm at verify time +- **L2 norm**: integer isqrt with round half up, eps = 17 at 2^2F. For q the divisor is multiplied by round(sqrt(128) x 2^12) and the numerator by 2^12. +- **a and beta**: two public tables keyed by clamp(ar) and clamp(br), built once with correctly rounded exp, as the docs/model.md tables are + +The prototype `head_reference.py` uses float64 numpy at the table points. A build lane must replace that with the tables, and add a test that two builds give byte-identical tables. + +## 4 Lowering (on the merged M1b machinery) + +### 4.1 Unchanged from M1b +- Product oracle a, b, c (toy PSC). +- Wide affine relations. +- RNG (blocks/iop/rng.h) with its LKP digit lookup, in ONE proximity-test group. +- RNG cells laid directly on product row a, so the range-checked integers are the cells the relations read. No wiring relations. +- The staged-floor recurrence (M1 README). +- delta two-limb at 2^18. + +### 4.2 RNG cell classes and sizing (hard rule b) + +Every RNG cell class has an honest contract |x| < 2^18. Each class sits in a 16-column RNG matrix with window 2D = 2^23 (b0 = 12, b1 = 11). An honest combination is therefore at most 16 x 2^18 = 2^22 = D, which is RNG's completeness condition, and RNG proves |x| < 2^23 (soundness side). + +| class | honest contract | why the contract holds | measured max (H4, 20 runs x 1,024 steps) | +|---|---|---|---| +| S' | \|S'\| < 2^18 | builder rejects otherwise (statement refused, never saturated) | 150,189 | +| M | \|M\| < 2^18 | as M1b | 53,294 | +| delta_lo, delta_hi | delta = 2^18 hi + lo, lo in [0, 2^18), \|hi\| < 2^18 | split (registered, D4) | lo 262,143 (its bound), hi 1 | +| o (new, private) | \|o\| < 2^18 | builder rejects otherwise. Nearly provable: \|o\| <= \|S'\|max x \|\|q\|\|_1 / 2^F, and \|\|q\|\|_1 <= sqrt(128) \|\|q\|\|_2 <= 2^F + 64 after rounding, so \|o\| < 2^18 (1 + 2^-6). In practice it is 40x below. | 6,379 | +| w (new) | \|w\| <= 2^12 | provable: \|w\| <= \|o\| Rc / 2^24 <= \|o\| 2^12 / sigma <= 2^12, since \|o\| <= sigma | 4,048 | + +No other class needs a split. The registered split rule (H4) did not have to fire. The M2 head has 272,384 RNG cells at T = 16: 17,024 cell rows, laid as two matrices of 16,384 and 1,024 rows. That gives 348,160 digit entries at N = 10. + +### 4.3 Remainders (hard rule c): two arms + +The M1b result showed remainder bits are 12 of the 17.4 product columns per state cell per token. The design registers two arms: + +**Arm A: exact bits** (M1b as is). A width-w remainder costs: +- w columns [x, x, x] +- 2w relations (a = b, c = a) +- w terms in the floor relation + +**Arm B: lookup-exact remainders.** +- Each 12-bit remainder (F = 12) is ONE product cell [r, 0, 0] in a key region of row a. It is a direct key of a second LKP instance into the public range table {0, .., 2^12 - 1}: LkpTablePart with empty rows, one function, unshifted. +- A 24-bit remainder (the M floor, 2^2F) is two such cells, r = lo + 2^12 hi. Both terms go directly into the floor relation, so no extra relation is needed. +- An exact interval [0, 2^12) on each limb gives r in [0, 2^24) exactly, so floor uniqueness is unchanged from M1b. + +This is an exact range proof, not RNG's approximate one, so the floor-uniqueness argument of M1b goes through as is. The keys come in parts (LkpPublic::parts, allowed for one unshifted function). The key region is laid like RNG's cells, and LKP's key claim becomes a laid_form on message 0, as `rng_key_row` does for the digits. The multiplicities are one extra message with about 20 planes x 2^12 entries. + +**Savings (count model, H2 and H3):** + +| | arm A | arm B | B / A | +|---|---|---|---| +| M1b shape (T = 4, public o, no norm): product columns per state cell per token | 17.41 | 6.07 | **0.349** | +| same: relations | 2,231,808 | 609,792 | **0.273** | +| same: terms | 5,174,272 | 1,186,816 | 0.229 | +| same: table log-length | 21 | 19 | | +| M2 head (T = 16, private o, norm): gate columns + range cells + keys | 4,649,440 | 1,605,872 | 0.345 | +| same: relations | 9,042,944 | 2,402,432 | 0.266 | +| same: table log-length | **23 (does not fit 2^22)** | **21** | | +| largest T at log-length <= 22 | 14 | 41 | | + +Arm B removes, per remainder bit, one gate column and two relations. In their place it adds one key per 12 bits plus a small fixed multiplicity message. That is about 2.9x fewer product columns and about 3.7x fewer relations. **Arm A cannot hold M2 at T = 16 in the 2^22 table. Arm B can, and holds it at the M1b table size (2^21).** Arm B is therefore the primary arm for M2. Arm A at T = 8 is the fallback if arm B's build fails. + +### 4.4 Gated RMSNorm (new, proved) + +Per token, with Vx = sum of o_j^2 (exact, in the relation) and eps' = round(128 x 1e-6 x 2^2F) = 2,147: + +``` +sigma = isqrt(Vx + eps') e1 = Vx + eps' - sigma^2 >= 0, e2 = sigma^2 + 2 sigma - Vx - eps' >= 0 +Rc = floor(2^36 / sigma) rr = 2^36 - Rc sigma >= 0, sigma - 1 - rr >= 0 +w_j = floor(o_j Rc / 2^24) remainder in [0, 2^24) +y_j = floor(cg_j w_j / 2^F) remainder in [0, 2^F); cg_j public (section 3); y_j public +``` + +The sigma here is the root of the sum, not of the mean. The sqrt(128) goes into cg, as docs/model.md section 5 puts it into the weights. + +**Gates per token:** +- sigma x sigma, Rc x sigma +- 128 x (o x o), 128 x (o x Rc) + +**Exact limbs per token** (arm B keys / arm A bits): sigma 2, e1 2, e2 2, Rc 3, rr 2, gap 2, and per j: one 24-bit remainder (2) and one 12-bit remainder (1). + +**Uniqueness.** The bounds are provable from the cell contracts (|o| < 2^18, so Vx < 2^43, sigma < 2^22, e1, e2 <= 2 sigma < 2^23, Rc < 2^36). e1 >= 0 and e2 >= 0 pin sigma = isqrt. rr in [0, sigma) pins Rc. Each floor has an exact remainder. + +**No wrap.** RNG proves |o| < 2^23. The largest magnitudes are then: +- sum of o^2 < 2^53 +- o Rc < 2^59 +- cg w < 2^47 + +All are below p. + +The H4 range suite found no exact limb outside its interval and no negative exact quantity in 20/20 runs. + +### 4.5 Verifier entry point (hard rule a) + +`verify_m2(shape_constants, statement_words, proof_bytes)` is the only entry. The prototype is `verify_from_words` in `research/operators/qwen_deltanet_m2/admit_proto.cpp`, on the M1b statement. It runs these steps: +1. Fix the shape from the verifier's own constants. The header words must equal them, and the word count must be exact. +2. Decode every public word as a canonical Goldilocks element (< p), read it as a signed integer (a word above p/2 is negative), and check it against its class bound (section 2). A non-canonical word, a huge element, or an integer outside its class is rejected **here**. +3. Recompute the public-only stage (section 3) from the decoded integers. +4. Derive every relation target and coefficient from those integers with the relation-only builder. +5. Size the run (a pure function of the shape) and run the unchanged proof verifier. + +There is no `admissible()` that a caller runs separately, and no entry point that accepts caller-built relations or a caller-built `LookupRun`. The low-level `lookup_verify(run, ...)` stays a library internal. **The M2 build must not export it as a verifier.** + +The prototype measured this on the M1b statement (RESULTS.md H5): +- 10/10 honest seeds accepted. +- 34/34 hostile encodings rejected at the expected stage, including a huge field element in each of the 8 public classes and the #63 review's R9. +- For R9, an honest proof of the statement with o' = o - 2^-F mod p is **accepted** by the current path, `lookup_verify` with caller-built relations and `admissible()` skipped. The new entry **rejects** it at decode. + +## 5 What stays unproved in M2 + +- in_proj_qkv, in_proj_z, in_proj_a/b and out_proj (the matmuls) +- the layer's pre-norm RMSNorm and residual +- the SwiGLU MLP +- the full-attention layers +- the link from one layer to the next + +In M2 the in_proj outputs are public statement values. Nothing in M2 shows they came from a given x and weights. That is M3's link (a matmul sub-proof whose outputs are the committed inputs of the M2 relation, under the shared-wire or same-oracle tie of research/operators/shared_wires). Conv, SiLU, L2 norm and gates move from "verifier recomputes" to "proved" at that point (section 3). + +## 6 Cost (counts exact; time estimates indicative) + +| | arm A | arm B | +|---|---|---| +| M2 head, T = 16: product table | 2^23 (over) | 2^21 | +| relations | 9.04M | 2.40M | +| RNG digit entries | 348,160 | 348,160 | +| LKP keys (remainders) | 0 | 276,688 | +| prove, estimate (linear in table length from #47 / #63) | n/a | about 39 s | +| peak RSS, estimate (M1b at 2^21: 4.4 GB) | n/a | about 4.5 GB | +| per token, one layer (48 heads): relations | 27.1M | 7.21M | +| per token, one layer: product columns + cells + keys | 13.9M | 4.82M | +| per token, 48 DeltaNet layers: product columns + cells + keys | 670M | 231M | + +The last row is the number M3 must beat with chunked state commitment or streaming. FEASIBILITY.md's per-token state alone is 37.7M cells per token. Arm B's 231M is 6.1 product cells per state cell. That fits no single-host table at T = 1,024 without chunking. M2 does not resolve this, and is not meant to. + +## 7 M2 build preregistration sketch (for the build lane, not run here) + +The build lane must keep these, and register numbers in its own PREREG before code: +- **H1 honest:** 10/10 at T = 16 (seeds 1..10, the H4 generator, R1). +- **H2 hostile:** M1b's 44 classes ported, plus: + - o tamper with its floor preserved + - the norm: sigma +1 with e1/e2 shifted, Rc +1 with rr shifted, w +1 with its remainder shifted + - one remainder key off-table (arm B: key 2^12, which only a hostile LKP prover can send) + - the limb re-split +- **H3 range:** H4 of this lane re-run on the build's reference, 20/20. +- **H4 admission:** this lane's H5 suite on the M2 statement words, plus a huge element in xq, ar and z, and the R9 analogue on y. All rejected by `verify_m2` alone. +- **Kill:** no honest T = 16 arm B proof in 6 h, then fall back to arm A at T = 8 and report. diff --git a/experiments/20261105-32-qwen-deltanet-m2-design/ENV.json b/experiments/20261105-32-qwen-deltanet-m2-design/ENV.json new file mode 100644 index 00000000..d5e9d50b --- /dev/null +++ b/experiments/20261105-32-qwen-deltanet-m2-design/ENV.json @@ -0,0 +1,25 @@ +{ + "experiment": "20261105-32-qwen-deltanet-m2-design", + "kind": "design + CPU prototypes for Qwen DeltaNet M2: counting model (checked vs M1/M1b), whole-head integer range reference, verifier-side admission prototype on the M1b statement; synthetic inputs, no weights, no GPU", + "commits": { + "flashprove_base": "b6186e6ccb687348e3be66fec51bb3756879b8dc", + "prereg": "84776893", + "prototypes": "a8cdf660", + "m1b_lookup_pr63": "research/operators/qwen_deltanet_lookup/ on main (lookup.h unchanged)" + }, + "toolchain": { + "gxx": "g++ (Ubuntu 13.3.0-6ubuntu2~24.04.1) 13.3.0, -std=c++17 -O2 -fno-ipa-modref -DBLK_FIELD=2 -DBLK_HASH=1", + "python": "3.12.3", + "numpy": "2.4.3" + }, + "hardware": { + "cpu": "AMD EPYC-Rome, 16 vCPU (shared, loaded VM); runs pinned to 6 cores (taskset -c 0-5); the CPU prover is single-threaded", + "ram_gb": 30, + "os": "Linux 6.8 x86_64", + "gpu": "none" + }, + "proof_parameters": "as #63: Goldilocks, target 10 bits, 6 work bits on QUERY, ToyCoins(100); log-length 21, stages {1,1,21,21},{0,0,21,21}; RNG digits 12+11 bits (2D = 2^23), 16 columns, N = 10", + "seeds": {"h5_honest": [1,2,3,4,5,6,7,8,9,10], "h5_hostile": [1], "h4_ranges": {"dists": ["R1","R2","R3","R4"], "seeds": [1,2,3,4,5], "steps": 1024, "generator_seed": "1000 + seed"}}, + "runs": {"h5_honest": 10, "h5_hostile": 34, "h4_range_runs": 20, "h4_secondary_runs": 40, "model": 1}, + "raw": ["runs/h5-honest.jsonl", "runs/h5-hostile-seed1.jsonl", "runs/model.json", "runs/ranges-head.json", "runs/ranges-head-q-unscaled.json", "runs/ranges-head-delta-kernel.json", "../../research/operators/qwen_deltanet_m2/evidence.json"] +} diff --git a/experiments/20261105-32-qwen-deltanet-m2-design/RESULTS.md b/experiments/20261105-32-qwen-deltanet-m2-design/RESULTS.md new file mode 100644 index 00000000..797878e7 --- /dev/null +++ b/experiments/20261105-32-qwen-deltanet-m2-design/RESULTS.md @@ -0,0 +1,163 @@ +# RESULTS: 20261105-32-qwen-deltanet-m2-design + +Issue: #32. PR: #66 (draft). PREREG: 84776893, the first commit on the branch. Design: [DESIGN.md](DESIGN.md). +All numbers are **measured or counted on one host, unreplicated**. No M2 proof was built: this is a design lane. + +**Verdict against the registered threshold: PASS on H1 to H5, so GO for an M2 build lane**, with arm B (lookup-exact remainders) as the primary arm. One registered item reads as a design constraint: arm A cannot hold the M2 head at T = 16 in a 2^22 table. + +Code: `research/operators/qwen_deltanet_m2/` +- `admit_proto.cpp` + `m1b_builder.h` (H5) +- `count_model.py` (H1 to H3) +- `head_reference.py` (H4) +- `record.py` regenerates everything; `evidence.json` holds commands, exit codes, timings and source SHA-256s. + +Raw data in `runs/`: +- `h5-honest.jsonl` +- `h5-hostile-seed1.jsonl` +- `model.json` +- `ranges-head.json` (registered) +- `ranges-head-q-unscaled.json` and `ranges-head-delta-kernel.json` (secondary, unregistered) + +## H1: counting model reproduces M1 and M1b (threshold: exact) + +| | model | measured | match | +|---|---|---|---| +| M1 product columns | 2,338,816 | 2,338,816 (#47) | yes | +| M1 relations | 4,761,088 | 4,761,088 | yes | +| M1 terms | 11,430,400 | 11,430,400 | yes | +| M1 log-length | 22 | 22 | yes | +| M1b columns laid out (aligned region) | 1,310,720 | 1,310,720 (#63) | yes | +| M1b non-region columns | 1,074,176 | 1,074,176 | yes | +| M1b relations | 2,231,808 | 2,231,808 | yes | +| M1b terms | 5,174,272 | 5,174,272 | yes | +| M1b range cells | 67,072 | 67,072 | yes | +| M1b log-length | 21 | 21 | yes | + +**H1: PASS.** The recurrence counts are exact. Two parts have no builder to check against yet: the gated-norm counts (about 0.8% of M2's columns) and the arm-B layout (key parts, region alignment). Those are design counts. + +## H2: lookup-exact remainders at the M1b shape (threshold: B / A <= 0.5 on columns per cell and on relations) + +| | arm A (exact bits = M1b) | arm B (direct LKP keys, 2^12 table) | B / A | +|---|---|---|---| +| product columns + range cells + keys, per state cell per token | 17.41 | 6.07 | **0.349** | +| relations | 2,231,808 | 609,792 | **0.273** | +| terms | 5,174,272 | 1,186,816 | 0.229 | +| table log-length | 21 | 19 | | + +**H2: PASS.** The saving is about 11.3 product columns and about 24 relations per state cell per token. This answers hard rule (c): lookup-exact remainders are worth about 2.9x on columns and 3.7x on relations. + +## H3: the M2 head statement at T = 16 (threshold: arm B log-length <= 22) + +| | arm A | arm B | +|---|---|---| +| gate columns | 4,377,056 | 1,056,800 | +| RNG range cells | 272,384 | 272,384 | +| LKP remainder keys | 0 | 276,688 | +| relations | 9,042,944 | 2,402,432 | +| terms | 21,110,960 | 4,786,704 | +| table log-length | **23** | **21** | +| largest T with log-length <= 22 | 14 | 41 | + +Layer extrapolation (48 V heads, per token): + +| | arm A | arm B | +|---|---|---| +| relations | 27.1M | 7.21M | +| columns + cells + keys | 13.9M | 4.82M | + +For all 48 DeltaNet layers, per token, the columns + cells + keys are 670M (arm A) and 231M (arm B). This is an extrapolation, not a measurement. + +The arm-B prove time at 2^21 is estimated at about 39 s, by linear interpolation from #47 and #63. That is an estimate. + +**H3: PASS for arm B.** Arm A at T = 16 does not fit 2^22. That is descriptive under the PREREG and is a constraint on the build (fallback: arm A at T <= 14, e.g. T = 8). + +## H4: ranges of every cell class, whole head (threshold: 20/20) + +Setup: integer reference of the whole head, R1..R4 x seeds 1..5 x 1,024 steps, conv taps U(-0.5, 0.5), 14.5 s CPU. + +**Every class fit in 20/20 runs.** + +| class | contract | max over 20 runs | +|---|---|---| +| S' | < 2^18 (262,144) | 150,189 | +| M | < 2^18 | 53,294 | +| delta | split at 2^18 | 282,387 (split needed in 1/20 runs; hi <= 1) | +| o (private, new) | < 2^18 | 6,379 | +| w (norm output, new) | <= 2^12 | 4,048 | +| sigma | < 2^24 (2 limbs) | 6,454 | +| e1, e2 | [0, 2^24) | 12,148 / 7,253, min 0 | +| Rc | < 2^36 (3 limbs) | 1,431,655,765 (< 2^31) | +| rr, sigma - 1 - rr | [0, 2^24) | 4,264 / 4,583, min 0 | +| w and y floor remainders | [0, 2^24) / [0, 2^12) | min 0 | +| public v (post-conv) | < 2^31 | 527,711 | +| public y | < 2^31 | 101,976 | +| public cg | < 2^24 | 230,259 | + +**H4: PASS.** No new limb split was needed. + +Divergence from float64: +- **State: 0.040 worst.** M1b's was 0.032. +- **Gated-norm output y: 1.23 worst (absolute, y up to about 25 in real units).** + +This y number is a fidelity finding, not a range one. The integer o is small: the median run's max |o| is 853 and early-token rms(o) is 2 to 60 integer units at 2^F, because q carries 1/sqrt(128) and the decayed state is small. So o has a 1-ulp relative error of up to about 15%, and the scale-free norm amplifies it into y. The norm step itself adds under 0.01 (checked by applying the same norm to float and integer o). + +**Design implication (not registered, secondary run):** compute o from the unscaled q (the norm is scale-invariant, so 1/sqrt(128) moves into eps x 128). This raises o about 11x in integer units: +- max |o| is 72,479, still < 2^18 +- 20/20 fit +- worst y divergence is 1.19, unchanged, because the worst cases are early tokens with near-zero state + +So y fidelity needs a larger F for o, or a rule for near-zero rms. **Fidelity is owned by M0/#8. M2 range soundness is unaffected.** A delta-kernel stress run (conv = pass-through) also fit 20/20, with max |delta| 582,625 and hi <= 2. + +## H5: verifier-side admission (threshold: 10/10 honest, 100% hostile, R9 control accepted) + +This uses `verify_from_words` on the M1b statement (D = 128, T = 4) with the merged lookup.h unchanged. It decodes 34,827 words (each canonical, each in its class), derives the relations, sizes, then verifies. Runs were pinned to 6 cores. + +| metric | n | median | SD | min | max | +|---|---|---|---|---|---| +| accepted | 10 | **10/10** | | | | +| decode (s) | 10 | 0.0001 | 0 | 0.0001 | 0.0001 | +| derive relations + size (s) | 10 | 0.182 | 0.007 | 0.170 | 0.196 | +| proof verify (s) | 10 | 7.61 | 0.11 | 7.53 | 7.86 | +| prove (s) | 10 | 39.91 | 0.24 | 39.64 | 40.48 | +| proof bytes | 10 | 25,411,392 | 313 | 25,410,848 | 25,411,968 | +| peak RSS (KB) | 10 | 4,433,912 | 54 | 4,433,784 | 4,433,912 | + +Admission adds about 0.18 s (2.4%) to a 7.6 s verify. The proof bytes equal #63's to the byte. + +Hostile, seed 1: **34/34 at the expected stage.** + +| class | what | result | +|---|---|---| +| huge-element | each of S0, a, beta, k, v, q, o, S_T set to (p + 1) / 2 | 8/8 rejected at decode | +| class-bound | each class at the first integer outside, on both sides | 16/16 rejected at decode | +| non-canonical | a_0 + p, o_0[0] + p (second encoding of the same element) | 2/2 rejected at decode | +| header / length | T = 5, F = 10, one word extra, one word short | 4/4 rejected at decode | +| in-class (control) | o_T[0] + 1, S0[0] + 1 (valid integers, wrong) | 2/2 pass decode, rejected by the proof verifier | +| **R9 control** | o' = o - 2^-F mod p, honest proof of the changed relation (0 violated relations, remainder r + 1), checked by the CURRENT path (`lookup_verify` with caller-built relations, `admissible()` skipped) | **ACCEPTED** (as expected: the hole is real) | +| **R9** | the same statement and proof through `verify_from_words` | **rejected at decode** | + +**H5: PASS.** This answers hard rule (a): the entry point range-checks its own integer inputs, derives targets from them, and rejects a huge public field element on its own. + +## Kill criterion + +Not triggered. The lane started at 14:33Z. H5 honest accept came at about 15:10Z, and all results were in by 15:30Z. Peak RSS was 4.4 GB, and free disk stayed above 30 GB. + +## Deviations from PREREG + +- **H1.** The registered comparison quantity for M1b "columns used" was 1,141,248 in the PREREG text, which is non-region plus range cells. The model matches both the measured laid-out length 1,310,720 and the non-region count 1,074,176; 1,141,248 = 1,074,176 + 67,072 also holds. No threshold moves. +- **H5.** The class-bound test was registered as "bound + 1 for one-sided". It ran both sides for every class (16 cases instead of 8), which is stricter. +- **H4.** The PREREG listed the float divergence of y as a metric without a threshold. It is reported, and it is large (see H4). The q-unscaled and delta-kernel runs are secondary and unregistered. +- No amendments. + +## Open risks + +- **No M2 proof exists.** Arm B needs a second LKP instance whose keys are product-oracle cells. LKP supports parts and range tables, and RNG already composes a lookup whose key claim is a laid form on a sent message. But a lookup keyed directly on message-0 cells has not been built in this repo. That is the build lane's main risk. +- **Verifier-recomputed nonlinearities must be bit-exact** across prover and verifier. The prototype uses float64 numpy at table points. The build needs pinned public tables (DESIGN.md section 3). +- **y fidelity is poor at F = 12** (worst 1.2 absolute) because o is small in integer units. This is a fidelity issue for M0/#8, not soundness, but it argues for a finer o scale before any fidelity claim. +- **The layer extrapolation is 48x one head.** Per-token cost for 48 layers (231M product cells, arm B) means M3 needs chunked state commitment. M2 does not address that. +- The count model's norm and arm-B layout are design counts, unchecked by a builder. Same host class, research security (N = 10), hostile classes sampled, unreplicated. + +## Proposed claims (PR body only, not in claims.json) + +- On the M1b statement, a verifier entry point that admits its own integer public inputs costs about 0.18 s and closes the R9 hole: a huge public field element is accepted by the current caller path and rejected by the new entry alone. Measured, unreplicated. +- Lookup-exact remainders would cut the M1b statement's product columns per state cell per token from 17.4 to 6.1 and its relations by 3.7x. Counted, not built. diff --git a/experiments/20261105-32-qwen-deltanet-m2-design/runs/h5-honest.jsonl b/experiments/20261105-32-qwen-deltanet-m2-design/runs/h5-honest.jsonl new file mode 100644 index 00000000..04495e1a --- /dev/null +++ b/experiments/20261105-32-qwen-deltanet-m2-design/runs/h5-honest.jsonl @@ -0,0 +1,10 @@ +{"seed": 1, "exit": 0, "wall": 48.517, "mode": "honest", "accept": true, "stage": "accept", "statement_words": 34827, "proof_bytes": 25411968, "prove_s": 39.878, "decode_s": 0.0001, "derive_s": 0.176, "verify_s": 7.534, "peak_rss_kb": 4433912, "stderr": ""} +{"seed": 2, "exit": 0, "wall": 48.728, "mode": "honest", "accept": true, "stage": "accept", "statement_words": 34827, "proof_bytes": 25411232, "prove_s": 40.057, "decode_s": 0.0001, "derive_s": 0.17, "verify_s": 7.562, "peak_rss_kb": 4433908, "stderr": ""} +{"seed": 3, "exit": 0, "wall": 48.674, "mode": "honest", "accept": true, "stage": "accept", "statement_words": 34827, "proof_bytes": 25411424, "prove_s": 39.729, "decode_s": 0.0001, "derive_s": 0.18, "verify_s": 7.78, "peak_rss_kb": 4433912, "stderr": ""} +{"seed": 4, "exit": 0, "wall": 49.491, "mode": "honest", "accept": true, "stage": "accept", "statement_words": 34827, "proof_bytes": 25411648, "prove_s": 40.478, "decode_s": 0.0001, "derive_s": 0.184, "verify_s": 7.861, "peak_rss_kb": 4433912, "stderr": ""} +{"seed": 5, "exit": 0, "wall": 48.863, "mode": "honest", "accept": true, "stage": "accept", "statement_words": 34827, "proof_bytes": 25411360, "prove_s": 39.965, "decode_s": 0.0001, "derive_s": 0.183, "verify_s": 7.672, "peak_rss_kb": 4433784, "stderr": ""} +{"seed": 6, "exit": 0, "wall": 48.801, "mode": "honest", "accept": true, "stage": "accept", "statement_words": 34827, "proof_bytes": 25411488, "prove_s": 39.946, "decode_s": 0.0001, "derive_s": 0.181, "verify_s": 7.664, "peak_rss_kb": 4433912, "stderr": ""} +{"seed": 7, "exit": 0, "wall": 48.451, "mode": "honest", "accept": true, "stage": "accept", "statement_words": 34827, "proof_bytes": 25411200, "prove_s": 39.768, "decode_s": 0.0001, "derive_s": 0.177, "verify_s": 7.551, "peak_rss_kb": 4433912, "stderr": ""} +{"seed": 8, "exit": 0, "wall": 48.823, "mode": "honest", "accept": true, "stage": "accept", "statement_words": 34827, "proof_bytes": 25411168, "prove_s": 39.948, "decode_s": 0.0001, "derive_s": 0.183, "verify_s": 7.667, "peak_rss_kb": 4433784, "stderr": ""} +{"seed": 9, "exit": 0, "wall": 48.424, "mode": "honest", "accept": true, "stage": "accept", "statement_words": 34827, "proof_bytes": 25411648, "prove_s": 39.772, "decode_s": 0.0001, "derive_s": 0.196, "verify_s": 7.527, "peak_rss_kb": 4433912, "stderr": ""} +{"seed": 10, "exit": 0, "wall": 48.345, "mode": "honest", "accept": true, "stage": "accept", "statement_words": 34827, "proof_bytes": 25410848, "prove_s": 39.636, "decode_s": 0.0001, "derive_s": 0.187, "verify_s": 7.55, "peak_rss_kb": 4433908, "stderr": ""} diff --git a/experiments/20261105-32-qwen-deltanet-m2-design/runs/h5-hostile-seed1.jsonl b/experiments/20261105-32-qwen-deltanet-m2-design/runs/h5-hostile-seed1.jsonl new file mode 100644 index 00000000..f9f42e11 --- /dev/null +++ b/experiments/20261105-32-qwen-deltanet-m2-design/runs/h5-hostile-seed1.jsonl @@ -0,0 +1,37 @@ +{"mode":"honest","accept":true,"stage":"accept","statement_words":34827,"proof_bytes":25411968,"prove_s":38.929,"decode_s":0.0001,"derive_s":0.191,"verify_s":7.656,"peak_rss_kb":4433912} +{"mode":"hostile","class":"huge-element","attack":"S0 = (p+1)/2","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"class-bound","attack":"S0 = first value outside class (+)","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"class-bound","attack":"S0 = first value outside class (-)","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"huge-element","attack":"a = (p+1)/2","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"class-bound","attack":"a = first value outside class (+)","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"class-bound","attack":"a = first value outside class (-)","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"huge-element","attack":"beta = (p+1)/2","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"class-bound","attack":"beta = first value outside class (+)","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"class-bound","attack":"beta = first value outside class (-)","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"huge-element","attack":"k = (p+1)/2","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"class-bound","attack":"k = first value outside class (+)","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"class-bound","attack":"k = first value outside class (-)","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"huge-element","attack":"v = (p+1)/2","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"class-bound","attack":"v = first value outside class (+)","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"class-bound","attack":"v = first value outside class (-)","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"huge-element","attack":"q = (p+1)/2","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"class-bound","attack":"q = first value outside class (+)","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"class-bound","attack":"q = first value outside class (-)","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"huge-element","attack":"o = (p+1)/2","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"class-bound","attack":"o = first value outside class (+)","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"class-bound","attack":"o = first value outside class (-)","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"huge-element","attack":"S_T = (p+1)/2","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"class-bound","attack":"S_T = first value outside class (+)","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"class-bound","attack":"S_T = first value outside class (-)","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"non-canonical","attack":"a_0 word = a_0 + p (second encoding of the same element)","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"non-canonical","attack":"o_0[0] word = o_0[0] + p","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"header","attack":"T word = 5","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"header","attack":"F word = 10","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"length","attack":"one trailing word","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"length","attack":"one word short","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"in-class","attack":"o_T[0] += 1 (valid integer, wrong)","rejected":true,"stage":"proof","expected_stage":"proof","as_expected":true} +{"mode":"hostile","class":"in-class","attack":"S0[0] += 1 (valid integer, wrong)","rejected":true,"stage":"proof","expected_stage":"proof","as_expected":true} +{"mode":"r9-detail","j":0,"o":-1,"o_prime_word":"4503599626321919","violated_relations":0} +{"mode":"hostile","class":"r9-control","attack":"current path: lookup_verify with caller-built relations, admissible() skipped","rejected":false,"stage":"accept","expected_stage":"accept","as_expected":true} +{"mode":"hostile","class":"r9","attack":"verify_from_words on the R9 statement and R9 proof","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile-summary","as_expected":34,"total":34,"peak_rss_kb":5032968} diff --git a/experiments/20261105-32-qwen-deltanet-m2-design/runs/model.json b/experiments/20261105-32-qwen-deltanet-m2-design/runs/model.json new file mode 100644 index 00000000..e3894004 --- /dev/null +++ b/experiments/20261105-32-qwen-deltanet-m2-design/runs/model.json @@ -0,0 +1,236 @@ +{ + "H1": { + "checks": { + "m1": { + "columns_used": [ + 2338816, + 2338816, + true + ], + "log_length": [ + 22, + 22, + true + ], + "relations": [ + 4761088, + 4761088, + true + ], + "terms": [ + 11430400, + 11430400, + true + ], + "range_cells": [ + 0, + 0, + true + ] + }, + "m1b": { + "columns_used": [ + 1310720, + 1310720, + true + ], + "columns_nonregion": [ + 1074176, + 1074176, + true + ], + "log_length": [ + 21, + 21, + true + ], + "relations": [ + 2231808, + 2231808, + true + ], + "terms": [ + 5174272, + 5174272, + true + ], + "range_cells": [ + 67072, + 67072, + true + ] + } + }, + "pass_": true, + "model_m1": { + "arm": "m1", + "D": 128, + "T": 4, + "o_private": false, + "norm": false, + "gate_columns": 2338816, + "range_cells": 0, + "lkp_keys": 0, + "relations": 4761088, + "terms": 11430400, + "product_columns_per_state_cell_per_token": 35.688, + "relations_per_state_cell_per_token": 72.648, + "columns_used": 2338816, + "layout": 2338816, + "log_length": 22 + }, + "model_m1b": { + "arm": "m1b", + "D": 128, + "T": 4, + "o_private": false, + "norm": false, + "gate_columns": 1074176, + "range_cells": 67072, + "lkp_keys": 0, + "relations": 2231808, + "terms": 5174272, + "product_columns_per_state_cell_per_token": 17.414, + "relations_per_state_cell_per_token": 34.055, + "columns_used": 1310720, + "columns_nonregion": 1074176, + "rng_rows": 8192, + "log_length": 21 + } + }, + "H2": { + "armA_m1b_shape": { + "arm": "A", + "D": 128, + "T": 4, + "o_private": false, + "norm": false, + "gate_columns": 1074176, + "range_cells": 67072, + "lkp_keys": 0, + "relations": 2231808, + "terms": 5174272, + "product_columns_per_state_cell_per_token": 17.414, + "relations_per_state_cell_per_token": 34.055, + "columns_used": 1156096, + "columns_nonregion": 1074176, + "rng_rows": [ + 4096, + 1024 + ], + "key_parts": [], + "digit_entries": 102400, + "log_length": 21 + }, + "armB_m1b_shape": { + "arm": "B", + "D": 128, + "T": 4, + "o_private": false, + "norm": false, + "gate_columns": 263168, + "range_cells": 67072, + "lkp_keys": 67584, + "relations": 609792, + "terms": 1186816, + "product_columns_per_state_cell_per_token": 6.07, + "relations_per_state_cell_per_token": 9.305, + "columns_used": 427008, + "columns_nonregion": 263168, + "rng_rows": [ + 4096, + 1024 + ], + "key_parts": [ + 65536, + 16384 + ], + "digit_entries": 102400, + "log_length": 19 + }, + "ratio_columns_per_cell": 0.349, + "ratio_relations": 0.273, + "ratio_terms": 0.229, + "pass_": true, + "note": "arm A at the M1b shape equals M1b (same lowering); arm B swaps exact bits for direct LKP keys" + }, + "H3": { + "armA_T16": { + "arm": "A", + "D": 128, + "T": 16, + "o_private": true, + "norm": true, + "gate_columns": 4377056, + "range_cells": 272384, + "lkp_keys": 0, + "relations": 9042944, + "terms": 21110960, + "product_columns_per_state_cell_per_token": 17.736, + "relations_per_state_cell_per_token": 34.496, + "columns_used": 4655584, + "columns_nonregion": 4377056, + "rng_rows": [ + 16384, + 1024 + ], + "key_parts": [], + "digit_entries": 348160, + "log_length": 23, + "prove_s_estimate": null + }, + "armB_T16": { + "arm": "B", + "D": 128, + "T": 16, + "o_private": true, + "norm": true, + "gate_columns": 1056800, + "range_cells": 272384, + "lkp_keys": 276688, + "relations": 2402432, + "terms": 4786704, + "product_columns_per_state_cell_per_token": 6.126, + "relations_per_state_cell_per_token": 9.165, + "columns_used": 1613856, + "columns_nonregion": 1056800, + "rng_rows": [ + 16384, + 1024 + ], + "key_parts": [ + 262144, + 16384 + ], + "digit_entries": 348160, + "log_length": 21, + "prove_s_estimate": 39.2 + }, + "max_T_log22": { + "A": 14, + "B": 41 + }, + "layer_extrapolation": { + "A": { + "relations_per_token_per_layer": 27128832, + "columns_per_token_per_layer": 13948320, + "columns_per_token_48_deltanet_layers": 669519360 + }, + "B": { + "relations_per_token_per_layer": 7207296, + "columns_per_token_per_layer": 4817616, + "columns_per_token_48_deltanet_layers": 231245568 + } + }, + "pass_": true, + "note": "prove_s_estimate is linear in table length from #47/#63 points; indicative only. Layer extrapolation = 48 V heads x per-head-per-token; DeltaNet recurrence + gated norm only (no matmuls)." + }, + "remainder_savings": { + "armA_T16_columns": 4649440, + "armB_T16_columns": 1605872, + "relations_A": 9042944, + "relations_B": 2402432, + "terms_A": 21110960, + "terms_B": 4786704 + } +} diff --git a/experiments/20261105-32-qwen-deltanet-m2-design/runs/ranges-head-delta-kernel.json b/experiments/20261105-32-qwen-deltanet-m2-design/runs/ranges-head-delta-kernel.json new file mode 100644 index 00000000..0aad2423 --- /dev/null +++ b/experiments/20261105-32-qwen-deltanet-m2-design/runs/ranges-head-delta-kernel.json @@ -0,0 +1,1134 @@ +{ + "kernel": "delta", + "q_unscaled": false, + "steps": 1024, + "n": 20, + "fit": 20, + "max": { + "S": 200463, + "M": 137315, + "delta": 582625, + "delta_lo": 262143, + "delta_hi": 2, + "o": 8271, + "w": 3928, + "v": 1022655, + "k_hat": 2743, + "q_hat": 260, + "xin": 1022655, + "y": 98012, + "cg": 227005, + "sigma": 8875, + "e1": 13545, + "e2": 16637, + "Rc": 1347440720, + "rr": 6759, + "gap": 7166, + "Vx": 78764591 + }, + "worst_div_S": 0.02597577748053581, + "worst_div_y": 0.5987994346756061, + "seconds": 14.5, + "runs": [ + { + "seed": 1, + "dist": "R1", + "steps": 1024, + "kernel": "delta", + "max": { + "S": 5424, + "M": 5259, + "delta": 12392, + "delta_lo": 262143, + "delta_hi": 1, + "o": 413, + "w": 1524, + "v": 18125, + "k_hat": 2547, + "q_hat": 213, + "xin": 18411, + "y": 33385, + "cg": 190996, + "sigma": 1403, + "e1": 2435, + "e2": 2679, + "Rc": 636291451, + "rr": 1186, + "gap": 1279, + "Vx": 1966791 + }, + "min_exact": { + "e1": 0, + "e2": 0, + "rr": 0, + "gap": 0, + "rw": 0, + "ry": 0 + }, + "fit": { + "rng_cells": true, + "delta_split_needed": false, + "sigma_24": true, + "e1_24": true, + "e2_24": true, + "Rc_36": true, + "rr_24": true, + "gap_24": true, + "rw_24": true, + "ry_12": true, + "public_v_31": true, + "public_y_31": true, + "public_cg_24": true, + "public_x_31": true + }, + "all_fit": true, + "div_S": 0.008299300155050336, + "div_y": 0.3325357395140931 + }, + { + "seed": 2, + "dist": "R1", + "steps": 1024, + "kernel": "delta", + "max": { + "S": 5850, + "M": 4562, + "delta": 14190, + "delta_lo": 262143, + "delta_hi": 1, + "o": 365, + "w": 1751, + "v": 17941, + "k_hat": 2362, + "q_hat": 209, + "xin": 18966, + "y": 37920, + "cg": 206901, + "sigma": 1094, + "e1": 1993, + "e2": 1884, + "Rc": 536870912, + "rr": 944, + "gap": 958, + "Vx": 1195203 + }, + "min_exact": { + "e1": 0, + "e2": 0, + "rr": 0, + "gap": 0, + "rw": 0, + "ry": 0 + }, + "fit": { + "rng_cells": true, + "delta_split_needed": false, + "sigma_24": true, + "e1_24": true, + "e2_24": true, + "Rc_36": true, + "rr_24": true, + "gap_24": true, + "rw_24": true, + "ry_12": true, + "public_v_31": true, + "public_y_31": true, + "public_cg_24": true, + "public_x_31": true + }, + "all_fit": true, + "div_S": 0.004622403008962175, + "div_y": 0.3413020711832724 + }, + { + "seed": 3, + "dist": "R1", + "steps": 1024, + "kernel": "delta", + "max": { + "S": 5949, + "M": 4646, + "delta": 12848, + "delta_lo": 262143, + "delta_hi": 1, + "o": 398, + "w": 1667, + "v": 20352, + "k_hat": 2318, + "q_hat": 260, + "xin": 21058, + "y": 41150, + "cg": 204422, + "sigma": 1351, + "e1": 2243, + "e2": 2676, + "Rc": 838042399, + "rr": 1149, + "gap": 1071, + "Vx": 1823080 + }, + "min_exact": { + "e1": 0, + "e2": 0, + "rr": 0, + "gap": 0, + "rw": 0, + "ry": 0 + }, + "fit": { + "rng_cells": true, + "delta_split_needed": false, + "sigma_24": true, + "e1_24": true, + "e2_24": true, + "Rc_36": true, + "rr_24": true, + "gap_24": true, + "rw_24": true, + "ry_12": true, + "public_v_31": true, + "public_y_31": true, + "public_cg_24": true, + "public_x_31": true + }, + "all_fit": true, + "div_S": 0.007478557291233853, + "div_y": 0.24587317248009843 + }, + { + "seed": 4, + "dist": "R1", + "steps": 1024, + "kernel": "delta", + "max": { + "S": 7125, + "M": 6008, + "delta": 12199, + "delta_lo": 262143, + "delta_hi": 1, + "o": 557, + "w": 1588, + "v": 16056, + "k_hat": 2373, + "q_hat": 211, + "xin": 18680, + "y": 39970, + "cg": 196979, + "sigma": 1850, + "e1": 3065, + "e2": 3561, + "Rc": 432197966, + "rr": 1576, + "gap": 1663, + "Vx": 3420492 + }, + "min_exact": { + "e1": 0, + "e2": 0, + "rr": 0, + "gap": 0, + "rw": 0, + "ry": 0 + }, + "fit": { + "rng_cells": true, + "delta_split_needed": false, + "sigma_24": true, + "e1_24": true, + "e2_24": true, + "Rc_36": true, + "rr_24": true, + "gap_24": true, + "rw_24": true, + "ry_12": true, + "public_v_31": true, + "public_y_31": true, + "public_cg_24": true, + "public_x_31": true + }, + "all_fit": true, + "div_S": 0.016357526966573964, + "div_y": 0.2715894834193284 + }, + { + "seed": 5, + "dist": "R1", + "steps": 1024, + "kernel": "delta", + "max": { + "S": 5098, + "M": 3265, + "delta": 13253, + "delta_lo": 262143, + "delta_hi": 1, + "o": 349, + "w": 2233, + "v": 18376, + "k_hat": 2289, + "q_hat": 226, + "xin": 20901, + "y": 52460, + "cg": 193316, + "sigma": 788, + "e1": 1369, + "e2": 1235, + "Rc": 1145324612, + "rr": 628, + "gap": 754, + "Vx": 619510 + }, + "min_exact": { + "e1": 0, + "e2": 0, + "rr": 0, + "gap": 0, + "rw": 0, + "ry": 0 + }, + "fit": { + "rng_cells": true, + "delta_split_needed": false, + "sigma_24": true, + "e1_24": true, + "e2_24": true, + "Rc_36": true, + "rr_24": true, + "gap_24": true, + "rw_24": true, + "ry_12": true, + "public_v_31": true, + "public_y_31": true, + "public_cg_24": true, + "public_x_31": true + }, + "all_fit": true, + "div_S": 0.0019555345956458903, + "div_y": 0.5294328864012781 + }, + { + "seed": 1, + "dist": "R2", + "steps": 1024, + "kernel": "delta", + "max": { + "S": 11188, + "M": 11554, + "delta": 25142, + "delta_lo": 262143, + "delta_hi": 1, + "o": 928, + "w": 1571, + "v": 36657, + "k_hat": 2547, + "q_hat": 213, + "xin": 36662, + "y": 31144, + "cg": 190996, + "sigma": 3624, + "e1": 6726, + "e2": 7230, + "Rc": 313787565, + "rr": 3269, + "gap": 3559, + "Vx": 13131247 + }, + "min_exact": { + "e1": 0, + "e2": 0, + "rr": 0, + "gap": 0, + "rw": 0, + "ry": 0 + }, + "fit": { + "rng_cells": true, + "delta_split_needed": false, + "sigma_24": true, + "e1_24": true, + "e2_24": true, + "Rc_36": true, + "rr_24": true, + "gap_24": true, + "rw_24": true, + "ry_12": true, + "public_v_31": true, + "public_y_31": true, + "public_cg_24": true, + "public_x_31": true + }, + "all_fit": true, + "div_S": 0.008127104797514051, + "div_y": 0.12511473130024586 + }, + { + "seed": 2, + "dist": "R2", + "steps": 1024, + "kernel": "delta", + "max": { + "S": 11758, + "M": 9610, + "delta": 28507, + "delta_lo": 262143, + "delta_hi": 1, + "o": 825, + "w": 1689, + "v": 36304, + "k_hat": 2362, + "q_hat": 209, + "xin": 36309, + "y": 33785, + "cg": 206901, + "sigma": 2790, + "e1": 4853, + "e2": 5040, + "Rc": 255462738, + "rr": 2437, + "gap": 2559, + "Vx": 7783724 + }, + "min_exact": { + "e1": 0, + "e2": 0, + "rr": 0, + "gap": 0, + "rw": 0, + "ry": 0 + }, + "fit": { + "rng_cells": true, + "delta_split_needed": false, + "sigma_24": true, + "e1_24": true, + "e2_24": true, + "Rc_36": true, + "rr_24": true, + "gap_24": true, + "rw_24": true, + "ry_12": true, + "public_v_31": true, + "public_y_31": true, + "public_cg_24": true, + "public_x_31": true + }, + "all_fit": true, + "div_S": 0.0047758739432378905, + "div_y": 0.12066407883241848 + }, + { + "seed": 3, + "dist": "R2", + "steps": 1024, + "kernel": "delta", + "max": { + "S": 12287, + "M": 9961, + "delta": 25702, + "delta_lo": 262143, + "delta_hi": 1, + "o": 888, + "w": 1589, + "v": 40977, + "k_hat": 2318, + "q_hat": 260, + "xin": 42117, + "y": 34744, + "cg": 204422, + "sigma": 3527, + "e1": 6226, + "e2": 6410, + "Rc": 432197966, + "rr": 2828, + "gap": 3067, + "Vx": 12438226 + }, + "min_exact": { + "e1": 0, + "e2": 0, + "rr": 0, + "gap": 0, + "rw": 0, + "ry": 0 + }, + "fit": { + "rng_cells": true, + "delta_split_needed": false, + "sigma_24": true, + "e1_24": true, + "e2_24": true, + "Rc_36": true, + "rr_24": true, + "gap_24": true, + "rw_24": true, + "ry_12": true, + "public_v_31": true, + "public_y_31": true, + "public_cg_24": true, + "public_x_31": true + }, + "all_fit": true, + "div_S": 0.007357157730240621, + "div_y": 0.1140908941324299 + }, + { + "seed": 4, + "dist": "R2", + "steps": 1024, + "kernel": "delta", + "max": { + "S": 14726, + "M": 13410, + "delta": 24582, + "delta_lo": 262143, + "delta_hi": 1, + "o": 1195, + "w": 1434, + "v": 32694, + "k_hat": 2373, + "q_hat": 211, + "xin": 37103, + "y": 37920, + "cg": 196979, + "sigma": 4659, + "e1": 7781, + "e2": 7722, + "Rc": 173534032, + "rr": 3901, + "gap": 4528, + "Vx": 21707090 + }, + "min_exact": { + "e1": 0, + "e2": 0, + "rr": 0, + "gap": 0, + "rw": 0, + "ry": 0 + }, + "fit": { + "rng_cells": true, + "delta_split_needed": false, + "sigma_24": true, + "e1_24": true, + "e2_24": true, + "Rc_36": true, + "rr_24": true, + "gap_24": true, + "rw_24": true, + "ry_12": true, + "public_v_31": true, + "public_y_31": true, + "public_cg_24": true, + "public_x_31": true + }, + "all_fit": true, + "div_S": 0.016803996655398556, + "div_y": 0.1266261615965073 + }, + { + "seed": 5, + "dist": "R2", + "steps": 1024, + "kernel": "delta", + "max": { + "S": 10419, + "M": 7025, + "delta": 26959, + "delta_lo": 262143, + "delta_hi": 1, + "o": 746, + "w": 1993, + "v": 37141, + "k_hat": 2289, + "q_hat": 226, + "xin": 37145, + "y": 46181, + "cg": 193316, + "sigma": 1925, + "e1": 3660, + "e2": 3508, + "Rc": 648296950, + "rr": 1461, + "gap": 1715, + "Vx": 3704929 + }, + "min_exact": { + "e1": 0, + "e2": 0, + "rr": 0, + "gap": 0, + "rw": 0, + "ry": 0 + }, + "fit": { + "rng_cells": true, + "delta_split_needed": false, + "sigma_24": true, + "e1_24": true, + "e2_24": true, + "Rc_36": true, + "rr_24": true, + "gap_24": true, + "rw_24": true, + "ry_12": true, + "public_v_31": true, + "public_y_31": true, + "public_cg_24": true, + "public_x_31": true + }, + "all_fit": true, + "div_S": 0.002002694793120069, + "div_y": 0.2653354985651064 + }, + { + "seed": 1, + "dist": "R3", + "steps": 1024, + "kernel": "delta", + "max": { + "S": 74704, + "M": 44625, + "delta": 237244, + "delta_lo": 262143, + "delta_hi": 1, + "o": 4257, + "w": 3007, + "v": 380527, + "k_hat": 2649, + "q_hat": 199, + "xin": 380527, + "y": 69694, + "cg": 205057, + "sigma": 6232, + "e1": 10245, + "e2": 9617, + "Rc": 98876944, + "rr": 5456, + "gap": 5695, + "Vx": 38844806 + }, + "min_exact": { + "e1": 0, + "e2": 0, + "rr": 0, + "gap": 0, + "rw": 0, + "ry": 0 + }, + "fit": { + "rng_cells": true, + "delta_split_needed": false, + "sigma_24": true, + "e1_24": true, + "e2_24": true, + "Rc_36": true, + "rr_24": true, + "gap_24": true, + "rw_24": true, + "ry_12": true, + "public_v_31": true, + "public_y_31": true, + "public_cg_24": true, + "public_x_31": true + }, + "all_fit": true, + "div_S": 0.011622697488510525, + "div_y": 0.10666226747298779 + }, + { + "seed": 2, + "dist": "R3", + "steps": 1024, + "kernel": "delta", + "max": { + "S": 200463, + "M": 137315, + "delta": 582625, + "delta_lo": 262143, + "delta_hi": 2, + "o": 8271, + "w": 3928, + "v": 1022655, + "k_hat": 2604, + "q_hat": 217, + "xin": 1022655, + "y": 66595, + "cg": 201163, + "sigma": 8875, + "e1": 12578, + "e2": 16637, + "Rc": 1347440720, + "rr": 5611, + "gap": 5231, + "Vx": 78764591 + }, + "min_exact": { + "e1": 0, + "e2": 0, + "rr": 0, + "gap": 0, + "rw": 0, + "ry": 0 + }, + "fit": { + "rng_cells": true, + "delta_split_needed": true, + "sigma_24": true, + "e1_24": true, + "e2_24": true, + "Rc_36": true, + "rr_24": true, + "gap_24": true, + "rw_24": true, + "ry_12": true, + "public_v_31": true, + "public_y_31": true, + "public_cg_24": true, + "public_x_31": true + }, + "all_fit": true, + "div_S": 0.02597577748053581, + "div_y": 0.5987994346756061 + }, + { + "seed": 3, + "dist": "R3", + "steps": 1024, + "kernel": "delta", + "max": { + "S": 104250, + "M": 76380, + "delta": 273909, + "delta_lo": 262143, + "delta_hi": 1, + "o": 7204, + "w": 3602, + "v": 344434, + "k_hat": 2743, + "q_hat": 201, + "xin": 436628, + "y": 53945, + "cg": 194628, + "sigma": 8191, + "e1": 12994, + "e2": 11093, + "Rc": 160185260, + "rr": 5278, + "gap": 7166, + "Vx": 67100247 + }, + "min_exact": { + "e1": 0, + "e2": 0, + "rr": 0, + "gap": 0, + "rw": 0, + "ry": 0 + }, + "fit": { + "rng_cells": true, + "delta_split_needed": true, + "sigma_24": true, + "e1_24": true, + "e2_24": true, + "Rc_36": true, + "rr_24": true, + "gap_24": true, + "rw_24": true, + "ry_12": true, + "public_v_31": true, + "public_y_31": true, + "public_cg_24": true, + "public_x_31": true + }, + "all_fit": true, + "div_S": 0.014007120397929507, + "div_y": 0.11165680730479632 + }, + { + "seed": 4, + "dist": "R3", + "steps": 1024, + "kernel": "delta", + "max": { + "S": 77280, + "M": 51664, + "delta": 187351, + "delta_lo": 262143, + "delta_hi": 1, + "o": 4159, + "w": 2626, + "v": 381736, + "k_hat": 2360, + "q_hat": 210, + "xin": 381736, + "y": 60172, + "cg": 227005, + "sigma": 7425, + "e1": 13545, + "e2": 12846, + "Rc": 100614167, + "rr": 6759, + "gap": 6535, + "Vx": 55142023 + }, + "min_exact": { + "e1": 0, + "e2": 0, + "rr": 0, + "gap": 0, + "rw": 0, + "ry": 0 + }, + "fit": { + "rng_cells": true, + "delta_split_needed": false, + "sigma_24": true, + "e1_24": true, + "e2_24": true, + "Rc_36": true, + "rr_24": true, + "gap_24": true, + "rw_24": true, + "ry_12": true, + "public_v_31": true, + "public_y_31": true, + "public_cg_24": true, + "public_x_31": true + }, + "all_fit": true, + "div_S": 0.022346411314579484, + "div_y": 0.10188280754729817 + }, + { + "seed": 5, + "dist": "R3", + "steps": 1024, + "kernel": "delta", + "max": { + "S": 100699, + "M": 30035, + "delta": 253724, + "delta_lo": 262143, + "delta_hi": 1, + "o": 4045, + "w": 3879, + "v": 421369, + "k_hat": 2403, + "q_hat": 221, + "xin": 421369, + "y": 98012, + "cg": 206419, + "sigma": 4536, + "e1": 7918, + "e2": 6671, + "Rc": 512831915, + "rr": 2836, + "gap": 3711, + "Vx": 20578117 + }, + "min_exact": { + "e1": 0, + "e2": 0, + "rr": 0, + "gap": 0, + "rw": 0, + "ry": 0 + }, + "fit": { + "rng_cells": true, + "delta_split_needed": false, + "sigma_24": true, + "e1_24": true, + "e2_24": true, + "Rc_36": true, + "rr_24": true, + "gap_24": true, + "rw_24": true, + "ry_12": true, + "public_v_31": true, + "public_y_31": true, + "public_cg_24": true, + "public_x_31": true + }, + "all_fit": true, + "div_S": 0.009244793540513996, + "div_y": 0.354888803733056 + }, + { + "seed": 1, + "dist": "R4", + "steps": 1024, + "kernel": "delta", + "max": { + "S": 15048, + "M": 25885, + "delta": 23158, + "delta_lo": 262143, + "delta_hi": 1, + "o": 2407, + "w": 1914, + "v": 33823, + "k_hat": 2547, + "q_hat": 213, + "xin": 33832, + "y": 49131, + "cg": 190996, + "sigma": 5415, + "e1": 9209, + "e2": 10066, + "Rc": 313787565, + "rr": 4286, + "gap": 4597, + "Vx": 29320842 + }, + "min_exact": { + "e1": 0, + "e2": 0, + "rr": 0, + "gap": 0, + "rw": 0, + "ry": 0 + }, + "fit": { + "rng_cells": true, + "delta_split_needed": false, + "sigma_24": true, + "e1_24": true, + "e2_24": true, + "Rc_36": true, + "rr_24": true, + "gap_24": true, + "rw_24": true, + "ry_12": true, + "public_v_31": true, + "public_y_31": true, + "public_cg_24": true, + "public_x_31": true + }, + "all_fit": true, + "div_S": 0.00857669170250841, + "div_y": 0.09346556815971807 + }, + { + "seed": 2, + "dist": "R4", + "steps": 1024, + "kernel": "delta", + "max": { + "S": 13066, + "M": 19712, + "delta": 19867, + "delta_lo": 262143, + "delta_hi": 1, + "o": 1544, + "w": 1994, + "v": 34043, + "k_hat": 2362, + "q_hat": 209, + "xin": 34376, + "y": 50185, + "cg": 206901, + "sigma": 4195, + "e1": 6890, + "e2": 7431, + "Rc": 255462738, + "rr": 3486, + "gap": 3513, + "Vx": 17597173 + }, + "min_exact": { + "e1": 0, + "e2": 0, + "rr": 0, + "gap": 0, + "rw": 0, + "ry": 0 + }, + "fit": { + "rng_cells": true, + "delta_split_needed": false, + "sigma_24": true, + "e1_24": true, + "e2_24": true, + "Rc_36": true, + "rr_24": true, + "gap_24": true, + "rw_24": true, + "ry_12": true, + "public_v_31": true, + "public_y_31": true, + "public_cg_24": true, + "public_x_31": true + }, + "all_fit": true, + "div_S": 0.0044681428500567, + "div_y": 0.15556906906821588 + }, + { + "seed": 3, + "dist": "R4", + "steps": 1024, + "kernel": "delta", + "max": { + "S": 17887, + "M": 26346, + "delta": 21502, + "delta_lo": 262143, + "delta_hi": 1, + "o": 1987, + "w": 2276, + "v": 39301, + "k_hat": 2318, + "q_hat": 260, + "xin": 39304, + "y": 52474, + "cg": 204422, + "sigma": 4684, + "e1": 7828, + "e2": 8782, + "Rc": 432197966, + "rr": 4096, + "gap": 4110, + "Vx": 21938295 + }, + "min_exact": { + "e1": 0, + "e2": 0, + "rr": 0, + "gap": 0, + "rw": 0, + "ry": 0 + }, + "fit": { + "rng_cells": true, + "delta_split_needed": false, + "sigma_24": true, + "e1_24": true, + "e2_24": true, + "Rc_36": true, + "rr_24": true, + "gap_24": true, + "rw_24": true, + "ry_12": true, + "public_v_31": true, + "public_y_31": true, + "public_cg_24": true, + "public_x_31": true + }, + "all_fit": true, + "div_S": 0.008133376679793636, + "div_y": 0.10393809271189142 + }, + { + "seed": 4, + "dist": "R4", + "steps": 1024, + "kernel": "delta", + "max": { + "S": 15666, + "M": 23074, + "delta": 20861, + "delta_lo": 262143, + "delta_hi": 1, + "o": 2171, + "w": 1959, + "v": 35174, + "k_hat": 2373, + "q_hat": 211, + "xin": 35181, + "y": 65079, + "cg": 196979, + "sigma": 6339, + "e1": 11112, + "e2": 10400, + "Rc": 139958201, + "rr": 5065, + "gap": 5818, + "Vx": 40183917 + }, + "min_exact": { + "e1": 0, + "e2": 0, + "rr": 0, + "gap": 0, + "rw": 0, + "ry": 0 + }, + "fit": { + "rng_cells": true, + "delta_split_needed": false, + "sigma_24": true, + "e1_24": true, + "e2_24": true, + "Rc_36": true, + "rr_24": true, + "gap_24": true, + "rw_24": true, + "ry_12": true, + "public_v_31": true, + "public_y_31": true, + "public_cg_24": true, + "public_x_31": true + }, + "all_fit": true, + "div_S": 0.016654399873036296, + "div_y": 0.10080320452171954 + }, + { + "seed": 5, + "dist": "R4", + "steps": 1024, + "kernel": "delta", + "max": { + "S": 11611, + "M": 12591, + "delta": 27684, + "delta_lo": 262143, + "delta_hi": 1, + "o": 1208, + "w": 2375, + "v": 38363, + "k_hat": 2289, + "q_hat": 226, + "xin": 38366, + "y": 43652, + "cg": 193316, + "sigma": 3031, + "e1": 4630, + "e2": 5738, + "Rc": 487372175, + "rr": 2396, + "gap": 2875, + "Vx": 9187781 + }, + "min_exact": { + "e1": 0, + "e2": 0, + "rr": 0, + "gap": 0, + "rw": 0, + "ry": 0 + }, + "fit": { + "rng_cells": true, + "delta_split_needed": false, + "sigma_24": true, + "e1_24": true, + "e2_24": true, + "Rc_36": true, + "rr_24": true, + "gap_24": true, + "rw_24": true, + "ry_12": true, + "public_v_31": true, + "public_y_31": true, + "public_cg_24": true, + "public_x_31": true + }, + "all_fit": true, + "div_S": 0.0022908209116986322, + "div_y": 0.2499861631809468 + } + ] +} diff --git a/experiments/20261105-32-qwen-deltanet-m2-design/runs/ranges-head-q-unscaled.json b/experiments/20261105-32-qwen-deltanet-m2-design/runs/ranges-head-q-unscaled.json new file mode 100644 index 00000000..a8a4d491 --- /dev/null +++ b/experiments/20261105-32-qwen-deltanet-m2-design/runs/ranges-head-q-unscaled.json @@ -0,0 +1,1134 @@ +{ + "kernel": "random", + "q_unscaled": true, + "steps": 1024, + "n": 20, + "fit": 20, + "max": { + "S": 150189, + "M": 53294, + "delta": 282387, + "delta_lo": 262143, + "delta_hi": 1, + "o": 72479, + "w": 4047, + "v": 527711, + "k_hat": 2988, + "q_hat": 2911, + "xin": 1478242, + "y": 101401, + "cg": 230259, + "sigma": 73340, + "e1": 109055, + "e2": 96301, + "Rc": 128207978, + "rr": 43567, + "gap": 43843, + "Vx": 5378589839 + }, + "worst_div_S": 0.03978339234812325, + "worst_div_y": 1.1899773994043372, + "seconds": 14.2, + "runs": [ + { + "seed": 1, + "dist": "R1", + "steps": 1024, + "kernel": "random", + "max": { + "S": 4374, + "M": 3736, + "delta": 8638, + "delta_lo": 262143, + "delta_hi": 1, + "o": 3338, + "w": 2378, + "v": 12912, + "k_hat": 2863, + "q_hat": 2649, + "xin": 18411, + "y": 70677, + "cg": 205057, + "sigma": 7060, + "e1": 12407, + "e2": 11948, + "Rc": 120984994, + "rr": 6576, + "gap": 6529, + "Vx": 49580361 + }, + "min_exact": { + "e1": 0, + "e2": 0, + "rr": 0, + "gap": 0, + "rw": 0, + "ry": 0 + }, + "fit": { + "rng_cells": true, + "delta_split_needed": false, + "sigma_24": true, + "e1_24": true, + "e2_24": true, + "Rc_36": true, + "rr_24": true, + "gap_24": true, + "rw_24": true, + "ry_12": true, + "public_v_31": true, + "public_y_31": true, + "public_cg_24": true, + "public_x_31": true + }, + "all_fit": true, + "div_S": 0.011811117983765274, + "div_y": 0.7435241115951337 + }, + { + "seed": 2, + "dist": "R1", + "steps": 1024, + "kernel": "random", + "max": { + "S": 4866, + "M": 2975, + "delta": 8769, + "delta_lo": 262143, + "delta_hi": 1, + "o": 1994, + "w": 2089, + "v": 11196, + "k_hat": 2874, + "q_hat": 2831, + "xin": 18966, + "y": 46334, + "cg": 206901, + "sigma": 5670, + "e1": 9863, + "e2": 9572, + "Rc": 120560485, + "rr": 4592, + "gap": 4903, + "Vx": 31883947 + }, + "min_exact": { + "e1": 0, + "e2": 0, + "rr": 0, + "gap": 0, + "rw": 0, + "ry": 0 + }, + "fit": { + "rng_cells": true, + "delta_split_needed": false, + "sigma_24": true, + "e1_24": true, + "e2_24": true, + "Rc_36": true, + "rr_24": true, + "gap_24": true, + "rw_24": true, + "ry_12": true, + "public_v_31": true, + "public_y_31": true, + "public_cg_24": true, + "public_x_31": true + }, + "all_fit": true, + "div_S": 0.005407772287761618, + "div_y": 0.6834228247275562 + }, + { + "seed": 3, + "dist": "R1", + "steps": 1024, + "kernel": "random", + "max": { + "S": 4075, + "M": 3306, + "delta": 8561, + "delta_lo": 262143, + "delta_hi": 1, + "o": 3280, + "w": 2393, + "v": 11643, + "k_hat": 2598, + "q_hat": 2810, + "xin": 19371, + "y": 56367, + "cg": 230259, + "sigma": 7456, + "e1": 11571, + "e2": 10828, + "Rc": 123818877, + "rr": 6180, + "gap": 7327, + "Vx": 55328377 + }, + "min_exact": { + "e1": 0, + "e2": 0, + "rr": 0, + "gap": 0, + "rw": 0, + "ry": 0 + }, + "fit": { + "rng_cells": true, + "delta_split_needed": false, + "sigma_24": true, + "e1_24": true, + "e2_24": true, + "Rc_36": true, + "rr_24": true, + "gap_24": true, + "rw_24": true, + "ry_12": true, + "public_v_31": true, + "public_y_31": true, + "public_cg_24": true, + "public_x_31": true + }, + "all_fit": true, + "div_S": 0.010789764803398272, + "div_y": 0.9157835639545326 + }, + { + "seed": 4, + "dist": "R1", + "steps": 1024, + "kernel": "random", + "max": { + "S": 5134, + "M": 4102, + "delta": 7572, + "delta_lo": 262143, + "delta_hi": 1, + "o": 3981, + "w": 2281, + "v": 12311, + "k_hat": 2829, + "q_hat": 2871, + "xin": 20958, + "y": 49553, + "cg": 196979, + "sigma": 8991, + "e1": 15368, + "e2": 15742, + "Rc": 121412503, + "rr": 8465, + "gap": 7807, + "Vx": 80566104 + }, + "min_exact": { + "e1": 0, + "e2": 0, + "rr": 0, + "gap": 0, + "rw": 0, + "ry": 0 + }, + "fit": { + "rng_cells": true, + "delta_split_needed": false, + "sigma_24": true, + "e1_24": true, + "e2_24": true, + "Rc_36": true, + "rr_24": true, + "gap_24": true, + "rw_24": true, + "ry_12": true, + "public_v_31": true, + "public_y_31": true, + "public_cg_24": true, + "public_x_31": true + }, + "all_fit": true, + "div_S": 0.03816041735654266, + "div_y": 1.1899773994043372 + }, + { + "seed": 5, + "dist": "R1", + "steps": 1024, + "kernel": "random", + "max": { + "S": 4130, + "M": 2480, + "delta": 8476, + "delta_lo": 262143, + "delta_hi": 1, + "o": 2162, + "w": 2761, + "v": 13173, + "k_hat": 2697, + "q_hat": 2692, + "xin": 20901, + "y": 54788, + "cg": 207898, + "sigma": 4361, + "e1": 6252, + "e2": 8030, + "Rc": 127023062, + "rr": 3250, + "gap": 3589, + "Vx": 18748512 + }, + "min_exact": { + "e1": 0, + "e2": 0, + "rr": 0, + "gap": 0, + "rw": 0, + "ry": 0 + }, + "fit": { + "rng_cells": true, + "delta_split_needed": false, + "sigma_24": true, + "e1_24": true, + "e2_24": true, + "Rc_36": true, + "rr_24": true, + "gap_24": true, + "rw_24": true, + "ry_12": true, + "public_v_31": true, + "public_y_31": true, + "public_cg_24": true, + "public_x_31": true + }, + "all_fit": true, + "div_S": 0.0018535262388349799, + "div_y": 0.4879692787828158 + }, + { + "seed": 1, + "dist": "R2", + "steps": 1024, + "kernel": "random", + "max": { + "S": 9669, + "M": 8577, + "delta": 18417, + "delta_lo": 262143, + "delta_hi": 1, + "o": 7544, + "w": 2276, + "v": 26768, + "k_hat": 2863, + "q_hat": 2649, + "xin": 36172, + "y": 63568, + "cg": 205057, + "sigma": 20464, + "e1": 32087, + "e2": 34210, + "Rc": 96651866, + "rr": 18294, + "gap": 17300, + "Vx": 418509923 + }, + "min_exact": { + "e1": 0, + "e2": 0, + "rr": 0, + "gap": 0, + "rw": 0, + "ry": 0 + }, + "fit": { + "rng_cells": true, + "delta_split_needed": false, + "sigma_24": true, + "e1_24": true, + "e2_24": true, + "Rc_36": true, + "rr_24": true, + "gap_24": true, + "rw_24": true, + "ry_12": true, + "public_v_31": true, + "public_y_31": true, + "public_cg_24": true, + "public_x_31": true + }, + "all_fit": true, + "div_S": 0.011878092405511364, + "div_y": 0.3316755454580318 + }, + { + "seed": 2, + "dist": "R2", + "steps": 1024, + "kernel": "random", + "max": { + "S": 10765, + "M": 6927, + "delta": 18380, + "delta_lo": 262143, + "delta_hi": 1, + "o": 4714, + "w": 2098, + "v": 23569, + "k_hat": 2874, + "q_hat": 2831, + "xin": 36309, + "y": 43034, + "cg": 206901, + "sigma": 14089, + "e1": 25002, + "e2": 23214, + "Rc": 94654926, + "rr": 12922, + "gap": 11772, + "Vx": 198233155 + }, + "min_exact": { + "e1": 0, + "e2": 0, + "rr": 0, + "gap": 0, + "rw": 0, + "ry": 0 + }, + "fit": { + "rng_cells": true, + "delta_split_needed": false, + "sigma_24": true, + "e1_24": true, + "e2_24": true, + "Rc_36": true, + "rr_24": true, + "gap_24": true, + "rw_24": true, + "ry_12": true, + "public_v_31": true, + "public_y_31": true, + "public_cg_24": true, + "public_x_31": true + }, + "all_fit": true, + "div_S": 0.005188915170521599, + "div_y": 0.29762430097645876 + }, + { + "seed": 3, + "dist": "R2", + "steps": 1024, + "kernel": "random", + "max": { + "S": 9075, + "M": 7370, + "delta": 18306, + "delta_lo": 262143, + "delta_hi": 1, + "o": 7398, + "w": 2239, + "v": 24401, + "k_hat": 2598, + "q_hat": 2810, + "xin": 38741, + "y": 47040, + "cg": 230259, + "sigma": 18960, + "e1": 31698, + "e2": 28778, + "Rc": 105722271, + "rr": 18496, + "gap": 15111, + "Vx": 359238482 + }, + "min_exact": { + "e1": 0, + "e2": 0, + "rr": 0, + "gap": 0, + "rw": 0, + "ry": 0 + }, + "fit": { + "rng_cells": true, + "delta_split_needed": false, + "sigma_24": true, + "e1_24": true, + "e2_24": true, + "Rc_36": true, + "rr_24": true, + "gap_24": true, + "rw_24": true, + "ry_12": true, + "public_v_31": true, + "public_y_31": true, + "public_cg_24": true, + "public_x_31": true + }, + "all_fit": true, + "div_S": 0.010336854292446018, + "div_y": 0.41903995209765954 + }, + { + "seed": 4, + "dist": "R2", + "steps": 1024, + "kernel": "random", + "max": { + "S": 11021, + "M": 9200, + "delta": 15951, + "delta_lo": 262143, + "delta_hi": 1, + "o": 9252, + "w": 2295, + "v": 25643, + "k_hat": 2829, + "q_hat": 2871, + "xin": 37103, + "y": 45040, + "cg": 196979, + "sigma": 22699, + "e1": 40740, + "e2": 42837, + "Rc": 96651866, + "rr": 21832, + "gap": 21367, + "Vx": 515008313 + }, + "min_exact": { + "e1": 0, + "e2": 0, + "rr": 0, + "gap": 0, + "rw": 0, + "ry": 0 + }, + "fit": { + "rng_cells": true, + "delta_split_needed": false, + "sigma_24": true, + "e1_24": true, + "e2_24": true, + "Rc_36": true, + "rr_24": true, + "gap_24": true, + "rw_24": true, + "ry_12": true, + "public_v_31": true, + "public_y_31": true, + "public_cg_24": true, + "public_x_31": true + }, + "all_fit": true, + "div_S": 0.037921908535273594, + "div_y": 0.5875066467499441 + }, + { + "seed": 5, + "dist": "R2", + "steps": 1024, + "kernel": "random", + "max": { + "S": 9057, + "M": 5514, + "delta": 17964, + "delta_lo": 262143, + "delta_hi": 1, + "o": 4845, + "w": 2723, + "v": 27255, + "k_hat": 2697, + "q_hat": 2692, + "xin": 37339, + "y": 54407, + "cg": 207898, + "sigma": 10193, + "e1": 18408, + "e2": 19862, + "Rc": 112470502, + "rr": 6754, + "gap": 8587, + "Vx": 103622957 + }, + "min_exact": { + "e1": 0, + "e2": 0, + "rr": 0, + "gap": 0, + "rw": 0, + "ry": 0 + }, + "fit": { + "rng_cells": true, + "delta_split_needed": false, + "sigma_24": true, + "e1_24": true, + "e2_24": true, + "Rc_36": true, + "rr_24": true, + "gap_24": true, + "rw_24": true, + "ry_12": true, + "public_v_31": true, + "public_y_31": true, + "public_cg_24": true, + "public_x_31": true + }, + "all_fit": true, + "div_S": 0.0019988409118104483, + "div_y": 0.36381095418429377 + }, + { + "seed": 1, + "dist": "R3", + "steps": 1024, + "kernel": "random", + "max": { + "S": 49299, + "M": 48573, + "delta": 151336, + "delta_lo": 262143, + "delta_hi": 1, + "o": 31162, + "w": 3554, + "v": 166204, + "k_hat": 2789, + "q_hat": 2473, + "xin": 388587, + "y": 67218, + "cg": 205057, + "sigma": 39730, + "e1": 62175, + "e2": 71254, + "Rc": 104120419, + "rr": 27742, + "gap": 35610, + "Vx": 1578227757 + }, + "min_exact": { + "e1": 0, + "e2": 0, + "rr": 0, + "gap": 0, + "rw": 0, + "ry": 0 + }, + "fit": { + "rng_cells": true, + "delta_split_needed": false, + "sigma_24": true, + "e1_24": true, + "e2_24": true, + "Rc_36": true, + "rr_24": true, + "gap_24": true, + "rw_24": true, + "ry_12": true, + "public_v_31": true, + "public_y_31": true, + "public_cg_24": true, + "public_x_31": true + }, + "all_fit": true, + "div_S": 0.012992215626891568, + "div_y": 0.17799296410642285 + }, + { + "seed": 2, + "dist": "R3", + "steps": 1024, + "kernel": "random", + "max": { + "S": 150189, + "M": 45186, + "delta": 282387, + "delta_lo": 262143, + "delta_hi": 1, + "o": 72479, + "w": 4047, + "v": 470159, + "k_hat": 2697, + "q_hat": 2911, + "xin": 1022655, + "y": 69541, + "cg": 201163, + "sigma": 73340, + "e1": 109055, + "e2": 72079, + "Rc": 85899345, + "rr": 43416, + "gap": 33063, + "Vx": 5378589839 + }, + "min_exact": { + "e1": 0, + "e2": 0, + "rr": 0, + "gap": 0, + "rw": 0, + "ry": 0 + }, + "fit": { + "rng_cells": true, + "delta_split_needed": true, + "sigma_24": true, + "e1_24": true, + "e2_24": true, + "Rc_36": true, + "rr_24": true, + "gap_24": true, + "rw_24": true, + "ry_12": true, + "public_v_31": true, + "public_y_31": true, + "public_cg_24": true, + "public_x_31": true + }, + "all_fit": true, + "div_S": 0.021333899687325797, + "div_y": 0.1955057386275474 + }, + { + "seed": 3, + "dist": "R3", + "steps": 1024, + "kernel": "random", + "max": { + "S": 54105, + "M": 35671, + "delta": 105549, + "delta_lo": 262143, + "delta_hi": 1, + "o": 28504, + "w": 3298, + "v": 149901, + "k_hat": 2971, + "q_hat": 2684, + "xin": 436628, + "y": 73375, + "cg": 194628, + "sigma": 42798, + "e1": 70208, + "e2": 63774, + "Rc": 82794550, + "rr": 29908, + "gap": 32172, + "Vx": 1831428618 + }, + "min_exact": { + "e1": 0, + "e2": 0, + "rr": 0, + "gap": 0, + "rw": 0, + "ry": 0 + }, + "fit": { + "rng_cells": true, + "delta_split_needed": false, + "sigma_24": true, + "e1_24": true, + "e2_24": true, + "Rc_36": true, + "rr_24": true, + "gap_24": true, + "rw_24": true, + "ry_12": true, + "public_v_31": true, + "public_y_31": true, + "public_cg_24": true, + "public_x_31": true + }, + "all_fit": true, + "div_S": 0.011543801091357886, + "div_y": 0.2573523619574658 + }, + { + "seed": 4, + "dist": "R3", + "steps": 1024, + "kernel": "random", + "max": { + "S": 84016, + "M": 53294, + "delta": 157947, + "delta_lo": 262143, + "delta_hi": 1, + "o": 48292, + "w": 3720, + "v": 527711, + "k_hat": 2753, + "q_hat": 2499, + "xin": 1478242, + "y": 72387, + "cg": 183791, + "sigma": 53164, + "e1": 82717, + "e2": 96301, + "Rc": 128207978, + "rr": 43567, + "gap": 43843, + "Vx": 2826172832 + }, + "min_exact": { + "e1": 0, + "e2": 0, + "rr": 0, + "gap": 0, + "rw": 0, + "ry": 0 + }, + "fit": { + "rng_cells": true, + "delta_split_needed": false, + "sigma_24": true, + "e1_24": true, + "e2_24": true, + "Rc_36": true, + "rr_24": true, + "gap_24": true, + "rw_24": true, + "ry_12": true, + "public_v_31": true, + "public_y_31": true, + "public_cg_24": true, + "public_x_31": true + }, + "all_fit": true, + "div_S": 0.03978339234812325, + "div_y": 0.34565981688497605 + }, + { + "seed": 5, + "dist": "R3", + "steps": 1024, + "kernel": "random", + "max": { + "S": 38716, + "M": 30406, + "delta": 111118, + "delta_lo": 262143, + "delta_hi": 1, + "o": 15723, + "w": 3594, + "v": 364819, + "k_hat": 2988, + "q_hat": 2781, + "xin": 869012, + "y": 101401, + "cg": 189598, + "sigma": 19951, + "e1": 32117, + "e2": 30581, + "Rc": 91503963, + "rr": 16512, + "gap": 15871, + "Vx": 397792438 + }, + "min_exact": { + "e1": 0, + "e2": 0, + "rr": 0, + "gap": 0, + "rw": 0, + "ry": 0 + }, + "fit": { + "rng_cells": true, + "delta_split_needed": false, + "sigma_24": true, + "e1_24": true, + "e2_24": true, + "Rc_36": true, + "rr_24": true, + "gap_24": true, + "rw_24": true, + "ry_12": true, + "public_v_31": true, + "public_y_31": true, + "public_cg_24": true, + "public_x_31": true + }, + "all_fit": true, + "div_S": 0.008907953502454546, + "div_y": 0.20511626290356078 + }, + { + "seed": 1, + "dist": "R4", + "steps": 1024, + "kernel": "random", + "max": { + "S": 28044, + "M": 25850, + "delta": 26290, + "delta_lo": 262143, + "delta_hi": 1, + "o": 20858, + "w": 3101, + "v": 36478, + "k_hat": 2863, + "q_hat": 2649, + "xin": 32743, + "y": 89692, + "cg": 205057, + "sigma": 33211, + "e1": 55466, + "e2": 49926, + "Rc": 96651866, + "rr": 27081, + "gap": 27032, + "Vx": 1102748286 + }, + "min_exact": { + "e1": 0, + "e2": 0, + "rr": 0, + "gap": 0, + "rw": 0, + "ry": 0 + }, + "fit": { + "rng_cells": true, + "delta_split_needed": false, + "sigma_24": true, + "e1_24": true, + "e2_24": true, + "Rc_36": true, + "rr_24": true, + "gap_24": true, + "rw_24": true, + "ry_12": true, + "public_v_31": true, + "public_y_31": true, + "public_cg_24": true, + "public_x_31": true + }, + "all_fit": true, + "div_S": 0.012131181150351611, + "div_y": 0.37948160448547347 + }, + { + "seed": 2, + "dist": "R4", + "steps": 1024, + "kernel": "random", + "max": { + "S": 16205, + "M": 16119, + "delta": 20678, + "delta_lo": 262143, + "delta_hi": 1, + "o": 14126, + "w": 3270, + "v": 32443, + "k_hat": 2874, + "q_hat": 2831, + "xin": 34376, + "y": 65085, + "cg": 206901, + "sigma": 21380, + "e1": 42443, + "e2": 38249, + "Rc": 96651866, + "rr": 21072, + "gap": 21253, + "Vx": 456834095 + }, + "min_exact": { + "e1": 0, + "e2": 0, + "rr": 0, + "gap": 0, + "rw": 0, + "ry": 0 + }, + "fit": { + "rng_cells": true, + "delta_split_needed": false, + "sigma_24": true, + "e1_24": true, + "e2_24": true, + "Rc_36": true, + "rr_24": true, + "gap_24": true, + "rw_24": true, + "ry_12": true, + "public_v_31": true, + "public_y_31": true, + "public_cg_24": true, + "public_x_31": true + }, + "all_fit": true, + "div_S": 0.006178498091920059, + "div_y": 0.3277266322640603 + }, + { + "seed": 3, + "dist": "R4", + "steps": 1024, + "kernel": "random", + "max": { + "S": 26109, + "M": 21544, + "delta": 23600, + "delta_lo": 262143, + "delta_hi": 1, + "o": 23642, + "w": 3317, + "v": 35674, + "k_hat": 2598, + "q_hat": 2810, + "xin": 39304, + "y": 71948, + "cg": 230259, + "sigma": 33859, + "e1": 48748, + "e2": 56353, + "Rc": 117872172, + "rr": 29093, + "gap": 24835, + "Vx": 1146200850 + }, + "min_exact": { + "e1": 0, + "e2": 0, + "rr": 0, + "gap": 0, + "rw": 0, + "ry": 0 + }, + "fit": { + "rng_cells": true, + "delta_split_needed": false, + "sigma_24": true, + "e1_24": true, + "e2_24": true, + "Rc_36": true, + "rr_24": true, + "gap_24": true, + "rw_24": true, + "ry_12": true, + "public_v_31": true, + "public_y_31": true, + "public_cg_24": true, + "public_x_31": true + }, + "all_fit": true, + "div_S": 0.012081004182579846, + "div_y": 0.37506104483382074 + }, + { + "seed": 4, + "dist": "R4", + "steps": 1024, + "kernel": "random", + "max": { + "S": 21927, + "M": 24545, + "delta": 18461, + "delta_lo": 262143, + "delta_hi": 1, + "o": 23956, + "w": 3037, + "v": 35976, + "k_hat": 2829, + "q_hat": 2871, + "xin": 38117, + "y": 69992, + "cg": 196979, + "sigma": 37151, + "e1": 68505, + "e2": 66501, + "Rc": 89829381, + "rr": 31186, + "gap": 33234, + "Vx": 1379990490 + }, + "min_exact": { + "e1": 0, + "e2": 0, + "rr": 0, + "gap": 0, + "rw": 0, + "ry": 0 + }, + "fit": { + "rng_cells": true, + "delta_split_needed": false, + "sigma_24": true, + "e1_24": true, + "e2_24": true, + "Rc_36": true, + "rr_24": true, + "gap_24": true, + "rw_24": true, + "ry_12": true, + "public_v_31": true, + "public_y_31": true, + "public_cg_24": true, + "public_x_31": true + }, + "all_fit": true, + "div_S": 0.038746587142156716, + "div_y": 0.5782205097123554 + }, + { + "seed": 5, + "dist": "R4", + "steps": 1024, + "kernel": "random", + "max": { + "S": 14188, + "M": 9544, + "delta": 25182, + "delta_lo": 262143, + "delta_hi": 1, + "o": 9655, + "w": 3028, + "v": 35281, + "k_hat": 2697, + "q_hat": 2692, + "xin": 36071, + "y": 84062, + "cg": 207898, + "sigma": 14100, + "e1": 25327, + "e2": 19507, + "Rc": 107542217, + "rr": 10636, + "gap": 9857, + "Vx": 198560511 + }, + "min_exact": { + "e1": 0, + "e2": 0, + "rr": 0, + "gap": 0, + "rw": 0, + "ry": 0 + }, + "fit": { + "rng_cells": true, + "delta_split_needed": false, + "sigma_24": true, + "e1_24": true, + "e2_24": true, + "Rc_36": true, + "rr_24": true, + "gap_24": true, + "rw_24": true, + "ry_12": true, + "public_v_31": true, + "public_y_31": true, + "public_cg_24": true, + "public_x_31": true + }, + "all_fit": true, + "div_S": 0.003337717820431907, + "div_y": 0.4149468148138491 + } + ] +} diff --git a/experiments/20261105-32-qwen-deltanet-m2-design/runs/ranges-head.json b/experiments/20261105-32-qwen-deltanet-m2-design/runs/ranges-head.json new file mode 100644 index 00000000..e21d66fd --- /dev/null +++ b/experiments/20261105-32-qwen-deltanet-m2-design/runs/ranges-head.json @@ -0,0 +1,1134 @@ +{ + "kernel": "random", + "q_unscaled": false, + "steps": 1024, + "n": 20, + "fit": 20, + "max": { + "S": 150189, + "M": 53294, + "delta": 282387, + "delta_lo": 262143, + "delta_hi": 1, + "o": 6379, + "w": 4048, + "v": 527711, + "k_hat": 2988, + "q_hat": 257, + "xin": 1478242, + "y": 101976, + "cg": 230259, + "sigma": 6454, + "e1": 12148, + "e2": 7253, + "Rc": 1431655765, + "rr": 4264, + "gap": 4583, + "Vx": 41664117 + }, + "worst_div_S": 0.03978339234812325, + "worst_div_y": 1.2254855274411849, + "seconds": 14.5, + "runs": [ + { + "seed": 1, + "dist": "R1", + "steps": 1024, + "kernel": "random", + "max": { + "S": 4374, + "M": 3736, + "delta": 8638, + "delta_lo": 262143, + "delta_hi": 1, + "o": 295, + "w": 2386, + "v": 12912, + "k_hat": 2863, + "q_hat": 234, + "xin": 18411, + "y": 70919, + "cg": 205057, + "sigma": 621, + "e1": 1073, + "e2": 1126, + "Rc": 1374389534, + "rr": 546, + "gap": 543, + "Vx": 383858 + }, + "min_exact": { + "e1": 0, + "e2": 0, + "rr": 0, + "gap": 0, + "rw": 0, + "ry": 0 + }, + "fit": { + "rng_cells": true, + "delta_split_needed": false, + "sigma_24": true, + "e1_24": true, + "e2_24": true, + "Rc_36": true, + "rr_24": true, + "gap_24": true, + "rw_24": true, + "ry_12": true, + "public_v_31": true, + "public_y_31": true, + "public_cg_24": true, + "public_x_31": true + }, + "all_fit": true, + "div_S": 0.011811117983765274, + "div_y": 0.8879549234146031 + }, + { + "seed": 2, + "dist": "R1", + "steps": 1024, + "kernel": "random", + "max": { + "S": 4866, + "M": 2975, + "delta": 8769, + "delta_lo": 262143, + "delta_hi": 1, + "o": 175, + "w": 2102, + "v": 11196, + "k_hat": 2874, + "q_hat": 250, + "xin": 18966, + "y": 46299, + "cg": 206901, + "sigma": 500, + "e1": 771, + "e2": 949, + "Rc": 1374389534, + "rr": 361, + "gap": 391, + "Vx": 247904 + }, + "min_exact": { + "e1": 0, + "e2": 0, + "rr": 0, + "gap": 0, + "rw": 0, + "ry": 0 + }, + "fit": { + "rng_cells": true, + "delta_split_needed": false, + "sigma_24": true, + "e1_24": true, + "e2_24": true, + "Rc_36": true, + "rr_24": true, + "gap_24": true, + "rw_24": true, + "ry_12": true, + "public_v_31": true, + "public_y_31": true, + "public_cg_24": true, + "public_x_31": true + }, + "all_fit": true, + "div_S": 0.005407772287761618, + "div_y": 0.8748872170447984 + }, + { + "seed": 3, + "dist": "R1", + "steps": 1024, + "kernel": "random", + "max": { + "S": 4075, + "M": 3306, + "delta": 8561, + "delta_lo": 262143, + "delta_hi": 1, + "o": 290, + "w": 2404, + "v": 11643, + "k_hat": 2598, + "q_hat": 248, + "xin": 19371, + "y": 56492, + "cg": 230259, + "sigma": 656, + "e1": 1209, + "e2": 1023, + "Rc": 1402438300, + "rr": 592, + "gap": 553, + "Vx": 429398 + }, + "min_exact": { + "e1": 0, + "e2": 0, + "rr": 0, + "gap": 0, + "rw": 0, + "ry": 0 + }, + "fit": { + "rng_cells": true, + "delta_split_needed": false, + "sigma_24": true, + "e1_24": true, + "e2_24": true, + "Rc_36": true, + "rr_24": true, + "gap_24": true, + "rw_24": true, + "ry_12": true, + "public_v_31": true, + "public_y_31": true, + "public_cg_24": true, + "public_x_31": true + }, + "all_fit": true, + "div_S": 0.010789764803398272, + "div_y": 1.0175902045795326 + }, + { + "seed": 4, + "dist": "R1", + "steps": 1024, + "kernel": "random", + "max": { + "S": 5134, + "M": 4102, + "delta": 7572, + "delta_lo": 262143, + "delta_hi": 1, + "o": 351, + "w": 2281, + "v": 12311, + "k_hat": 2829, + "q_hat": 254, + "xin": 20958, + "y": 49906, + "cg": 196979, + "sigma": 793, + "e1": 1334, + "e2": 1548, + "Rc": 1347440720, + "rr": 729, + "gap": 712, + "Vx": 627731 + }, + "min_exact": { + "e1": 0, + "e2": 0, + "rr": 0, + "gap": 0, + "rw": 0, + "ry": 0 + }, + "fit": { + "rng_cells": true, + "delta_split_needed": false, + "sigma_24": true, + "e1_24": true, + "e2_24": true, + "Rc_36": true, + "rr_24": true, + "gap_24": true, + "rw_24": true, + "ry_12": true, + "public_v_31": true, + "public_y_31": true, + "public_cg_24": true, + "public_x_31": true + }, + "all_fit": true, + "div_S": 0.03816041735654266, + "div_y": 1.2254855274411849 + }, + { + "seed": 5, + "dist": "R1", + "steps": 1024, + "kernel": "random", + "max": { + "S": 4130, + "M": 2480, + "delta": 8476, + "delta_lo": 262143, + "delta_hi": 1, + "o": 190, + "w": 2756, + "v": 13173, + "k_hat": 2697, + "q_hat": 238, + "xin": 20901, + "y": 54839, + "cg": 207898, + "sigma": 382, + "e1": 622, + "e2": 569, + "Rc": 1431655765, + "rr": 361, + "gap": 321, + "Vx": 144399 + }, + "min_exact": { + "e1": 0, + "e2": 0, + "rr": 0, + "gap": 0, + "rw": 0, + "ry": 0 + }, + "fit": { + "rng_cells": true, + "delta_split_needed": false, + "sigma_24": true, + "e1_24": true, + "e2_24": true, + "Rc_36": true, + "rr_24": true, + "gap_24": true, + "rw_24": true, + "ry_12": true, + "public_v_31": true, + "public_y_31": true, + "public_cg_24": true, + "public_x_31": true + }, + "all_fit": true, + "div_S": 0.0018535262388349799, + "div_y": 1.0099419350328158 + }, + { + "seed": 1, + "dist": "R2", + "steps": 1024, + "kernel": "random", + "max": { + "S": 9669, + "M": 8577, + "delta": 18417, + "delta_lo": 262143, + "delta_hi": 1, + "o": 668, + "w": 2280, + "v": 26768, + "k_hat": 2863, + "q_hat": 234, + "xin": 36172, + "y": 63637, + "cg": 205057, + "sigma": 1800, + "e1": 3077, + "e2": 2716, + "Rc": 1126548798, + "rr": 1576, + "gap": 1663, + "Vx": 3239204 + }, + "min_exact": { + "e1": 0, + "e2": 0, + "rr": 0, + "gap": 0, + "rw": 0, + "ry": 0 + }, + "fit": { + "rng_cells": true, + "delta_split_needed": false, + "sigma_24": true, + "e1_24": true, + "e2_24": true, + "Rc_36": true, + "rr_24": true, + "gap_24": true, + "rw_24": true, + "ry_12": true, + "public_v_31": true, + "public_y_31": true, + "public_cg_24": true, + "public_x_31": true + }, + "all_fit": true, + "div_S": 0.011878092405511364, + "div_y": 0.4454450767080318 + }, + { + "seed": 2, + "dist": "R2", + "steps": 1024, + "kernel": "random", + "max": { + "S": 10765, + "M": 6927, + "delta": 18380, + "delta_lo": 262143, + "delta_hi": 1, + "o": 415, + "w": 2102, + "v": 23569, + "k_hat": 2874, + "q_hat": 250, + "xin": 36309, + "y": 42999, + "cg": 206901, + "sigma": 1243, + "e1": 2146, + "e2": 1806, + "Rc": 1073741824, + "rr": 991, + "gap": 1023, + "Vx": 1543878 + }, + "min_exact": { + "e1": 0, + "e2": 0, + "rr": 0, + "gap": 0, + "rw": 0, + "ry": 0 + }, + "fit": { + "rng_cells": true, + "delta_split_needed": false, + "sigma_24": true, + "e1_24": true, + "e2_24": true, + "Rc_36": true, + "rr_24": true, + "gap_24": true, + "rw_24": true, + "ry_12": true, + "public_v_31": true, + "public_y_31": true, + "public_cg_24": true, + "public_x_31": true + }, + "all_fit": true, + "div_S": 0.005188915170521599, + "div_y": 0.4542303810018238 + }, + { + "seed": 3, + "dist": "R2", + "steps": 1024, + "kernel": "random", + "max": { + "S": 9075, + "M": 7370, + "delta": 18306, + "delta_lo": 262143, + "delta_hi": 1, + "o": 654, + "w": 2247, + "v": 24401, + "k_hat": 2598, + "q_hat": 248, + "xin": 38741, + "y": 47071, + "cg": 230259, + "sigma": 1672, + "e1": 3316, + "e2": 2684, + "Rc": 1205604855, + "rr": 1336, + "gap": 1343, + "Vx": 2796753 + }, + "min_exact": { + "e1": 0, + "e2": 0, + "rr": 0, + "gap": 0, + "rw": 0, + "ry": 0 + }, + "fit": { + "rng_cells": true, + "delta_split_needed": false, + "sigma_24": true, + "e1_24": true, + "e2_24": true, + "Rc_36": true, + "rr_24": true, + "gap_24": true, + "rw_24": true, + "ry_12": true, + "public_v_31": true, + "public_y_31": true, + "public_cg_24": true, + "public_x_31": true + }, + "all_fit": true, + "div_S": 0.010336854292446018, + "div_y": 0.4603733674255581 + }, + { + "seed": 4, + "dist": "R2", + "steps": 1024, + "kernel": "random", + "max": { + "S": 11021, + "M": 9200, + "delta": 15951, + "delta_lo": 262143, + "delta_hi": 1, + "o": 816, + "w": 2295, + "v": 25643, + "k_hat": 2829, + "q_hat": 254, + "xin": 37103, + "y": 45001, + "cg": 196979, + "sigma": 2001, + "e1": 3705, + "e2": 3577, + "Rc": 1073741824, + "rr": 1939, + "gap": 1831, + "Vx": 4003732 + }, + "min_exact": { + "e1": 0, + "e2": 0, + "rr": 0, + "gap": 0, + "rw": 0, + "ry": 0 + }, + "fit": { + "rng_cells": true, + "delta_split_needed": false, + "sigma_24": true, + "e1_24": true, + "e2_24": true, + "Rc_36": true, + "rr_24": true, + "gap_24": true, + "rw_24": true, + "ry_12": true, + "public_v_31": true, + "public_y_31": true, + "public_cg_24": true, + "public_x_31": true + }, + "all_fit": true, + "div_S": 0.037921908535273594, + "div_y": 0.6839421936249441 + }, + { + "seed": 5, + "dist": "R2", + "steps": 1024, + "kernel": "random", + "max": { + "S": 9057, + "M": 5514, + "delta": 17964, + "delta_lo": 262143, + "delta_hi": 1, + "o": 427, + "w": 2727, + "v": 27255, + "k_hat": 2697, + "q_hat": 238, + "xin": 37339, + "y": 54458, + "cg": 207898, + "sigma": 896, + "e1": 1637, + "e2": 1440, + "Rc": 1296593900, + "rr": 648, + "gap": 700, + "Vx": 801580 + }, + "min_exact": { + "e1": 0, + "e2": 0, + "rr": 0, + "gap": 0, + "rw": 0, + "ry": 0 + }, + "fit": { + "rng_cells": true, + "delta_split_needed": false, + "sigma_24": true, + "e1_24": true, + "e2_24": true, + "Rc_36": true, + "rr_24": true, + "gap_24": true, + "rw_24": true, + "ry_12": true, + "public_v_31": true, + "public_y_31": true, + "public_cg_24": true, + "public_x_31": true + }, + "all_fit": true, + "div_S": 0.0019988409118104483, + "div_y": 0.6020922041842938 + }, + { + "seed": 1, + "dist": "R3", + "steps": 1024, + "kernel": "random", + "max": { + "S": 49299, + "M": 48573, + "delta": 151336, + "delta_lo": 262143, + "delta_hi": 1, + "o": 2768, + "w": 3554, + "v": 166204, + "k_hat": 2789, + "q_hat": 219, + "xin": 388587, + "y": 67348, + "cg": 205057, + "sigma": 3519, + "e1": 6328, + "e2": 5963, + "Rc": 1205604855, + "rr": 2746, + "gap": 2663, + "Vx": 12387542 + }, + "min_exact": { + "e1": 0, + "e2": 0, + "rr": 0, + "gap": 0, + "rw": 0, + "ry": 0 + }, + "fit": { + "rng_cells": true, + "delta_split_needed": false, + "sigma_24": true, + "e1_24": true, + "e2_24": true, + "Rc_36": true, + "rr_24": true, + "gap_24": true, + "rw_24": true, + "ry_12": true, + "public_v_31": true, + "public_y_31": true, + "public_cg_24": true, + "public_x_31": true + }, + "all_fit": true, + "div_S": 0.012992215626891568, + "div_y": 0.38122247378073737 + }, + { + "seed": 2, + "dist": "R3", + "steps": 1024, + "kernel": "random", + "max": { + "S": 150189, + "M": 45186, + "delta": 282387, + "delta_lo": 262143, + "delta_hi": 1, + "o": 6379, + "w": 4048, + "v": 470159, + "k_hat": 2697, + "q_hat": 257, + "xin": 1022655, + "y": 69617, + "cg": 201163, + "sigma": 6454, + "e1": 12148, + "e2": 5386, + "Rc": 941362695, + "rr": 2396, + "gap": 4583, + "Vx": 41664117 + }, + "min_exact": { + "e1": 0, + "e2": 0, + "rr": 0, + "gap": 0, + "rw": 0, + "ry": 0 + }, + "fit": { + "rng_cells": true, + "delta_split_needed": true, + "sigma_24": true, + "e1_24": true, + "e2_24": true, + "Rc_36": true, + "rr_24": true, + "gap_24": true, + "rw_24": true, + "ry_12": true, + "public_v_31": true, + "public_y_31": true, + "public_cg_24": true, + "public_x_31": true + }, + "all_fit": true, + "div_S": 0.021333899687325797, + "div_y": 0.4346130981689871 + }, + { + "seed": 3, + "dist": "R3", + "steps": 1024, + "kernel": "random", + "max": { + "S": 54105, + "M": 35671, + "delta": 105549, + "delta_lo": 262143, + "delta_hi": 1, + "o": 2514, + "w": 3299, + "v": 149901, + "k_hat": 2971, + "q_hat": 237, + "xin": 436628, + "y": 73375, + "cg": 194628, + "sigma": 3764, + "e1": 7057, + "e2": 5351, + "Rc": 928641577, + "rr": 2912, + "gap": 3003, + "Vx": 14172606 + }, + "min_exact": { + "e1": 0, + "e2": 0, + "rr": 0, + "gap": 0, + "rw": 0, + "ry": 0 + }, + "fit": { + "rng_cells": true, + "delta_split_needed": false, + "sigma_24": true, + "e1_24": true, + "e2_24": true, + "Rc_36": true, + "rr_24": true, + "gap_24": true, + "rw_24": true, + "ry_12": true, + "public_v_31": true, + "public_y_31": true, + "public_cg_24": true, + "public_x_31": true + }, + "all_fit": true, + "div_S": 0.011543801091357886, + "div_y": 0.3740515807074658 + }, + { + "seed": 4, + "dist": "R3", + "steps": 1024, + "kernel": "random", + "max": { + "S": 84016, + "M": 53294, + "delta": 157947, + "delta_lo": 262143, + "delta_hi": 1, + "o": 4288, + "w": 3723, + "v": 527711, + "k_hat": 2753, + "q_hat": 221, + "xin": 1478242, + "y": 72605, + "cg": 183791, + "sigma": 4717, + "e1": 6597, + "e2": 7253, + "Rc": 1431655765, + "rr": 4264, + "gap": 3778, + "Vx": 22253117 + }, + "min_exact": { + "e1": 0, + "e2": 0, + "rr": 0, + "gap": 0, + "rw": 0, + "ry": 0 + }, + "fit": { + "rng_cells": true, + "delta_split_needed": false, + "sigma_24": true, + "e1_24": true, + "e2_24": true, + "Rc_36": true, + "rr_24": true, + "gap_24": true, + "rw_24": true, + "ry_12": true, + "public_v_31": true, + "public_y_31": true, + "public_cg_24": true, + "public_x_31": true + }, + "all_fit": true, + "div_S": 0.03978339234812325, + "div_y": 0.4893054011804668 + }, + { + "seed": 5, + "dist": "R3", + "steps": 1024, + "kernel": "random", + "max": { + "S": 38716, + "M": 30406, + "delta": 111118, + "delta_lo": 262143, + "delta_hi": 1, + "o": 1387, + "w": 3602, + "v": 364819, + "k_hat": 2988, + "q_hat": 246, + "xin": 869012, + "y": 101976, + "cg": 189598, + "sigma": 1758, + "e1": 3397, + "e2": 3055, + "Rc": 1025663831, + "rr": 1198, + "gap": 1611, + "Vx": 3090193 + }, + "min_exact": { + "e1": 0, + "e2": 0, + "rr": 0, + "gap": 0, + "rw": 0, + "ry": 0 + }, + "fit": { + "rng_cells": true, + "delta_split_needed": false, + "sigma_24": true, + "e1_24": true, + "e2_24": true, + "Rc_36": true, + "rr_24": true, + "gap_24": true, + "rw_24": true, + "ry_12": true, + "public_v_31": true, + "public_y_31": true, + "public_cg_24": true, + "public_x_31": true + }, + "all_fit": true, + "div_S": 0.008907953502454546, + "div_y": 0.6735961455026331 + }, + { + "seed": 1, + "dist": "R4", + "steps": 1024, + "kernel": "random", + "max": { + "S": 28044, + "M": 25850, + "delta": 26290, + "delta_lo": 262143, + "delta_hi": 1, + "o": 1840, + "w": 3102, + "v": 36478, + "k_hat": 2863, + "q_hat": 234, + "xin": 32743, + "y": 89727, + "cg": 205057, + "sigma": 2938, + "e1": 5606, + "e2": 3992, + "Rc": 1126548798, + "rr": 2384, + "gap": 2455, + "Vx": 8635303 + }, + "min_exact": { + "e1": 0, + "e2": 0, + "rr": 0, + "gap": 0, + "rw": 0, + "ry": 0 + }, + "fit": { + "rng_cells": true, + "delta_split_needed": false, + "sigma_24": true, + "e1_24": true, + "e2_24": true, + "Rc_36": true, + "rr_24": true, + "gap_24": true, + "rw_24": true, + "ry_12": true, + "public_v_31": true, + "public_y_31": true, + "public_cg_24": true, + "public_x_31": true + }, + "all_fit": true, + "div_S": 0.012131181150351611, + "div_y": 0.605252099087517 + }, + { + "seed": 2, + "dist": "R4", + "steps": 1024, + "kernel": "random", + "max": { + "S": 16205, + "M": 16119, + "delta": 20678, + "delta_lo": 262143, + "delta_hi": 1, + "o": 1244, + "w": 3272, + "v": 32443, + "k_hat": 2874, + "q_hat": 250, + "xin": 34376, + "y": 65194, + "cg": 206901, + "sigma": 1889, + "e1": 3023, + "e2": 3554, + "Rc": 1090785345, + "rr": 1600, + "gap": 1651, + "Vx": 3569101 + }, + "min_exact": { + "e1": 0, + "e2": 0, + "rr": 0, + "gap": 0, + "rw": 0, + "ry": 0 + }, + "fit": { + "rng_cells": true, + "delta_split_needed": false, + "sigma_24": true, + "e1_24": true, + "e2_24": true, + "Rc_36": true, + "rr_24": true, + "gap_24": true, + "rw_24": true, + "ry_12": true, + "public_v_31": true, + "public_y_31": true, + "public_cg_24": true, + "public_x_31": true + }, + "all_fit": true, + "div_S": 0.006178498091920059, + "div_y": 0.4488203822640603 + }, + { + "seed": 3, + "dist": "R4", + "steps": 1024, + "kernel": "random", + "max": { + "S": 26109, + "M": 21544, + "delta": 23600, + "delta_lo": 262143, + "delta_hi": 1, + "o": 2087, + "w": 3321, + "v": 35674, + "k_hat": 2598, + "q_hat": 248, + "xin": 39304, + "y": 72436, + "cg": 230259, + "sigma": 2996, + "e1": 4921, + "e2": 4495, + "Rc": 1321528398, + "rr": 2368, + "gap": 2959, + "Vx": 8978790 + }, + "min_exact": { + "e1": 0, + "e2": 0, + "rr": 0, + "gap": 0, + "rw": 0, + "ry": 0 + }, + "fit": { + "rng_cells": true, + "delta_split_needed": false, + "sigma_24": true, + "e1_24": true, + "e2_24": true, + "Rc_36": true, + "rr_24": true, + "gap_24": true, + "rw_24": true, + "ry_12": true, + "public_v_31": true, + "public_y_31": true, + "public_cg_24": true, + "public_x_31": true + }, + "all_fit": true, + "div_S": 0.012081004182579846, + "div_y": 0.5209488829895685 + }, + { + "seed": 4, + "dist": "R4", + "steps": 1024, + "kernel": "random", + "max": { + "S": 21927, + "M": 24545, + "delta": 18461, + "delta_lo": 262143, + "delta_hi": 1, + "o": 2128, + "w": 3040, + "v": 35976, + "k_hat": 2829, + "q_hat": 254, + "xin": 38117, + "y": 70078, + "cg": 196979, + "sigma": 3292, + "e1": 5810, + "e2": 6262, + "Rc": 1010580540, + "rr": 2968, + "gap": 2743, + "Vx": 10835439 + }, + "min_exact": { + "e1": 0, + "e2": 0, + "rr": 0, + "gap": 0, + "rw": 0, + "ry": 0 + }, + "fit": { + "rng_cells": true, + "delta_split_needed": false, + "sigma_24": true, + "e1_24": true, + "e2_24": true, + "Rc_36": true, + "rr_24": true, + "gap_24": true, + "rw_24": true, + "ry_12": true, + "public_v_31": true, + "public_y_31": true, + "public_cg_24": true, + "public_x_31": true + }, + "all_fit": true, + "div_S": 0.038746587142156716, + "div_y": 0.6040994159623554 + }, + { + "seed": 5, + "dist": "R4", + "steps": 1024, + "kernel": "random", + "max": { + "S": 14188, + "M": 9544, + "delta": 25182, + "delta_lo": 262143, + "delta_hi": 1, + "o": 853, + "w": 3039, + "v": 35281, + "k_hat": 2697, + "q_hat": 238, + "xin": 36071, + "y": 84370, + "cg": 207898, + "sigma": 1244, + "e1": 1942, + "e2": 1648, + "Rc": 1227133513, + "rr": 1152, + "gap": 927, + "Vx": 1546432 + }, + "min_exact": { + "e1": 0, + "e2": 0, + "rr": 0, + "gap": 0, + "rw": 0, + "ry": 0 + }, + "fit": { + "rng_cells": true, + "delta_split_needed": false, + "sigma_24": true, + "e1_24": true, + "e2_24": true, + "Rc_36": true, + "rr_24": true, + "gap_24": true, + "rw_24": true, + "ry_12": true, + "public_v_31": true, + "public_y_31": true, + "public_cg_24": true, + "public_x_31": true + }, + "all_fit": true, + "div_S": 0.003337717820431907, + "div_y": 0.7603817260288137 + } + ] +} diff --git a/research/operators/qwen_deltanet_m2/README.md b/research/operators/qwen_deltanet_m2/README.md new file mode 100644 index 00000000..277cf545 --- /dev/null +++ b/research/operators/qwen_deltanet_m2/README.md @@ -0,0 +1,20 @@ +# Qwen DeltaNet M2 design prototypes (research only) + +These are prototypes for the M2 design. They do not prove M2. Design, PREREG and verdict: `experiments/20261105-32-qwen-deltanet-m2-design/` (DESIGN.md, RESULTS.md). + +- `admit_proto.cpp` with `m1b_builder.h`: a verifier entry point `verify_from_words` that works on the M1b statement. It runs in this order: + 1. Decode every public word (it must be canonical and inside its integer class). + 2. Derive the relations from the decoded integers. + 3. Run the unchanged `lookup_verify`. + + The test suite covers huge field elements, class bounds, non-canonical words, header and length mismatches, and the #63 review's R9. `m1b_builder.h` is the #63 builder and prover, split out with a relation-only switch. +- `count_model.py`: counts columns, relations, terms, range cells and LKP keys for the lowering. It reproduces M1 and M1b exactly. It also has arm A (exact remainder bits) and arm B (lookup-exact remainders, direct LKP keys into a 2^12 table), with the M2 head added (private o, gated RMSNorm, T = 16). +- `head_reference.py`: an integer reference of one V head, from the in_proj outputs to the gated-norm output, with a float64 twin. Used for the range study. + +Reproduce (CPU, about 12 min on 6 cores, about 4.5 GB RAM): + +``` +python3 research/operators/qwen_deltanet_m2/record.py build honest hostile model ranges +``` + +Research security settings only (target 10 bits). diff --git a/research/operators/qwen_deltanet_m2/evidence.json b/research/operators/qwen_deltanet_m2/evidence.json new file mode 100644 index 00000000..e704c221 --- /dev/null +++ b/research/operators/qwen_deltanet_m2/evidence.json @@ -0,0 +1,346 @@ +{ + "platform": "Linux-6.8.0-100-generic-x86_64-with-glibc2.39", + "python": "3.12.3", + "cores": "0-5", + "compiler": "g++ (Ubuntu 13.3.0-6ubuntu2~24.04.1) 13.3.0", + "base": "b6186e6ccb687348e3be66fec51bb3756879b8dc", + "sources": { + "research/operators/qwen_deltanet_m2/count_model.py": "9136d5ab4a6ebbc8999c1829f1be2ef39469d019abf16a9699411f3002271499", + "research/operators/qwen_deltanet_m2/head_reference.py": "97f0586c1878969035c0bbd00f1cc684522b003c49db35bdb9acf35b576f5626", + "research/operators/qwen_deltanet_m2/record.py": "25fe5d53ceba293114a1c1bf2c018db08c910784efde3761247e112a3f8ddf2a", + "research/operators/qwen_deltanet_m2/admit_proto.cpp": "8c6a780efc965e87be6433e466e5326e38d37223e9f716269267fb40ace0611d", + "research/operators/qwen_deltanet_m2/m1b_builder.h": "a66d4b372400a20af89869fffb2db8c9c118b99644740699cd9939f6154c47f0", + "research/operators/qwen_deltanet_lookup/lookup.h": "04bc74b28fb34f5a3a20ce7413d1310297c7fbb2f22a8da7aa9ae5caaaefd770", + "research/operators/qwen_deltanet/reference.py": "f6fbd580bf834afd2d6280036ddf52d410be094824299e89c61eb2a1b80edaf0" + }, + "fixtures": { + "1": { + "meta": { + "seed": 1, + "S0_max_abs": 1694, + "S4_max_abs": 443, + "decays": [ + 2440, + 1208, + 996, + 111 + ], + "betas": [ + 1511, + 2018, + 572, + 799 + ] + }, + "sha256": "7f86dc8bda6758075a5b6210a4c026d5a97ae2df77d2d3423b251b4df3b22772" + }, + "2": { + "meta": { + "seed": 2, + "S0_max_abs": 1949, + "S4_max_abs": 2541, + "decays": [ + 4028, + 4014, + 3935, + 4063 + ], + "betas": [ + 2197, + 2120, + 2965, + 2514 + ] + }, + "sha256": "4d919856d214adba60d64ae86dc036a50c996fd86289aec6651ea4ba0e753ff8" + }, + "3": { + "meta": { + "seed": 3, + "S0_max_abs": 3417, + "S4_max_abs": 3059, + "decays": [ + 4083, + 3961, + 4064, + 4019 + ], + "betas": [ + 2975, + 1307, + 2589, + 917 + ] + }, + "sha256": "6126b044c6eececc3b22156ca4f6d008bdd464342058fffeb4bee010bac0fc07" + }, + "4": { + "meta": { + "seed": 4, + "S0_max_abs": 1293, + "S4_max_abs": 1689, + "decays": [ + 3612, + 3391, + 3591, + 3629 + ], + "betas": [ + 1059, + 684, + 3075, + 587 + ] + }, + "sha256": "761c94c0189f200510ce757c8377ad47a71e2c6c897f0a1c5ba57e14e47d8f85" + }, + "5": { + "meta": { + "seed": 5, + "S0_max_abs": 1191, + "S4_max_abs": 964, + "decays": [ + 3131, + 3850, + 3502, + 2311 + ], + "betas": [ + 2732, + 841, + 1444, + 963 + ] + }, + "sha256": "aac3d7e59fdb55d700101d26c0af7f6405b173220a214404737b6e7d4e6163dd" + }, + "6": { + "meta": { + "seed": 6, + "S0_max_abs": 2156, + "S4_max_abs": 2162, + "decays": [ + 3929, + 3611, + 4040, + 4015 + ], + "betas": [ + 2850, + 2207, + 3257, + 1902 + ] + }, + "sha256": "1b2e5ed24f777da539d8ced79d63e0c8f46b96240d75396bc97980ecb83bfa53" + }, + "7": { + "meta": { + "seed": 7, + "S0_max_abs": 768, + "S4_max_abs": 850, + "decays": [ + 3291, + 1882, + 2334, + 2746 + ], + "betas": [ + 3065, + 398, + 1140, + 1423 + ] + }, + "sha256": "2538419cdcbe58c98c2f0caa7dc40ea5e48b50aa6cd9185d0099c598cdfefa7c" + }, + "8": { + "meta": { + "seed": 8, + "S0_max_abs": 901, + "S4_max_abs": 827, + "decays": [ + 2708, + 194, + 1869, + 2264 + ], + "betas": [ + 1725, + 2536, + 2070, + 1612 + ] + }, + "sha256": "0c0c3ae3cf8a1c597ede70f8487c34742f174a569dd0e802ad629881533c1915" + }, + "9": { + "meta": { + "seed": 9, + "S0_max_abs": 2076, + "S4_max_abs": 1629, + "decays": [ + 3741, + 4059, + 3505, + 3979 + ], + "betas": [ + 384, + 2249, + 902, + 1878 + ] + }, + "sha256": "de1d5924325874b8c74313a8c043c873127f4e7dc122aa3b79c70d84cd97ea18" + }, + "10": { + "meta": { + "seed": 10, + "S0_max_abs": 1699, + "S4_max_abs": 1797, + "decays": [ + 3998, + 3559, + 3351, + 4042 + ], + "betas": [ + 1637, + 2751, + 1777, + 2565 + ] + }, + "sha256": "9b0de48cd2c1781d58eaf1f0ec748f3693c83e4dcf25bf389bbee7c049b41164" + } + }, + "compile": { + "argv": [ + "g++", + "-std=c++17", + "-O2", + "-Wall", + "-Wextra", + "-fno-ipa-modref", + "-DBLK_FIELD=2", + "-DBLK_HASH=1", + "-I", + "blocks", + "research/operators/qwen_deltanet_m2/admit_proto.cpp", + "-o", + "research/operators/qwen_deltanet_m2/work/admit_proto.bin" + ], + "exit": 0, + "seconds": 32.701, + "stdout": "", + "stderr": "In file included from research/operators/qwen_deltanet_m2/admit_proto.cpp:10:\nresearch/operators/qwen_deltanet_m2/m1b_builder.h:48:13: warning: \u2018bool admissible(const Fixture&)\u2019 defined but not used [-Wunused-function]\n 48 | static bool admissible(const Fixture &z) {\n | ^~~~~~~~~~\n" + }, + "honest": { + "accepted": 10, + "of": 10, + "prove_s": { + "n": 10, + "median": 39.912, + "sd": 0.2351061462403741, + "min": 39.636, + "max": 40.478 + }, + "decode_s": { + "n": 10, + "median": 0.0001, + "sd": 0.0, + "min": 0.0001, + "max": 0.0001 + }, + "derive_s": { + "n": 10, + "median": 0.182, + "sd": 0.006961002482082273, + "min": 0.17, + "max": 0.196 + }, + "verify_s": { + "n": 10, + "median": 7.6129999999999995, + "sd": 0.11390912557336605, + "min": 7.527, + "max": 7.861 + }, + "proof_bytes": { + "n": 10, + "median": 25411392.0, + "sd": 313.09891940620514, + "min": 25410848, + "max": 25411968 + }, + "peak_rss_kb": { + "n": 10, + "median": 4433912.0, + "sd": 53.57279574975012, + "min": 4433784, + "max": 4433912 + } + }, + "hostile": { + "exit": 0, + "seconds": 106.764, + "as_expected": 34, + "total": 34, + "by_class": { + "huge-element": [ + 8, + 8 + ], + "class-bound": [ + 16, + 16 + ], + "non-canonical": [ + 2, + 2 + ], + "header": [ + 2, + 2 + ], + "length": [ + 2, + 2 + ], + "in-class": [ + 2, + 2 + ], + "r9-control": [ + 1, + 1 + ], + "r9": [ + 1, + 1 + ] + }, + "stderr": "" + }, + "model": { + "exit": 0, + "seconds": 0.884, + "stdout": "{\n \"H1\": {\n \"checks\": {\n \"m1\": {\n \"columns_used\": [\n 2338816,\n 2338816,\n true\n ],\n \"log_length\": [\n 22,\n 22,\n true\n ],\n \"relations\": [\n 4761088,\n 4761088,\n true\n ],\n \"terms\": [\n 11430400,\n 11430400,\n true\n ],\n \"range_cells\": [\n 0,\n 0,\n true\n ]\n },\n \"m1b\": {\n \"columns_used\": [\n 1310720,\n 1310720,\n true\n ],\n \"columns_nonregion\": [\n 1074176,\n 1074176,\n true\n ],\n \"log_length\": [\n 21,\n 21,\n true\n ],\n \"relations\": [\n 2231808,\n 2231808,\n true\n ],\n \"terms\": [\n 5174272,\n 5174272,\n true\n ],\n \"range_cells\": [\n 67072,\n 67072,\n true\n ]\n }\n },\n \"pass_\": true,\n \"model_m1\": {\n \"arm\": \"m1\",\n \"D\": 128,\n \"T\": 4,\n \"o_private\": false,\n \"norm\": false,\n \"gate_columns\": 2338816,\n \"range_cells\": 0,\n \"lkp_keys\": 0,\n \"relations\": 4761088,\n \"terms\": 11430400,\n \"product_columns_per_state_cell_per_token\": 35.688,\n \"relations_per_state_cell_per_token\": 72.648,\n \"columns_used\": 2338816,\n \"layout\": 2338816,\n \"log_length\": 22\n },\n \"model_m1b\": {\n \"arm\": \"m1b\",\n \"D\": 128,\n \"T\": 4,\n \"o_private\": false,\n \"norm\": false,\n \"gate_columns\": 1074176,\n \"range_cells\": 67072,\n \"lkp_keys\": 0,\n \"relations\": 2231808,\n \"terms\": 5174272,\n \"product_columns_per_state_cell_per_token\": 17.414,\n \"relations_per_state_cell_per_token\": 34.055,\n \"columns_used\": 1310720,\n \"columns_nonregion\": 1074176,\n \"rng_rows\": 8192,\n \"log_length\": 21\n }\n },\n \"H2\": {\n \"armA_m1b_shape\": {\n \"arm\": \"A\",\n \"D\": 128,\n \"T\": 4,\n \"o_private\": false,\n \"norm\": false,\n \"gate_columns\": 1074176,\n \"range_cells\": 67072,\n \"lkp_keys\": 0,\n \"relations\": 2231808,\n \"terms\": 5174272,\n \"product_columns_per_state_cell_per_token\": 17.414,\n \"relations_per_state_cell_per_token\": 34.055,\n \"columns_used\": 1156096,\n \"columns_nonregion\": 1074176,\n \"rng_rows\": [\n 4096,\n 1024\n ],\n \"key_parts\": [],\n \"digit_entries\": 102400,\n \"log_length\": 21\n },\n \"armB_m1b_shape\": {\n \"arm\": \"B\",\n \"D\": 128,\n \"T\": 4,\n \"o_private\": false,\n \"norm\": false,\n \"gate_columns\": 263168,\n \"range_cells\": 67072,\n \"lkp_keys\": 67584,\n \"relations\": 609792,\n \"terms\": 1186816,\n \"product_columns_per_state_cell_per_token\": 6.07,\n \"relations_per_state_cell_per_token\": 9.305,\n \"columns_used\": 427008,\n \"columns_nonregion\": 263168,\n \"rng_rows\": [\n 4096,\n 1024\n ],\n \"key_parts\": [\n 65536,\n 16384\n ],\n \"digit_entries\": 102400,\n \"log_length\": 19\n },\n \"ratio_columns_per_cell\": 0.349,\n \"ratio_relations\": 0.273,\n \"ratio_terms\": 0.229,\n \"pass_\": true,\n \"note\": \"arm A at the M1b shape equals M1b (same lowering); arm B swaps exact bits for direct LKP keys\"\n },\n \"H3\": {\n \"armA_T16\": {\n \"arm\": \"A\",\n \"D\": 128,\n \"T\": 16,\n \"o_private\": true,\n \"norm\": true,\n \"gate_columns\": 4377056,\n \"range_cells\": 272384,\n \"lkp_keys\": 0,\n \"relations\": 9042944,\n \"terms\": 21110960,\n \"product_columns_per_state_cell_per_token\": 17.736,\n \"relations_per_state_cell_per_token\": 34.496,\n \"columns_used\": 4655584,\n \"columns_nonregion\": 4377056,\n \"rng_rows\": [\n 16384,\n 1024\n ],\n \"key_parts\": [],\n \"digit_entries\": 348160,\n \"log_length\": 23,\n \"prove_s_estimate\": null\n },\n \"armB_T16\": {\n \"arm\": \"B\",\n \"D\": 128,\n \"T\": 16,\n \"o_private\": true,\n \"norm\": true,\n \"gate_columns\": 1056800,\n \"range_cells\": 272384,\n \"lkp_keys\": 276688,\n \"relations\": 2402432,\n \"terms\": 4786704,\n \"product_columns_per_state_cell_per_token\": 6.126,\n \"relations_per_state_cell_per_token\": 9.165,\n \"columns_used\": 1613856,\n \"columns_nonregion\": 1056800,\n \"rng_rows\": [\n 16384,\n 1024\n ],\n \"key_parts\": [\n 262144,\n 16384\n ],\n \"digit_entries\": 348160,\n \"log_length\": 21,\n \"prove_s_estimate\": 39.2\n },\n \"max_T_log22\": {\n \"A\": 14,\n \"B\": 41\n },\n \"layer_extrapolation\": {\n \"A\": {\n \"relations_per_token_per_layer\": 27128832,\n \"columns_per_token_per_layer\": 13948320,\n \"columns_per_token_48_deltanet_layers\": 669519360\n },\n \"B\": {\n \"relations_per_token_per_layer\": 7207296,\n \"columns_per_token_per_layer\": 4817616,\n \"columns_per_token_48_deltanet_layers\": 231245568\n }\n },\n \"pass_\": true,\n \"note\": \"prove_s_estimate is linear in table length from #47/#63 points; indicative only. Layer extrapolation = 48 V heads x per-head-per-token; DeltaNet recurrence + gated norm only (no matmuls).\"\n },\n \"remainder_savings\": {\n \"armA_T16_columns\": 4649440,\n \"armB_T16_columns\": 1605872,\n \"relations_A\": 9042944,\n \"relations_B\": 2402432,\n \"terms_A\": 21110960,\n \"terms_B\": 4786704\n }\n}\n" + }, + "ranges": { + "exit": 0, + "seconds": 14.678, + "stdout": "{\"dist\": \"R1\", \"seed\": 1, \"all_fit\": true, \"S\": 4374, \"delta\": 8638, \"o\": 295, \"w\": 2386, \"sigma\": 621, \"Rc\": 1374389534, \"div_S\": 0.0118, \"div_y\": 0.888}\n{\"dist\": \"R1\", \"seed\": 2, \"all_fit\": true, \"S\": 4866, \"delta\": 8769, \"o\": 175, \"w\": 2102, \"sigma\": 500, \"Rc\": 1374389534, \"div_S\": 0.0054, \"div_y\": 0.8749}\n{\"dist\": \"R1\", \"seed\": 3, \"all_fit\": true, \"S\": 4075, \"delta\": 8561, \"o\": 290, \"w\": 2404, \"sigma\": 656, \"Rc\": 1402438300, \"div_S\": 0.0108, \"div_y\": 1.0176}\n{\"dist\": \"R1\", \"seed\": 4, \"all_fit\": true, \"S\": 5134, \"delta\": 7572, \"o\": 351, \"w\": 2281, \"sigma\": 793, \"Rc\": 1347440720, \"div_S\": 0.0382, \"div_y\": 1.2255}\n{\"dist\": \"R1\", \"seed\": 5, \"all_fit\": true, \"S\": 4130, \"delta\": 8476, \"o\": 190, \"w\": 2756, \"sigma\": 382, \"Rc\": 1431655765, \"div_S\": 0.0019, \"div_y\": 1.0099}\n{\"dist\": \"R2\", \"seed\": 1, \"all_fit\": true, \"S\": 9669, \"delta\": 18417, \"o\": 668, \"w\": 2280, \"sigma\": 1800, \"Rc\": 1126548798, \"div_S\": 0.0119, \"div_y\": 0.4454}\n{\"dist\": \"R2\", \"seed\": 2, \"all_fit\": true, \"S\": 10765, \"delta\": 18380, \"o\": 415, \"w\": 2102, \"sigma\": 1243, \"Rc\": 1073741824, \"div_S\": 0.0052, \"div_y\": 0.4542}\n{\"dist\": \"R2\", \"seed\": 3, \"all_fit\": true, \"S\": 9075, \"delta\": 18306, \"o\": 654, \"w\": 2247, \"sigma\": 1672, \"Rc\": 1205604855, \"div_S\": 0.0103, \"div_y\": 0.4604}\n{\"dist\": \"R2\", \"seed\": 4, \"all_fit\": true, \"S\": 11021, \"delta\": 15951, \"o\": 816, \"w\": 2295, \"sigma\": 2001, \"Rc\": 1073741824, \"div_S\": 0.0379, \"div_y\": 0.6839}\n{\"dist\": \"R2\", \"seed\": 5, \"all_fit\": true, \"S\": 9057, \"delta\": 17964, \"o\": 427, \"w\": 2727, \"sigma\": 896, \"Rc\": 1296593900, \"div_S\": 0.002, \"div_y\": 0.6021}\n{\"dist\": \"R3\", \"seed\": 1, \"all_fit\": true, \"S\": 49299, \"delta\": 151336, \"o\": 2768, \"w\": 3554, \"sigma\": 3519, \"Rc\": 1205604855, \"div_S\": 0.013, \"div_y\": 0.3812}\n{\"dist\": \"R3\", \"seed\": 2, \"all_fit\": true, \"S\": 150189, \"delta\": 282387, \"o\": 6379, \"w\": 4048, \"sigma\": 6454, \"Rc\": 941362695, \"div_S\": 0.0213, \"div_y\": 0.4346}\n{\"dist\": \"R3\", \"seed\": 3, \"all_fit\": true, \"S\": 54105, \"delta\": 105549, \"o\": 2514, \"w\": 3299, \"sigma\": 3764, \"Rc\": 928641577, \"div_S\": 0.0115, \"div_y\": 0.3741}\n{\"dist\": \"R3\", \"seed\": 4, \"all_fit\": true, \"S\": 84016, \"delta\": 157947, \"o\": 4288, \"w\": 3723, \"sigma\": 4717, \"Rc\": 1431655765, \"div_S\": 0.0398, \"div_y\": 0.4893}\n{\"dist\": \"R3\", \"seed\": 5, \"all_fit\": true, \"S\": 38716, \"delta\": 111118, \"o\": 1387, \"w\": 3602, \"sigma\": 1758, \"Rc\": 1025663831, \"div_S\": 0.0089, \"div_y\": 0.6736}\n{\"dist\": \"R4\", \"seed\": 1, \"all_fit\": true, \"S\": 28044, \"delta\": 26290, \"o\": 1840, \"w\": 3102, \"sigma\": 2938, \"Rc\": 1126548798, \"div_S\": 0.0121, \"div_y\": 0.6053}\n{\"dist\": \"R4\", \"seed\": 2, \"all_fit\": true, \"S\": 16205, \"delta\": 20678, \"o\": 1244, \"w\": 3272, \"sigma\": 1889, \"Rc\": 1090785345, \"div_S\": 0.0062, \"div_y\": 0.4488}\n{\"dist\": \"R4\", \"seed\": 3, \"all_fit\": true, \"S\": 26109, \"delta\": 23600, \"o\": 2087, \"w\": 3321, \"sigma\": 2996, \"Rc\": 1321528398, \"div_S\": 0.0121, \"div_y\": 0.5209}\n{\"dist\": \"R4\", \"seed\": 4, \"all_fit\": true, \"S\": 21927, \"delta\": 18461, \"o\": 2128, \"w\": 3040, \"sigma\": 3292, \"Rc\": 1010580540, \"div_S\": 0.0387, \"div_y\": 0.6041}\n{\"dist\": \"R4\", \"seed\": 5, \"all_fit\": true, \"S\": 14188, \"delta\": 25182, \"o\": 853, \"w\": 3039, \"sigma\": 1244, \"Rc\": 1227133513, \"div_S\": 0.0033, \"div_y\": 0.7604}\n{\"kernel\": \"random\", \"q_unscaled\": false, \"steps\": 1024, \"n\": 20, \"fit\": 20, \"max\": {\"S\": 150189, \"M\": 53294, \"delta\": 282387, \"delta_lo\": 262143, \"delta_hi\": 1, \"o\": 6379, \"w\": 4048, \"v\": 527711, \"k_hat\": 2988, \"q_hat\": 257, \"xin\": 1478242, \"y\": 101976, \"cg\": 230259, \"sigma\": 6454, \"e1\": 12148, \"e2\": 7253, \"Rc\": 1431655765, \"rr\": 4264, \"gap\": 4583, \"Vx\": 41664117}, \"worst_div_S\": 0.03978339234812325, \"worst_div_y\": 1.2254855274411849, \"seconds\": 14.5}\n" + }, + "ranges_q-unscaled": { + "exit": 0, + "seconds": 14.346, + "stdout": "d\": 5, \"all_fit\": true, \"S\": 4130, \"delta\": 8476, \"o\": 2162, \"w\": 2761, \"sigma\": 4361, \"Rc\": 127023062, \"div_S\": 0.0019, \"div_y\": 0.488}\n{\"dist\": \"R2\", \"seed\": 1, \"all_fit\": true, \"S\": 9669, \"delta\": 18417, \"o\": 7544, \"w\": 2276, \"sigma\": 20464, \"Rc\": 96651866, \"div_S\": 0.0119, \"div_y\": 0.3317}\n{\"dist\": \"R2\", \"seed\": 2, \"all_fit\": true, \"S\": 10765, \"delta\": 18380, \"o\": 4714, \"w\": 2098, \"sigma\": 14089, \"Rc\": 94654926, \"div_S\": 0.0052, \"div_y\": 0.2976}\n{\"dist\": \"R2\", \"seed\": 3, \"all_fit\": true, \"S\": 9075, \"delta\": 18306, \"o\": 7398, \"w\": 2239, \"sigma\": 18960, \"Rc\": 105722271, \"div_S\": 0.0103, \"div_y\": 0.419}\n{\"dist\": \"R2\", \"seed\": 4, \"all_fit\": true, \"S\": 11021, \"delta\": 15951, \"o\": 9252, \"w\": 2295, \"sigma\": 22699, \"Rc\": 96651866, \"div_S\": 0.0379, \"div_y\": 0.5875}\n{\"dist\": \"R2\", \"seed\": 5, \"all_fit\": true, \"S\": 9057, \"delta\": 17964, \"o\": 4845, \"w\": 2723, \"sigma\": 10193, \"Rc\": 112470502, \"div_S\": 0.002, \"div_y\": 0.3638}\n{\"dist\": \"R3\", \"seed\": 1, \"all_fit\": true, \"S\": 49299, \"delta\": 151336, \"o\": 31162, \"w\": 3554, \"sigma\": 39730, \"Rc\": 104120419, \"div_S\": 0.013, \"div_y\": 0.178}\n{\"dist\": \"R3\", \"seed\": 2, \"all_fit\": true, \"S\": 150189, \"delta\": 282387, \"o\": 72479, \"w\": 4047, \"sigma\": 73340, \"Rc\": 85899345, \"div_S\": 0.0213, \"div_y\": 0.1955}\n{\"dist\": \"R3\", \"seed\": 3, \"all_fit\": true, \"S\": 54105, \"delta\": 105549, \"o\": 28504, \"w\": 3298, \"sigma\": 42798, \"Rc\": 82794550, \"div_S\": 0.0115, \"div_y\": 0.2574}\n{\"dist\": \"R3\", \"seed\": 4, \"all_fit\": true, \"S\": 84016, \"delta\": 157947, \"o\": 48292, \"w\": 3720, \"sigma\": 53164, \"Rc\": 128207978, \"div_S\": 0.0398, \"div_y\": 0.3457}\n{\"dist\": \"R3\", \"seed\": 5, \"all_fit\": true, \"S\": 38716, \"delta\": 111118, \"o\": 15723, \"w\": 3594, \"sigma\": 19951, \"Rc\": 91503963, \"div_S\": 0.0089, \"div_y\": 0.2051}\n{\"dist\": \"R4\", \"seed\": 1, \"all_fit\": true, \"S\": 28044, \"delta\": 26290, \"o\": 20858, \"w\": 3101, \"sigma\": 33211, \"Rc\": 96651866, \"div_S\": 0.0121, \"div_y\": 0.3795}\n{\"dist\": \"R4\", \"seed\": 2, \"all_fit\": true, \"S\": 16205, \"delta\": 20678, \"o\": 14126, \"w\": 3270, \"sigma\": 21380, \"Rc\": 96651866, \"div_S\": 0.0062, \"div_y\": 0.3277}\n{\"dist\": \"R4\", \"seed\": 3, \"all_fit\": true, \"S\": 26109, \"delta\": 23600, \"o\": 23642, \"w\": 3317, \"sigma\": 33859, \"Rc\": 117872172, \"div_S\": 0.0121, \"div_y\": 0.3751}\n{\"dist\": \"R4\", \"seed\": 4, \"all_fit\": true, \"S\": 21927, \"delta\": 18461, \"o\": 23956, \"w\": 3037, \"sigma\": 37151, \"Rc\": 89829381, \"div_S\": 0.0387, \"div_y\": 0.5782}\n{\"dist\": \"R4\", \"seed\": 5, \"all_fit\": true, \"S\": 14188, \"delta\": 25182, \"o\": 9655, \"w\": 3028, \"sigma\": 14100, \"Rc\": 107542217, \"div_S\": 0.0033, \"div_y\": 0.4149}\n{\"kernel\": \"random\", \"q_unscaled\": true, \"steps\": 1024, \"n\": 20, \"fit\": 20, \"max\": {\"S\": 150189, \"M\": 53294, \"delta\": 282387, \"delta_lo\": 262143, \"delta_hi\": 1, \"o\": 72479, \"w\": 4047, \"v\": 527711, \"k_hat\": 2988, \"q_hat\": 2911, \"xin\": 1478242, \"y\": 101401, \"cg\": 230259, \"sigma\": 73340, \"e1\": 109055, \"e2\": 96301, \"Rc\": 128207978, \"rr\": 43567, \"gap\": 43843, \"Vx\": 5378589839}, \"worst_div_S\": 0.03978339234812325, \"worst_div_y\": 1.1899773994043372, \"seconds\": 14.2}\n" + }, + "ranges_delta-kernel": { + "exit": 0, + "seconds": 14.606, + "stdout": "}\n{\"dist\": \"R1\", \"seed\": 5, \"all_fit\": true, \"S\": 5098, \"delta\": 13253, \"o\": 349, \"w\": 2233, \"sigma\": 788, \"Rc\": 1145324612, \"div_S\": 0.002, \"div_y\": 0.5294}\n{\"dist\": \"R2\", \"seed\": 1, \"all_fit\": true, \"S\": 11188, \"delta\": 25142, \"o\": 928, \"w\": 1571, \"sigma\": 3624, \"Rc\": 313787565, \"div_S\": 0.0081, \"div_y\": 0.1251}\n{\"dist\": \"R2\", \"seed\": 2, \"all_fit\": true, \"S\": 11758, \"delta\": 28507, \"o\": 825, \"w\": 1689, \"sigma\": 2790, \"Rc\": 255462738, \"div_S\": 0.0048, \"div_y\": 0.1207}\n{\"dist\": \"R2\", \"seed\": 3, \"all_fit\": true, \"S\": 12287, \"delta\": 25702, \"o\": 888, \"w\": 1589, \"sigma\": 3527, \"Rc\": 432197966, \"div_S\": 0.0074, \"div_y\": 0.1141}\n{\"dist\": \"R2\", \"seed\": 4, \"all_fit\": true, \"S\": 14726, \"delta\": 24582, \"o\": 1195, \"w\": 1434, \"sigma\": 4659, \"Rc\": 173534032, \"div_S\": 0.0168, \"div_y\": 0.1266}\n{\"dist\": \"R2\", \"seed\": 5, \"all_fit\": true, \"S\": 10419, \"delta\": 26959, \"o\": 746, \"w\": 1993, \"sigma\": 1925, \"Rc\": 648296950, \"div_S\": 0.002, \"div_y\": 0.2653}\n{\"dist\": \"R3\", \"seed\": 1, \"all_fit\": true, \"S\": 74704, \"delta\": 237244, \"o\": 4257, \"w\": 3007, \"sigma\": 6232, \"Rc\": 98876944, \"div_S\": 0.0116, \"div_y\": 0.1067}\n{\"dist\": \"R3\", \"seed\": 2, \"all_fit\": true, \"S\": 200463, \"delta\": 582625, \"o\": 8271, \"w\": 3928, \"sigma\": 8875, \"Rc\": 1347440720, \"div_S\": 0.026, \"div_y\": 0.5988}\n{\"dist\": \"R3\", \"seed\": 3, \"all_fit\": true, \"S\": 104250, \"delta\": 273909, \"o\": 7204, \"w\": 3602, \"sigma\": 8191, \"Rc\": 160185260, \"div_S\": 0.014, \"div_y\": 0.1117}\n{\"dist\": \"R3\", \"seed\": 4, \"all_fit\": true, \"S\": 77280, \"delta\": 187351, \"o\": 4159, \"w\": 2626, \"sigma\": 7425, \"Rc\": 100614167, \"div_S\": 0.0223, \"div_y\": 0.1019}\n{\"dist\": \"R3\", \"seed\": 5, \"all_fit\": true, \"S\": 100699, \"delta\": 253724, \"o\": 4045, \"w\": 3879, \"sigma\": 4536, \"Rc\": 512831915, \"div_S\": 0.0092, \"div_y\": 0.3549}\n{\"dist\": \"R4\", \"seed\": 1, \"all_fit\": true, \"S\": 15048, \"delta\": 23158, \"o\": 2407, \"w\": 1914, \"sigma\": 5415, \"Rc\": 313787565, \"div_S\": 0.0086, \"div_y\": 0.0935}\n{\"dist\": \"R4\", \"seed\": 2, \"all_fit\": true, \"S\": 13066, \"delta\": 19867, \"o\": 1544, \"w\": 1994, \"sigma\": 4195, \"Rc\": 255462738, \"div_S\": 0.0045, \"div_y\": 0.1556}\n{\"dist\": \"R4\", \"seed\": 3, \"all_fit\": true, \"S\": 17887, \"delta\": 21502, \"o\": 1987, \"w\": 2276, \"sigma\": 4684, \"Rc\": 432197966, \"div_S\": 0.0081, \"div_y\": 0.1039}\n{\"dist\": \"R4\", \"seed\": 4, \"all_fit\": true, \"S\": 15666, \"delta\": 20861, \"o\": 2171, \"w\": 1959, \"sigma\": 6339, \"Rc\": 139958201, \"div_S\": 0.0167, \"div_y\": 0.1008}\n{\"dist\": \"R4\", \"seed\": 5, \"all_fit\": true, \"S\": 11611, \"delta\": 27684, \"o\": 1208, \"w\": 2375, \"sigma\": 3031, \"Rc\": 487372175, \"div_S\": 0.0023, \"div_y\": 0.25}\n{\"kernel\": \"delta\", \"q_unscaled\": false, \"steps\": 1024, \"n\": 20, \"fit\": 20, \"max\": {\"S\": 200463, \"M\": 137315, \"delta\": 582625, \"delta_lo\": 262143, \"delta_hi\": 2, \"o\": 8271, \"w\": 3928, \"v\": 1022655, \"k_hat\": 2743, \"q_hat\": 260, \"xin\": 1022655, \"y\": 98012, \"cg\": 227005, \"sigma\": 8875, \"e1\": 13545, \"e2\": 16637, \"Rc\": 1347440720, \"rr\": 6759, \"gap\": 7166, \"Vx\": 78764591}, \"worst_div_S\": 0.02597577748053581, \"worst_div_y\": 0.5987994346756061, \"seconds\": 14.5}\n" + } +} From 88f8b95e91d8210e08cb2da183421929984a0393 Mon Sep 17 00:00:00 2001 From: wakesync <60821182+wakesync@users.noreply.github.com> Date: Thu, 5 Nov 2026 15:31:35 +0000 Subject: [PATCH 4/6] research: results.yaml entry for the Qwen M2 design, regenerate README block and CLAIMS.md (Part of #32) --- CLAIMS.md | 1 + README.md | 3 ++- research/results.yaml | 16 ++++++++++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/CLAIMS.md b/CLAIMS.md index 987e17c0..b585d108 100644 --- a/CLAIMS.md +++ b/CLAIMS.md @@ -29,6 +29,7 @@ tracked file, which both scripts resolve (they check that it exists, not what it | `steering-real-proofs` | Runtime steering under real cp_prove / cp_verify, small synthetic model (PR #50): exact match: 0 of 57 changed steered replies accepted (all refused by the prover), 13 of 13 unchanged accepted; shipped clipped-gap: 5 of 54 changed replies accepted `WITHIN LIMITS`, all at dose 0.05, passing on the theta mean bound (above-clip 0); Python score agreed with the real decision on 140 of 140 | measured, unreplicated (strict PASS, lenient half holds) | unreplicated | [experiments/20261104-36-steering-real-proofs/RESULTS.md](experiments/20261104-36-steering-real-proofs/RESULTS.md) | #50 | | `qwen-deltanet-m1` | Qwen Gated DeltaNet head at real shape, M1 (PR #47): FAIL (NO-GO for M2 as registered): 10 of 10 honest proofs accepted (prove median 51.4 s, verify 11.6 s, 50.4 MB) and 40 of 40 hostile proofs rejected, but under heavy-tailed values delta reaches 422,167 > 2^18 at F = 12 (0 of 5 seeds fit) | negative, unreplicated | unreplicated | [research/operators/qwen_deltanet/README.md](research/operators/qwen_deltanet/README.md), [experiments/20261104-11-qwen-deltanet-m1/RESULTS.md](experiments/20261104-11-qwen-deltanet-m1/RESULTS.md) | #47 | | `qwen-deltanet-m1b-lookup` | Qwen Gated DeltaNet head with the RNG/LKP range check, M1b (PR #63): PASS vs prereg (GO for M2 design): RNG/LKP composed with the wide product oracle in one group; 10 of 10 honest proofs (prove median 39.2 s, verify 7.1 s, 25.4 MB, table 2^21), 44 of 44 hostile rejected incl. limb-recombination and out-of-range state, and 5 of 5 R3 windows where M1 overflowed proved | measured, unreplicated | unreplicated | [experiments/20261105-32-qwen-deltanet-m1b-lookup/RESULTS.md](experiments/20261105-32-qwen-deltanet-m1b-lookup/RESULTS.md) | #63 | +| `qwen-deltanet-m2-design` | Qwen DeltaNet M2 design: verifier-side admission, lookup-exact remainders, whole-head ranges (PR #66): PASS vs prereg (GO for an M2 build, arm B): a verifier entry that admits its own integer public inputs accepted 10 of 10 honest M1b proofs (+0.18 s on a 7.6 s verify) and rejected 34 of 34 hostile encodings, including the #63 review's R9 that the caller path accepts; counted, not built: lookup-exact remainders cut product columns per state cell per token 17.4 to 6.1 and relations 3.7x, so the M2 head at T = 16 fits 2^21; whole-head ranges fit 20 of 20 | measured, unreplicated | unreplicated | [experiments/20261105-32-qwen-deltanet-m2-design/RESULTS.md](experiments/20261105-32-qwen-deltanet-m2-design/RESULTS.md) | #66 | | `swap-at-verifier` | #45 attack classes through the honest cp_prove + cp_verify, small synthetic model, one H100 (PR #51): honest-prover path: Python score matched all 256 decisions (89 cp_verify accepts, 167 honest-prover refusals, 0 cp_verify rejections); one swap then greedy continuation proved and accepted 15/16 (fp2nd, 95% CI 0.70-1.00) and 13/16 (rand, 0.54-0.96) under the shipped limits; 4 swaps and random replies 0/16; cp_verify's limit check was not exercised on a forged proof | measured, unreplicated | unreplicated | [experiments/20261104-38-swap-at-verifier/RESULTS.md](experiments/20261104-38-swap-at-verifier/RESULTS.md) | #51 | | `signed-manifest` | Signed model manifest checked next to cp_verify, stored small example (PR #59): 137 of 137 registered tampers refused, 10 of 10 untampered verifies accepted (cp_verify YES), verify about 1.41 s; 14 of 37 fields are the trusted signer's word: 11 always (10 as a one-field change, plus commitment.kind, which a matching seed file passes even with --public-weights-seed) and 3 (upstream, committed files, seed digest) unless the caller passes the rehash flag; small-order ed25519 keys and R refused; symlinks in rehashed dirs, deep nesting and non-true none refused | measured, unreplicated | unreplicated | [experiments/20261104-58-signed-manifest/RESULTS.md](experiments/20261104-58-signed-manifest/RESULTS.md) | #59 | | `open-studies` | Still open: integer-vs-BF16/FP8 fidelity (#8), KDA model-scale sweep (PR #42), cost teardown and larger-memory GPU test (PR #44): none yet | in progress | n/a | [docs/research-roadmap.md](docs/research-roadmap.md) | - | diff --git a/README.md b/README.md index e0680999..d9baf203 100644 --- a/README.md +++ b/README.md @@ -27,11 +27,12 @@ Every number below comes from a file on `main` (the one exception, a reviewer fi | Runtime steering under real cp_prove / cp_verify, small synthetic model (PR #50) | measured, unreplicated (strict PASS, lenient half holds) | exact match: 0 of 57 changed steered replies accepted (all refused by the prover), 13 of 13 unchanged accepted; shipped clipped-gap: 5 of 54 changed replies accepted `WITHIN LIMITS`, all at dose 0.05, passing on the theta mean bound (above-clip 0); Python score agreed with the real decision on 140 of 140 | one neuron, synthetic 8-layer model, 5 prompt seeds x 7 doses x 2 reply lengths x 2 policies, one H100 of an unverified host; honest prover only, no cheating prover tested; S and G replies are not paired (different scales) | [experiments/20261104-36-steering-real-proofs/RESULTS.md](experiments/20261104-36-steering-real-proofs/RESULTS.md) | | Qwen Gated DeltaNet head at real shape, M1 (PR #47) | negative, unreplicated | FAIL (NO-GO for M2 as registered): 10 of 10 honest proofs accepted (prove median 51.4 s, verify 11.6 s, 50.4 MB) and 40 of 40 hostile proofs rejected, but under heavy-tailed values delta reaches 422,167 > 2^18 at F = 12 (0 of 5 seeds fit) | one head, 128 x 128 state, T = 4, F = 12, synthetic inputs, research test security settings (target 10 bits), CPU, shared loaded VM; range study 1,024 steps x 4 distributions x 5 seeds, integer reference only | [research/operators/qwen_deltanet/](research/operators/qwen_deltanet/README.md) | | Qwen Gated DeltaNet head with the RNG/LKP range check, M1b (PR #63) | measured, unreplicated | PASS vs prereg (GO for M2 design): RNG/LKP composed with the wide product oracle in one group; 10 of 10 honest proofs (prove median 39.2 s, verify 7.1 s, 25.4 MB, table 2^21), 44 of 44 hostile rejected incl. limb-recombination and out-of-range state, and 5 of 5 R3 windows where M1 overflowed proved | one head, 128 x 128 state, T = 4, F = 12, two-limb delta, exact remainder bits, synthetic inputs, research security (target 10 bits, N = 10 combinations), CPU 6 cores, shared loaded VM | [experiments/20261105-32-qwen-deltanet-m1b-lookup/RESULTS.md](experiments/20261105-32-qwen-deltanet-m1b-lookup/RESULTS.md) | +| Qwen DeltaNet M2 design: verifier-side admission, lookup-exact remainders, whole-head ranges (PR #66) | measured, unreplicated | PASS vs prereg (GO for an M2 build, arm B): a verifier entry that admits its own integer public inputs accepted 10 of 10 honest M1b proofs (+0.18 s on a 7.6 s verify) and rejected 34 of 34 hostile encodings, including the #63 review's R9 that the caller path accepts; counted, not built: lookup-exact remainders cut product columns per state cell per token 17.4 to 6.1 and relations 3.7x, so the M2 head at T = 16 fits 2^21; whole-head ranges fit 20 of 20 | design lane, no M2 proof; admission prototype on the M1b statement (one head, T = 4); counts from a model that reproduces M1/M1b exactly; ranges from an integer reference with synthetic init; research security (target 10 bits), CPU 6 cores, shared loaded VM | [experiments/20261105-32-qwen-deltanet-m2-design/RESULTS.md](experiments/20261105-32-qwen-deltanet-m2-design/RESULTS.md) | | #45 attack classes through the honest cp_prove + cp_verify, small synthetic model, one H100 (PR #51) | measured, unreplicated | honest-prover path: Python score matched all 256 decisions (89 cp_verify accepts, 167 honest-prover refusals, 0 cp_verify rejections); one swap then greedy continuation proved and accepted 15/16 (fp2nd, 95% CI 0.70-1.00) and 13/16 (rand, 0.54-0.96) under the shipped limits; 4 swaps and random replies 0/16; cp_verify's limit check was not exercised on a forged proof | target_small_f2_gap, shipped limits and #45's fit point (both cap 1), one fresh commitment each; #45's seed 2026 held-out replies, 16 per class, one draw; synthetic weights; soundness flag vacuous for a cheating prover | [experiments/20261104-38-swap-at-verifier/RESULTS.md](experiments/20261104-38-swap-at-verifier/RESULTS.md) | | Signed model manifest checked next to cp_verify, stored small example (PR #59) | measured, unreplicated | 137 of 137 registered tampers refused, 10 of 10 untampered verifies accepted (cp_verify YES), verify about 1.41 s; 14 of 37 fields are the trusted signer's word: 11 always (10 as a one-field change, plus commitment.kind, which a matching seed file passes even with --public-weights-seed) and 3 (upstream, committed files, seed digest) unless the caller passes the rehash flag; small-order ed25519 keys and R refused; symlinks in rehashed dirs, deep nesting and non-true none refused | stored synthetic example, fixture tokenizer/template/system prompt, no real model; client-side detached manifest, pure-Python ed25519 (RFC 8032 3 of 3, 64 of 64 vs cryptography); CPU only, re-measured after PREREG amendment 3 | [experiments/20261104-58-signed-manifest/RESULTS.md](experiments/20261104-58-signed-manifest/RESULTS.md) | | Still open: integer-vs-BF16/FP8 fidelity (#8), KDA model-scale sweep (PR #42), cost teardown and larger-memory GPU test (PR #44) | in progress | none yet | open PRs, issues and lanes, not merged | [docs/research-roadmap.md](docs/research-roadmap.md) | -_Last regenerated from `research/results.yaml` on 2026-11-05 (UTC), against main `43bfa9fc`._ +_Last regenerated from `research/results.yaml` on 2026-11-05 (UTC), against main `4f7a8761`._ What this table does not show: no full-size (Maverick) run, no real model weights on GPU, no serving, no TEE, no production use, and no comparison with other verifiable-inference systems. The GPU rows are upstream's small example on made-up weights. diff --git a/research/results.yaml b/research/results.yaml index ec2805ae..620de4f9 100644 --- a/research/results.yaml +++ b/research/results.yaml @@ -355,6 +355,22 @@ results: - {name: hostile_rejected, stat: count, value: 44, total: 44, unit: count, n: 1, spread: null, hardware: cpu-epyc-rome-kvm-16, date: 2026-11-05, commit: b6186e6ccb687348e3be66fec51bb3756879b8dc, source: experiments/20261105-32-qwen-deltanet-m1b-lookup/runs/hostile-seed1.jsonl, note: "seed 1, 10 sampled classes; not exhaustive"} - {name: r3_windows_proved, stat: count, value: 5, total: 5, unit: count, n: 5, spread: null, hardware: cpu-epyc-rome-kvm-16, date: 2026-11-05, commit: b6186e6ccb687348e3be66fec51bb3756879b8dc, source: experiments/20261105-32-qwen-deltanet-m1b-lookup/runs/h3b-r3.jsonl, note: "T = 4 window ending at the first |delta| >= 2^18 step"} + - id: qwen-deltanet-m2-design + title: "Qwen DeltaNet M2 design: verifier-side admission, lookup-exact remainders, whole-head ranges (PR #66)" + status: measured + replication: unreplicated + pr: 66 + summary: "PASS vs prereg (GO for an M2 build, arm B): a verifier entry that admits its own integer public inputs accepted 10 of 10 honest M1b proofs (+0.18 s on a 7.6 s verify) and rejected 34 of 34 hostile encodings, including the #63 review's R9 that the caller path accepts; counted, not built: lookup-exact remainders cut product columns per state cell per token 17.4 to 6.1 and relations 3.7x, so the M2 head at T = 16 fits 2^21; whole-head ranges fit 20 of 20" + conditions: "design lane, no M2 proof; admission prototype on the M1b statement (one head, T = 4); counts from a model that reproduces M1/M1b exactly; ranges from an integer reference with synthetic init; research security (target 10 bits), CPU 6 cores, shared loaded VM" + evidence: [experiments/20261105-32-qwen-deltanet-m2-design/RESULTS.md] + metrics: + - {name: admission_honest_accepted, stat: count, value: 10, total: 10, unit: count, n: 10, spread: null, hardware: cpu-epyc-rome-kvm-16, date: 2026-11-05, commit: e08e5ce2603bc442361822c55880ea870b4a9292, source: experiments/20261105-32-qwen-deltanet-m2-design/runs/h5-honest.jsonl} + - {name: admission_derive_wall, stat: median, value: 0.182, unit: s, n: 10, spread: {sd: 0.007, min: 0.170, max: 0.196}, hardware: cpu-epyc-rome-kvm-16, date: 2026-11-05, commit: e08e5ce2603bc442361822c55880ea870b4a9292, source: experiments/20261105-32-qwen-deltanet-m2-design/runs/h5-honest.jsonl, note: "decode + relation derivation + sizing, before the proof verifier"} + - {name: verify_wall, stat: median, value: 7.613, unit: s, n: 10, spread: {sd: 0.114, min: 7.527, max: 7.861}, hardware: cpu-epyc-rome-kvm-16, date: 2026-11-05, commit: e08e5ce2603bc442361822c55880ea870b4a9292, source: experiments/20261105-32-qwen-deltanet-m2-design/runs/h5-honest.jsonl, note: "indicative only, shared loaded VM"} + - {name: admission_hostile_as_expected, stat: count, value: 34, total: 34, unit: count, n: 1, spread: null, hardware: cpu-epyc-rome-kvm-16, date: 2026-11-05, commit: e08e5ce2603bc442361822c55880ea870b4a9292, source: experiments/20261105-32-qwen-deltanet-m2-design/runs/h5-hostile-seed1.jsonl, note: "seed 1; includes R9 control accepted by the caller path and R9 rejected at decode"} + - {name: columns_per_state_cell_armB_over_armA, stat: single, value: 0.349, unit: ratio, n: 1, spread: null, hardware: cpu-epyc-rome-kvm-16, date: 2026-11-05, commit: e08e5ce2603bc442361822c55880ea870b4a9292, source: experiments/20261105-32-qwen-deltanet-m2-design/runs/model.json, note: "counted by a model, not built"} + - {name: head_range_runs_fit, stat: count, value: 20, total: 20, unit: count, n: 20, spread: null, hardware: cpu-epyc-rome-kvm-16, date: 2026-11-05, commit: e08e5ce2603bc442361822c55880ea870b4a9292, source: experiments/20261105-32-qwen-deltanet-m2-design/runs/ranges-head.json} + - id: swap-at-verifier title: "#45 attack classes through the honest cp_prove + cp_verify, small synthetic model, one H100 (PR #51)" status: measured From 2512c97c794a080854bc61ad0fb9db8d15d550a3 Mon Sep 17 00:00:00 2001 From: wakesync <60821182+wakesync@users.noreply.github.com> Date: Thu, 5 Nov 2026 17:00:25 +0000 Subject: [PATCH 5/6] experiments: Qwen M2 design, review fixes: witness not private, conv history public, second-lookup multiplicities, witness-free derive (Part of #32) - DESIGN.md: section 0, M2 is a rehearsal for M3; the M2 statement is verifier-computable; what M3 must add - conv history H (3 x 384) public and class-bounded; admit_proto decodes it, 4 hostile cases - count_model: multiplicity row of both lookups sized; H2/H3 unchanged, arm B T = 16 still 2^21 - transcript separation of the two LKP instances as a build requirement, challenge-replay case planned - verifier derivation witness-free, verdicts instead of exit(1); derive-overflow and derive-random cases, suite clean under UBSan - H5 rerun: honest 10/10, hostile 41/41; relations <= 2^23 loop condition listed as a deviation --- .../DESIGN.md | 69 ++++++-- .../ENV.json | 4 +- .../RESULTS.md | 65 +++++++- .../runs/h5-honest.jsonl | 20 +-- .../runs/h5-hostile-seed1-ubsan.jsonl | 46 ++++++ .../runs/h5-hostile-seed1.jsonl | 13 +- .../runs/model.json | 36 ++++- research/operators/qwen_deltanet_m2/README.md | 3 + .../qwen_deltanet_m2/admit_proto.cpp | 147 ++++++++++++++++-- .../operators/qwen_deltanet_m2/count_model.py | 59 +++++-- .../operators/qwen_deltanet_m2/evidence.json | 92 ++++++----- .../qwen_deltanet_m2/head_reference.py | 2 +- .../operators/qwen_deltanet_m2/m1b_builder.h | 27 ++-- research/operators/qwen_deltanet_m2/record.py | 16 +- 14 files changed, 488 insertions(+), 111 deletions(-) create mode 100644 experiments/20261105-32-qwen-deltanet-m2-design/runs/h5-hostile-seed1-ubsan.jsonl diff --git a/experiments/20261105-32-qwen-deltanet-m2-design/DESIGN.md b/experiments/20261105-32-qwen-deltanet-m2-design/DESIGN.md index cadbf9cf..734a87a9 100644 --- a/experiments/20261105-32-qwen-deltanet-m2-design/DESIGN.md +++ b/experiments/20261105-32-qwen-deltanet-m2-design/DESIGN.md @@ -9,9 +9,29 @@ The numbers are counts from `research/operators/qwen_deltanet_m2/count_model.py` Timings marked "estimate" are linear interpolations, not measurements. +## 0 What M2 is for (post-review, #66 must-fix 1) + +**M2 is a rehearsal of the proving machinery for M3. As a standalone statement it is redundant.** At the M2 boundary every input is public (section 2) and every step is a deterministic integer function of those inputs. So a verifier can compute every state S_t, every M and delta, the head output o and the output y itself, in about O(T D^2) integer operations: milliseconds, against a verify of seconds. The argument that moves conv, SiLU, the q/k norm and the gates to verifier recompute (section 3: "proving them would add cost and no soundness") applies to the whole M2 statement. A verifier that only wants to know y for these public inputs should recompute it, not check a proof. + +In this document, S_t, M, delta, o and the norm intermediates are **witness values: not in the statement, and computable from public inputs at the M2 boundary**. Nothing in M2 hides them, and M2 makes no hiding or zero-knowledge claim about them. Earlier drafts called them "private". That word was wrong and is gone. + +What M2 is worth, as a rehearsal: +- the measured cost of the recurrence and the gated-norm lowering at the real head shape (columns, relations, prove and verify time), which M3 inherits unchanged; +- the arm-B composition (a second LKP instance for remainders, keyed on product-oracle cells, next to RNG's), which has not been built in this repo; +- the verifier entry that admits its own integer inputs (section 4.5), which M3 needs in any case. + +What M2 does **not** de-risk: none of the operator families that are new in FEASIBILITY.md. Causal conv1d, the decay and beta tables and the per-head q/k L2 norm all moved to verifier recompute (section 3), so M2 builds no gadget for them. Their in-relation form is first built at M3 (or in the optional rehearsal arm M2-R, section 3). + +**What M3 must add so that proving is not redundant.** The proof has a point only when the verifier cannot recompute the result from what it holds. At least one of these must hold for each proved step: +1. **Committed inputs.** The in_proj outputs (xq, xk, xv, ar, br, z) are not public words but committed cells, outputs of a matmul sub-proof on the layer input and the committed weights (docs/model.md), tied to the M2 relation by shared wires or the same oracle (research/operators/shared_wires). Then conv, SiLU, the q/k norm and the gates must be in-relation gadgets (section 3, "What changes at M3"), and the conv history (section 2) is a committed value carried from the previous chunk. +2. **Committed state.** S0 and S_T are commitments carried between chunks (issue #34), not 2 x 16,384 public words. Then the recurrence proves a transition between commitments, which no verifier can recompute without the state. +3. **A statement the verifier does not hold in full.** The verifier receives a commitment to the model and the final output, not every layer's in_proj outputs. + +M2 has none of these by design. Every M2 number is reported as rehearsal cost, not as the cost of a useful proof. + ## 1 What M2 proves -**One V head of one Qwen3.8 Gated DeltaNet layer, end to end, T = 16, F = 12**, on synthetic inputs of the published shapes. The statement runs from that head's in_proj outputs to its gated-norm output. All 48 V heads of a layer have the same statement with different public inputs, so the layer cost is 48 times the head cost (section 6, an extrapolation). +**One V head of one Qwen3.8 Gated DeltaNet layer, T = 16, F = 12**, on synthetic inputs of the published shapes. The statement runs from that head's in_proj outputs to its gated-norm output. "End to end" means end to end within the head, with all inputs public: by section 0 the whole statement is verifier-computable, and M2 is a rehearsal for M3. All 48 V heads of a layer have the same statement with different public inputs, so the layer cost is 48 times the head cost (section 6, an extrapolation). The old M2 line in FEASIBILITY.md ("one full layer at exact shape, d 5,120, SwiGLU 17,408") is out of reach of the research harness. That layer is about 3.8e8 multiply-adds per token in its matrices: - in_proj_qkv 5,120 x 10,240 @@ -38,31 +58,39 @@ Per head, public and fixed by the shape, not per token: - A_log and dt_bias (folded into the decay table, section 3) - the gated-norm gain (128) - S0 (128 x 128, \|S0\| < 2^18) +- **H, the conv history (3 x 384): the last 3 pre-conv inputs of each of the head's 384 conv channels (128 q, 128 k, 128 v) before token 0, \|H\| < 2^31, the class of xq, xk, xv** - S_T (128 x 128, \|S_T\| < 2^18) -Private, and committed in the product oracle: +Witness, laid in the product oracle (not in the statement; computable from the public inputs at this boundary, section 0; no hiding claim): - every state S_t - M, delta (two limbs) - the head output o - the norm's scalars and w -In M1/M1b, o was public. In M2 it is private, because the result is now y. +In M1/M1b, o was a statement value. In M2 it is a witness value, because the statement's result is now y. That is a change of statement boundary, not a privacy property. + +**Conv history (post-review, #66 must-fix 2).** The causal conv at token t reads the inputs at t, t - 1, t - 2 and t - 3. At t = 0 the model pads with zeros only when token 0 starts the sequence. The statement allows any S0, and a window with S0 != 0 is a window that does not start the sequence, so its conv needs the 3 inputs before it. Two options were on the table: (a) make H a public, class-bounded input, or (b) restrict the statement to S0 = 0 at sequence start. **M2 takes (a).** Reasons: +- It is cheap. H is 1,152 words against the statement's 16,384 for S0 and 16,384 for S_T, plus 16 x 642 per-token words (xq, xk, xv, ar, br, z, y). Decoding it is a range check per word. The verifier's conv recompute reads it in place of the zero padding, so the conv rule is one rule for every window, with no special first window. +- (b) would make M2 a statement about sequence starts only, and M3's chunking (state committed between chunks, #34) needs exactly the non-zero case. Under (a), M3 changes H from public words to a committed value carried from the previous chunk, like S0. Under (b), M3 would have to add the history then. +- S0 = 0 with H = 0 is the sequence-start case of (a), so (a) loses nothing. + +The admission prototype now has H in its wire format and decodes it with its class bound: a huge element, the first value outside the class on both sides and a non-canonical word are rejected at decode (RESULTS.md, post-review rerun). The M1b relations the prototype proves do not read H (M1b has no conv), so a changed but in-class H is accepted there. That is recorded as a control, not a defence. In M2 the verifier's conv recompute reads H, so a changed H changes k, q and v and must fail at the proof verifier. That case is in the build's H4 suite (section 7). ## 3 Gate, conv and norm: which are proved | step | in M2 | why | |---|---|---| -| causal conv1d, kernel 4, zero padding (on xq, xk, xv) | **verifier recomputes** | a function of public integers only | +| causal conv1d, kernel 4, on xq, xk, xv, with the public history H in place of the padding before token 0 (H = 0 at sequence start) | **verifier recomputes** | a function of public integers only | | SiLU on the conv outputs | **verifier recomputes** | a function of public integers only | | q/k L2 norm, q scale 1/sqrt(128) | **verifier recomputes** | a function of public integers only | | decay a = exp(-A softplus(ar + dt_bias)), beta = sigmoid(br) | **verifier recomputes** | a function of public integers only | -| recurrence (M, delta, S', o) | **proved** | private state | -| gated RMSNorm of o, times SiLU(z) and gain | **proved** (norm), **verifier recomputes** cg = round(2^F gain SiLU(z) sqrt(128)) | the norm reads private o; cg is public-only | +| recurrence (M, delta, S', o) | **proved** (rehearsal, section 0) | witness values; at this boundary also verifier-computable | +| gated RMSNorm of o, times SiLU(z) and gain | **proved** (norm, rehearsal), **verifier recomputes** cg = round(2^F gain SiLU(z) sqrt(128)) | the norm reads the witness o; cg is public-only | | in_proj, out_proj, pre-norm RMSNorm, residual, MLP | **not proved in M2** | dense family, proved elsewhere; inputs to M2 are public | -**Why recomputation is enough at this boundary.** At this statement boundary every input to conv, SiLU, L2 norm and the gates is public. A relation over them would prove that the verifier's own computation is right, and the verifier can just do that computation. The cost is O(T x 128 x 4) integer operations, about 0.2 ms at T = 16. The results enter the proof only as relation coefficients and targets (k, q, a, beta), as public values did in M1b. +**Why recomputation is enough at this boundary.** At this statement boundary every input to conv, SiLU, L2 norm and the gates is public. A relation over them would prove that the verifier's own computation is right, and the verifier can just do that computation. The same holds for the recurrence and the norm (section 0); M2 proves those anyway, as the rehearsal for M3, and does not prove conv, SiLU, L2 norm or the gates. The cost is O(T x 128 x 4) integer operations, about 0.2 ms at T = 16. The results enter the proof only as relation coefficients and targets (k, q, a, beta), as public values did in M1b. -**What changes at M3.** When the in_proj outputs become private (committed by the matmul sub-proof), these steps must become in-relation gadgets: +**What changes at M3.** When the in_proj outputs become committed values (outputs of the matmul sub-proof, not public words), these steps must become in-relation gadgets: - conv: 4 product gates per channel per token - SiLU: a Ts-style table lookup, as docs/model.md section 7 - L2 norm: isqrt and reciprocal, as in section 4.4 below @@ -71,7 +99,7 @@ In M1/M1b, o was public. In M2 it is private, because the result is now y. By the same counting rules, that is under 10 columns per channel per token, about 4,000 per head per token. That is under 1% of the head's state work (sections 5 and 6). An optional build arm M2-R may prove them in-relation as a rehearsal. It is not needed for the M2 claim. **Determinism of recomputation.** The prover and verifier must derive identical integers. The design pins: -- **conv**: integer taps at 2^12; acc = sum of w_tau x_(t - tau); c = floor(acc / 2^12) +- **conv**: integer taps at 2^12; acc = sum of w_tau x_(t - tau); c = floor(acc / 2^12); x_(-1 - tau) = H[tau] for tau = 0, 1, 2 - **SiLU**: a public table on clamped keys (docs/model.md Ts form), not libm at verify time - **L2 norm**: integer isqrt with round half up, eps = 17 at 2^2F. For q the divisor is multiplied by round(sqrt(128) x 2^12) and the numerator by 2^12. - **a and beta**: two public tables keyed by clamp(ar) and clamp(br), built once with correctly rounded exp, as the docs/model.md tables are @@ -97,7 +125,7 @@ Every RNG cell class has an honest contract |x| < 2^18. Each class sits in a 16- | S' | \|S'\| < 2^18 | builder rejects otherwise (statement refused, never saturated) | 150,189 | | M | \|M\| < 2^18 | as M1b | 53,294 | | delta_lo, delta_hi | delta = 2^18 hi + lo, lo in [0, 2^18), \|hi\| < 2^18 | split (registered, D4) | lo 262,143 (its bound), hi 1 | -| o (new, private) | \|o\| < 2^18 | builder rejects otherwise. Nearly provable: \|o\| <= \|S'\|max x \|\|q\|\|_1 / 2^F, and \|\|q\|\|_1 <= sqrt(128) \|\|q\|\|_2 <= 2^F + 64 after rounding, so \|o\| < 2^18 (1 + 2^-6). In practice it is 40x below. | 6,379 | +| o (new, witness) | \|o\| < 2^18 | builder rejects otherwise. Nearly provable: \|o\| <= \|S'\|max x \|\|q\|\|_1 / 2^F, and \|\|q\|\|_1 <= sqrt(128) \|\|q\|\|_2 <= 2^F + 64 after rounding, so \|o\| < 2^18 (1 + 2^-6). In practice it is 40x below. | 6,379 | | w (new) | \|w\| <= 2^12 | provable: \|w\| <= \|o\| Rc / 2^24 <= \|o\| 2^12 / sigma <= 2^12, since \|o\| <= sigma | 4,048 | No other class needs a split. The registered split rule (H4) did not have to fire. The M2 head has 272,384 RNG cells at T = 16: 17,024 cell rows, laid as two matrices of 16,384 and 1,024 rows. That gives 348,160 digit entries at N = 10. @@ -116,7 +144,16 @@ The M1b result showed remainder bits are 12 of the 17.4 product columns per stat - A 24-bit remainder (the M floor, 2^2F) is two such cells, r = lo + 2^12 hi. Both terms go directly into the floor relation, so no extra relation is needed. - An exact interval [0, 2^12) on each limb gives r in [0, 2^24) exactly, so floor uniqueness is unchanged from M1b. -This is an exact range proof, not RNG's approximate one, so the floor-uniqueness argument of M1b goes through as is. The keys come in parts (LkpPublic::parts, allowed for one unshifted function). The key region is laid like RNG's cells, and LKP's key claim becomes a laid_form on message 0, as `rng_key_row` does for the digits. The multiplicities are one extra message with about 20 planes x 2^12 entries. +This is an exact range proof, not RNG's approximate one, so the floor-uniqueness argument of M1b goes through as is. The keys come in parts (LkpPublic::parts, allowed for one unshifted function). The key region is laid like RNG's cells, and LKP's key claim becomes a laid_form on message 0, as `rng_key_row` does for the digits. + +**Multiplicities (post-review, #66 should-fix 1).** The remainder lookup sends its multiplicities as planes x 2^12 places, like RNG's (`rng_planes_places`). At T = 16 it has 278,528 keys (parts 2^18 + 2^14), so 19 planes, 77,824 places. RNG's digit lookup needs 21 planes x 2^13 = 172,032 places. The design puts both in RNG's multiplicity row (message 3), which then holds 249,856 places, well under 2^21. The count model now sizes this row and lets it bound the table length. Neither H2 nor H3 moves (RESULTS.md, post-review correction). + +**Transcript separation between the two lookup instances (build requirement).** The arm-B run has two LKP instances in one chain: RNG's digit lookup (shifted, g = 1, d = 13) and the remainder lookup (unshifted, one function, parts, d = 12). The build must give them distinct challenges that are bound to distinct labels: +- each instance's public parameters (LkpPublic: parts, d, mu, live, tables, shifted, read_from) are bound into the transcript under its own Binder label ("range check: digit lookup", "remainder lookup") before any of its challenges are drawn; +- each instance draws its own alpha (and RNG its own theta) after both instances' key and multiplicity messages are fixed, so no challenge of one instance is available while the other's keys can still change; +- the remainder lookup's multiplicities are in a fixed, public place range of message 3, disjoint from RNG's, and each instance's `read_from` names only its own messages. + +The build's hostile suite must include a **challenge-replay case**: a prover that reuses the digit lookup's alpha (or its sumcheck challenges) as the remainder lookup's, or swaps the two instances' order, must be rejected by the verifier, and the case must show that an honest proof whose two instances are relabelled does not verify (section 7). **Savings (count model, H2 and H3):** @@ -126,7 +163,7 @@ This is an exact range proof, not RNG's approximate one, so the floor-uniqueness | same: relations | 2,231,808 | 609,792 | **0.273** | | same: terms | 5,174,272 | 1,186,816 | 0.229 | | same: table log-length | 21 | 19 | | -| M2 head (T = 16, private o, norm): gate columns + range cells + keys | 4,649,440 | 1,605,872 | 0.345 | +| M2 head (T = 16, witness o, norm): gate columns + range cells + keys | 4,649,440 | 1,605,872 | 0.345 | | same: relations | 9,042,944 | 2,402,432 | 0.266 | | same: table log-length | **23 (does not fit 2^22)** | **21** | | | largest T at log-length <= 22 | 14 | 41 | | @@ -169,14 +206,17 @@ The H4 range suite found no exact limb outside its interval and no negative exac 1. Fix the shape from the verifier's own constants. The header words must equal them, and the word count must be exact. 2. Decode every public word as a canonical Goldilocks element (< p), read it as a signed integer (a word above p/2 is negative), and check it against its class bound (section 2). A non-canonical word, a huge element, or an integer outside its class is rejected **here**. 3. Recompute the public-only stage (section 3) from the decoded integers. -4. Derive every relation target and coefficient from those integers with the relation-only builder. +4. Derive every relation target and coefficient from those integers with the relation-only builder. The relation-only builder computes **no witness**: it reads only the decoded public integers, so no in-class statement can make it overflow, and it never ends the process. 5. Size the run (a pure function of the shape) and run the unchanged proof verifier. +Every failure is a returned verdict (decode, derive or proof). Nothing on the verifier path calls exit() or a BLK_REQUIRE that a statement can reach: the entry checks lookup_size()'s guards on the derived statement first and returns "derive" if one would fire. (Post-review, #66 should-fix 3: the first prototype ran the full witness path with its checks off, so in-class but inconsistent inputs could reach i64 overflow, and a failed sizing called exit(1). The prototype now uses a witness-free derivation and checks that it gives the prover's relation list exactly.) + There is no `admissible()` that a caller runs separately, and no entry point that accepts caller-built relations or a caller-built `LookupRun`. The low-level `lookup_verify(run, ...)` stays a library internal. **The M2 build must not export it as a verifier.** The prototype measured this on the M1b statement (RESULTS.md H5): - 10/10 honest seeds accepted. - 34/34 hostile encodings rejected at the expected stage, including a huge field element in each of the 8 public classes and the #63 review's R9. +- Post-review rerun with the conv history and the witness-free derivation: see RESULTS.md, "Post-review rerun". - For R9, an honest proof of the statement with o' = o - 2^-F mod p is **accepted** by the current path, `lookup_verify` with caller-built relations and `admissible()` skipped. The new entry **rejects** it at decode. ## 5 What stays unproved in M2 @@ -215,5 +255,6 @@ The build lane must keep these, and register numbers in its own PREREG before co - one remainder key off-table (arm B: key 2^12, which only a hostile LKP prover can send) - the limb re-split - **H3 range:** H4 of this lane re-run on the build's reference, 20/20. -- **H4 admission:** this lane's H5 suite on the M2 statement words, plus a huge element in xq, ar and z, and the R9 analogue on y. All rejected by `verify_m2` alone. +- **H4 admission:** this lane's H5 suite on the M2 statement words, plus a huge element in xq, ar, z and the conv history H, and the R9 analogue on y. All rejected by `verify_m2` alone. Also: H changed in class (must fail at the proof verifier, since the conv recompute reads it), and the derive-overflow cases (every class at its extreme; every word random in class), which must return a verdict and never exit or overflow (run under UBSan). +- **H2 hostile, two lookups:** the challenge-replay case of section 4.3 (the digit lookup's alpha and sumcheck challenges reused in the remainder lookup; the two instances swapped), rejected. - **Kill:** no honest T = 16 arm B proof in 6 h, then fall back to arm A at T = 8 and report. diff --git a/experiments/20261105-32-qwen-deltanet-m2-design/ENV.json b/experiments/20261105-32-qwen-deltanet-m2-design/ENV.json index d5e9d50b..013070a2 100644 --- a/experiments/20261105-32-qwen-deltanet-m2-design/ENV.json +++ b/experiments/20261105-32-qwen-deltanet-m2-design/ENV.json @@ -20,6 +20,6 @@ }, "proof_parameters": "as #63: Goldilocks, target 10 bits, 6 work bits on QUERY, ToyCoins(100); log-length 21, stages {1,1,21,21},{0,0,21,21}; RNG digits 12+11 bits (2D = 2^23), 16 columns, N = 10", "seeds": {"h5_honest": [1,2,3,4,5,6,7,8,9,10], "h5_hostile": [1], "h4_ranges": {"dists": ["R1","R2","R3","R4"], "seeds": [1,2,3,4,5], "steps": 1024, "generator_seed": "1000 + seed"}}, - "runs": {"h5_honest": 10, "h5_hostile": 34, "h4_range_runs": 20, "h4_secondary_runs": 40, "model": 1}, - "raw": ["runs/h5-honest.jsonl", "runs/h5-hostile-seed1.jsonl", "runs/model.json", "runs/ranges-head.json", "runs/ranges-head-q-unscaled.json", "runs/ranges-head-delta-kernel.json", "../../research/operators/qwen_deltanet_m2/evidence.json"] + "runs": {"h5_honest": 10, "h5_hostile": 41, "h5_hostile_registered_run": 34, "h5_hostile_ubsan": 41, "h4_range_runs": 20, "h4_secondary_runs": 40, "model": 1}, + "raw": ["runs/h5-honest.jsonl", "runs/h5-hostile-seed1.jsonl", "runs/h5-hostile-seed1-ubsan.jsonl", "runs/model.json", "runs/ranges-head.json", "runs/ranges-head-q-unscaled.json", "runs/ranges-head-delta-kernel.json", "../../research/operators/qwen_deltanet_m2/evidence.json"] } diff --git a/experiments/20261105-32-qwen-deltanet-m2-design/RESULTS.md b/experiments/20261105-32-qwen-deltanet-m2-design/RESULTS.md index 797878e7..dcbb628f 100644 --- a/experiments/20261105-32-qwen-deltanet-m2-design/RESULTS.md +++ b/experiments/20261105-32-qwen-deltanet-m2-design/RESULTS.md @@ -5,6 +5,8 @@ All numbers are **measured or counted on one host, unreplicated**. No M2 proof w **Verdict against the registered threshold: PASS on H1 to H5, so GO for an M2 build lane**, with arm B (lookup-exact remainders) as the primary arm. One registered item reads as a design constraint: arm A cannot hold the M2 head at T = 16 in a 2^22 table. +**Scope of that GO (post-review, #66).** At the M2 boundary every input is public, so the whole M2 statement, y included, is verifier-computable. M2 is a rehearsal of the proving machinery for M3, not a useful standalone proof, and it de-risks none of the new operator families (conv1d, the gate tables, the q/k norm), since those moved to verifier recompute. DESIGN.md section 0 says what M3 must add. The post-review corrections and the rerun are in "Post-review corrections" at the end. None of them moves a registered verdict. + Code: `research/operators/qwen_deltanet_m2/` - `admit_proto.cpp` + `m1b_builder.h` (H5) - `count_model.py` (H1 to H3) @@ -82,7 +84,7 @@ Setup: integer reference of the whole head, R1..R4 x seeds 1..5 x 1,024 steps, c | S' | < 2^18 (262,144) | 150,189 | | M | < 2^18 | 53,294 | | delta | split at 2^18 | 282,387 (split needed in 1/20 runs; hi <= 1) | -| o (private, new) | < 2^18 | 6,379 | +| o (witness, new) | < 2^18 | 6,379 | | w (norm output, new) | <= 2^12 | 4,048 | | sigma | < 2^24 (2 limbs) | 6,454 | | e1, e2 | [0, 2^24) | 12,148 / 7,253, min 0 | @@ -93,7 +95,7 @@ Setup: integer reference of the whole head, R1..R4 x seeds 1..5 x 1,024 steps, c | public y | < 2^31 | 101,976 | | public cg | < 2^24 | 230,259 | -**H4: PASS.** No new limb split was needed. +**H4: PASS.** No new limb split was needed. (The remainder classes are checked only for >= 0. Their upper bounds hold by the construction of floor: a remainder of floor(x / 2^w) is below 2^w.) Divergence from float64: - **State: 0.040 worst.** M1b's was 0.032. @@ -138,6 +140,57 @@ Hostile, seed 1: **34/34 at the expected stage.** **H5: PASS.** This answers hard rule (a): the entry point range-checks its own integer inputs, derives targets from them, and rejects a huge public field element on its own. +## Post-review corrections and rerun (2026-11-05, review of #66) + +These are corrections after the review, not re-scored hypotheses. The registered verdicts above stand on the original runs (commit e08e5ce2). The prototype changed, so H5 was rerun on the new code. Its numbers are below and are the current receipts in `runs/`. + +**Wording (must-fix 1).** S_t, M, delta, o and the norm intermediates are witness values. They are not in the statement and are computable from public inputs at the M2 boundary. There is no hiding claim. "Private" is gone from DESIGN.md, this file and the prototypes (`o_private` in count_model.py is now `o_witness`; the counts are unchanged). DESIGN.md section 0 states that M2 is a rehearsal for M3, and what M3 must add. + +**Conv history (must-fix 2): option (a), public history.** The statement gains H: the last 3 pre-conv inputs of each of the 384 conv channels, 1,152 words, with |H| < 2^31. DESIGN.md section 2 gives the reasons. The admission prototype now carries H in its wire format (34,827 to 35,979 words, still the M1b statement plus H; the M2 statement is about 2 x 16,384 + 1,152 + 16 x 642 words) and decodes it with its class bound. + +**Count model, second lookup's multiplicities (should-fix 1).** `layout()` now sizes the multiplicity row of both lookups: +- RNG's digit lookup: 21 planes x 2^13 = 172,032 places at T = 16 (155,648 at T = 4) +- the remainder lookup: 19 planes x 2^12 = 77,824 places (69,632 at T = 4) +- both in one row: 249,856 places at T = 16, below 2^18 + +The row bounds the table length but does not bind at any shape here. Counts after the correction (`runs/model.json`): + +| | before | after | +|---|---|---| +| H2 columns per state cell, B / A | 0.349 | 0.349 | +| H2 relations, B / A | 0.273 | 0.273 | +| H3 arm B, T = 16: laid columns, log-length | 1,613,856, 21 | 1,613,856, 21 | +| H3 arm B, T = 16: fits 2^21 | yes | **yes** | +| H3 arm A, T = 16: log-length | 23 | 23 | +| largest T at log-length <= 22, A / B | 14 / 41 | 14 / 41 | +| cross-check: arm B with the keys as ONE 2^19 part, no parts | | 1,859,616 laid columns, log 21 (the review estimated about 1.86M) | + +**Two lookup instances (should-fix 2).** Transcript separation is now a stated build requirement (DESIGN.md section 4.3), with a planned hostile challenge-replay case in the build's suite (DESIGN.md section 7). Nothing is built for it here: the prototype has one lookup. + +**Derive step (should-fix 3).** The verifier's derivation is now witness-free (`build(z, true)` in m1b_builder.h). It reads only decoded public integers, so no in-class statement can make it overflow. Every failure, including sizing, is a returned verdict ("derive"), not exit(1). In honest mode the prototype checks that the witness-free derivation gives the prover's relation list exactly (columns, terms, coefficients, targets). New hostile cases: +- **derive-overflow**: every class at its extreme (|S0| = 2^18 - 1 alternating, k = q = a = beta = 2^F, |v| = 2^31 - 1 alternating, o = 2^31 - 1). A checked replay of the OLD witness path on this statement overflows i64 first at token 2. New path: returns a verdict, rejected by the proof verifier. +- **derive-random**: every word uniform in its class (fixed seed). Old path: no overflow on this draw. New path: rejected by the proof verifier. +- The whole hostile suite was also run under `-fsanitize=undefined -fno-sanitize-recover`: 41/41 as expected, 0 UBSan reports, exit 0 (`runs/h5-hostile-seed1-ubsan.jsonl`). + +**H5 rerun on the new code** (seeds 1..10, pinned to 6 cores, same loaded VM): + +| metric | n | median | SD | min | max | +|---|---|---|---|---|---| +| accepted | 10 | **10/10** | | | | +| derive relations + size (s) | 10 | 0.227 | 0.009 | 0.212 | 0.236 | +| proof verify (s) | 10 | 7.79 | 0.23 | 7.51 | 8.30 | +| prove (s) | 10 | 40.42 | 0.88 | 39.38 | 42.16 | +| proof bytes | 10 | 25,411,392 | 313 | 25,410,848 | 25,411,968 | +| peak RSS (KB) | 10 | 4,435,606 | 25 | 4,435,528 | 4,435,608 | + +Proof bytes are identical to the first run, seed by seed. The derivation is about 0.05 s slower than the first prototype's (0.182 s). The review expected witness-free to be faster. It is not, because the cost is relation and column allocation, which is unchanged, and the box was more loaded (verify and prove medians also rose about 2%). This is indicative only. + +Hostile, seed 1: **41/41 at the expected stage** (was 34/34): +- the original 34, unchanged verdicts; +- conv history: a huge element, the first value outside the class on both sides, and a non-canonical word (4 cases), all rejected at decode; +- derive-overflow and derive-random (2), rejected by the proof verifier, no exit, no UB; +- history control (1): H changed in class is ACCEPTED here, as expected, because the M1b relations read no history. That is a control, not a defence. In M2 the conv recompute reads H. The "H changed in class must fail at the proof verifier" case is in the build's suite. + ## Kill criterion Not triggered. The lane started at 14:33Z. H5 honest accept came at about 15:10Z, and all results were in by 15:30Z. Peak RSS was 4.4 GB, and free disk stayed above 30 GB. @@ -147,11 +200,15 @@ Not triggered. The lane started at 14:33Z. H5 honest accept came at about 15:10Z - **H1.** The registered comparison quantity for M1b "columns used" was 1,141,248 in the PREREG text, which is non-region plus range cells. The model matches both the measured laid-out length 1,310,720 and the non-region count 1,074,176; 1,141,248 = 1,074,176 + 67,072 also holds. No threshold moves. - **H5.** The class-bound test was registered as "bound + 1 for one-sided". It ran both sides for every class (16 cases instead of 8), which is stricter. - **H4.** The PREREG listed the float divergence of y as a metric without a threshold. It is reported, and it is large (see H4). The q-unscaled and delta-kernel runs are secondary and unregistered. -- No amendments. +- **H3 (post-review).** The largest-T loop in count_model.py also required relations <= 2^23. That is lookup_size()'s own guard, but it was not in the PREREG's metric text ("the largest T that fits log-length 22"). It does not bind: without it the largest T is still 14 (A) and 41 (B), now also reported in model.json. +- **H3 (post-review).** The count model first omitted the multiplicity row of the second (remainder) lookup. It now sizes it. No count, log-length or verdict changes (see "Post-review corrections"). +- **H5 (post-review).** The prototype was changed after the registered runs (conv history words, witness-free derivation, verdicts instead of exits, 7 more hostile cases). H5 was rerun on it and passes the same registered thresholds. The registered results.yaml metrics still point at e08e5ce2, the commit of the original receipts. +- No amendments to PREREG.md. ## Open risks -- **No M2 proof exists.** Arm B needs a second LKP instance whose keys are product-oracle cells. LKP supports parts and range tables, and RNG already composes a lookup whose key claim is a laid form on a sent message. But a lookup keyed directly on message-0 cells has not been built in this repo. That is the build lane's main risk. +- **M2 is redundant as a standalone proof** (DESIGN.md section 0). All its inputs are public. Its value is as a rehearsal for M3, and it de-risks none of conv1d, the gate tables or the q/k norm. +- **No M2 proof exists.** Arm B needs a second LKP instance whose keys are product-oracle cells. LKP supports parts and range tables, and RNG already composes a lookup whose key claim is a laid form on a sent message. But a lookup keyed directly on message-0 cells has not been built in this repo, and the two instances' transcript separation (DESIGN.md section 4.3) is a requirement on that build, untested here. That is the build lane's main risk. - **Verifier-recomputed nonlinearities must be bit-exact** across prover and verifier. The prototype uses float64 numpy at table points. The build needs pinned public tables (DESIGN.md section 3). - **y fidelity is poor at F = 12** (worst 1.2 absolute) because o is small in integer units. This is a fidelity issue for M0/#8, not soundness, but it argues for a finer o scale before any fidelity claim. - **The layer extrapolation is 48x one head.** Per-token cost for 48 layers (231M product cells, arm B) means M3 needs chunked state commitment. M2 does not address that. diff --git a/experiments/20261105-32-qwen-deltanet-m2-design/runs/h5-honest.jsonl b/experiments/20261105-32-qwen-deltanet-m2-design/runs/h5-honest.jsonl index 04495e1a..c77e0cad 100644 --- a/experiments/20261105-32-qwen-deltanet-m2-design/runs/h5-honest.jsonl +++ b/experiments/20261105-32-qwen-deltanet-m2-design/runs/h5-honest.jsonl @@ -1,10 +1,10 @@ -{"seed": 1, "exit": 0, "wall": 48.517, "mode": "honest", "accept": true, "stage": "accept", "statement_words": 34827, "proof_bytes": 25411968, "prove_s": 39.878, "decode_s": 0.0001, "derive_s": 0.176, "verify_s": 7.534, "peak_rss_kb": 4433912, "stderr": ""} -{"seed": 2, "exit": 0, "wall": 48.728, "mode": "honest", "accept": true, "stage": "accept", "statement_words": 34827, "proof_bytes": 25411232, "prove_s": 40.057, "decode_s": 0.0001, "derive_s": 0.17, "verify_s": 7.562, "peak_rss_kb": 4433908, "stderr": ""} -{"seed": 3, "exit": 0, "wall": 48.674, "mode": "honest", "accept": true, "stage": "accept", "statement_words": 34827, "proof_bytes": 25411424, "prove_s": 39.729, "decode_s": 0.0001, "derive_s": 0.18, "verify_s": 7.78, "peak_rss_kb": 4433912, "stderr": ""} -{"seed": 4, "exit": 0, "wall": 49.491, "mode": "honest", "accept": true, "stage": "accept", "statement_words": 34827, "proof_bytes": 25411648, "prove_s": 40.478, "decode_s": 0.0001, "derive_s": 0.184, "verify_s": 7.861, "peak_rss_kb": 4433912, "stderr": ""} -{"seed": 5, "exit": 0, "wall": 48.863, "mode": "honest", "accept": true, "stage": "accept", "statement_words": 34827, "proof_bytes": 25411360, "prove_s": 39.965, "decode_s": 0.0001, "derive_s": 0.183, "verify_s": 7.672, "peak_rss_kb": 4433784, "stderr": ""} -{"seed": 6, "exit": 0, "wall": 48.801, "mode": "honest", "accept": true, "stage": "accept", "statement_words": 34827, "proof_bytes": 25411488, "prove_s": 39.946, "decode_s": 0.0001, "derive_s": 0.181, "verify_s": 7.664, "peak_rss_kb": 4433912, "stderr": ""} -{"seed": 7, "exit": 0, "wall": 48.451, "mode": "honest", "accept": true, "stage": "accept", "statement_words": 34827, "proof_bytes": 25411200, "prove_s": 39.768, "decode_s": 0.0001, "derive_s": 0.177, "verify_s": 7.551, "peak_rss_kb": 4433912, "stderr": ""} -{"seed": 8, "exit": 0, "wall": 48.823, "mode": "honest", "accept": true, "stage": "accept", "statement_words": 34827, "proof_bytes": 25411168, "prove_s": 39.948, "decode_s": 0.0001, "derive_s": 0.183, "verify_s": 7.667, "peak_rss_kb": 4433784, "stderr": ""} -{"seed": 9, "exit": 0, "wall": 48.424, "mode": "honest", "accept": true, "stage": "accept", "statement_words": 34827, "proof_bytes": 25411648, "prove_s": 39.772, "decode_s": 0.0001, "derive_s": 0.196, "verify_s": 7.527, "peak_rss_kb": 4433912, "stderr": ""} -{"seed": 10, "exit": 0, "wall": 48.345, "mode": "honest", "accept": true, "stage": "accept", "statement_words": 34827, "proof_bytes": 25410848, "prove_s": 39.636, "decode_s": 0.0001, "derive_s": 0.187, "verify_s": 7.55, "peak_rss_kb": 4433908, "stderr": ""} +{"seed": 1, "exit": 0, "wall": 49.882, "mode": "honest", "accept": true, "stage": "accept", "statement_words": 35979, "proof_bytes": 25411968, "prove_s": 40.519, "decode_s": 0.0001, "derive_s": 0.236, "verify_s": 7.761, "peak_rss_kb": 4435528, "stderr": ""} +{"seed": 2, "exit": 0, "wall": 49.604, "mode": "honest", "accept": true, "stage": "accept", "statement_words": 35979, "proof_bytes": 25411232, "prove_s": 40.291, "decode_s": 0.0001, "derive_s": 0.217, "verify_s": 7.741, "peak_rss_kb": 4435604, "stderr": ""} +{"seed": 3, "exit": 0, "wall": 49.491, "mode": "honest", "accept": true, "stage": "accept", "statement_words": 35979, "proof_bytes": 25411424, "prove_s": 40.313, "decode_s": 0.0001, "derive_s": 0.217, "verify_s": 7.563, "peak_rss_kb": 4435608, "stderr": ""} +{"seed": 4, "exit": 0, "wall": 51.676, "mode": "honest", "accept": true, "stage": "accept", "statement_words": 35979, "proof_bytes": 25411648, "prove_s": 42.162, "decode_s": 0.0001, "derive_s": 0.228, "verify_s": 7.911, "peak_rss_kb": 4435604, "stderr": ""} +{"seed": 5, "exit": 0, "wall": 50.069, "mode": "honest", "accept": true, "stage": "accept", "statement_words": 35979, "proof_bytes": 25411360, "prove_s": 40.534, "decode_s": 0.0001, "derive_s": 0.212, "verify_s": 7.914, "peak_rss_kb": 4435604, "stderr": ""} +{"seed": 6, "exit": 0, "wall": 48.98, "mode": "honest", "accept": true, "stage": "accept", "statement_words": 35979, "proof_bytes": 25411488, "prove_s": 39.707, "decode_s": 0.0001, "derive_s": 0.236, "verify_s": 7.642, "peak_rss_kb": 4435608, "stderr": ""} +{"seed": 7, "exit": 0, "wall": 51.106, "mode": "honest", "accept": true, "stage": "accept", "statement_words": 35979, "proof_bytes": 25411200, "prove_s": 41.522, "decode_s": 0.0001, "derive_s": 0.218, "verify_s": 8.001, "peak_rss_kb": 4435604, "stderr": ""} +{"seed": 8, "exit": 0, "wall": 51.238, "mode": "honest", "accept": true, "stage": "accept", "statement_words": 35979, "proof_bytes": 25411168, "prove_s": 41.175, "decode_s": 0.0001, "derive_s": 0.232, "verify_s": 8.295, "peak_rss_kb": 4435608, "stderr": ""} +{"seed": 9, "exit": 0, "wall": 48.452, "mode": "honest", "accept": true, "stage": "accept", "statement_words": 35979, "proof_bytes": 25411648, "prove_s": 39.38, "decode_s": 0.0001, "derive_s": 0.226, "verify_s": 7.506, "peak_rss_kb": 4435608, "stderr": ""} +{"seed": 10, "exit": 0, "wall": 49.073, "mode": "honest", "accept": true, "stage": "accept", "statement_words": 35979, "proof_bytes": 25410848, "prove_s": 39.603, "decode_s": 0.0001, "derive_s": 0.227, "verify_s": 7.825, "peak_rss_kb": 4435608, "stderr": ""} diff --git a/experiments/20261105-32-qwen-deltanet-m2-design/runs/h5-hostile-seed1-ubsan.jsonl b/experiments/20261105-32-qwen-deltanet-m2-design/runs/h5-hostile-seed1-ubsan.jsonl new file mode 100644 index 00000000..9f6aeb6b --- /dev/null +++ b/experiments/20261105-32-qwen-deltanet-m2-design/runs/h5-hostile-seed1-ubsan.jsonl @@ -0,0 +1,46 @@ +{"mode":"honest","accept":true,"stage":"accept","statement_words":35979,"proof_bytes":25411968,"prove_s":66.807,"decode_s":0.0002,"derive_s":0.366,"verify_s":13.683,"peak_rss_kb":4438952} +{"mode":"hostile","class":"huge-element","attack":"S0 = (p+1)/2","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"class-bound","attack":"S0 = first value outside class (+)","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"class-bound","attack":"S0 = first value outside class (-)","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"huge-element","attack":"a = (p+1)/2","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"class-bound","attack":"a = first value outside class (+)","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"class-bound","attack":"a = first value outside class (-)","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"huge-element","attack":"beta = (p+1)/2","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"class-bound","attack":"beta = first value outside class (+)","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"class-bound","attack":"beta = first value outside class (-)","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"huge-element","attack":"k = (p+1)/2","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"class-bound","attack":"k = first value outside class (+)","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"class-bound","attack":"k = first value outside class (-)","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"huge-element","attack":"v = (p+1)/2","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"class-bound","attack":"v = first value outside class (+)","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"class-bound","attack":"v = first value outside class (-)","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"huge-element","attack":"q = (p+1)/2","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"class-bound","attack":"q = first value outside class (+)","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"class-bound","attack":"q = first value outside class (-)","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"huge-element","attack":"o = (p+1)/2","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"class-bound","attack":"o = first value outside class (+)","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"class-bound","attack":"o = first value outside class (-)","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"huge-element","attack":"S_T = (p+1)/2","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"class-bound","attack":"S_T = first value outside class (+)","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"class-bound","attack":"S_T = first value outside class (-)","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"huge-element","attack":"H (conv history) = (p+1)/2","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"class-bound","attack":"H (conv history) = first value outside class (+)","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"class-bound","attack":"H (conv history) = first value outside class (-)","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"non-canonical","attack":"a_0 word = a_0 + p (second encoding of the same element)","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"non-canonical","attack":"o_0[0] word = o_0[0] + p","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"non-canonical","attack":"H word = H + p (conv history)","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"derive-overflow-detail","old_path_first_overflow_token":2} +{"mode":"hostile","class":"derive-overflow","attack":"every class at its extreme (old witness path: i64 overflow)","rejected":true,"stage":"proof","expected_stage":"proof","as_expected":true} +{"mode":"derive-random-detail","old_path_first_overflow_token":-1} +{"mode":"hostile","class":"derive-random","attack":"every word uniform in its class (fixed xorshift seed)","rejected":true,"stage":"proof","expected_stage":"proof","as_expected":true} +{"mode":"hostile","class":"history-control","attack":"H[0] += 1 in class (M1b reads no history: accept expected here)","rejected":false,"stage":"accept","expected_stage":"accept","as_expected":true} +{"mode":"hostile","class":"header","attack":"T word = 5","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"header","attack":"F word = 10","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"length","attack":"one trailing word","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"length","attack":"one word short","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"in-class","attack":"o_T[0] += 1 (valid integer, wrong)","rejected":true,"stage":"proof","expected_stage":"proof","as_expected":true} +{"mode":"hostile","class":"in-class","attack":"S0[0] += 1 (valid integer, wrong)","rejected":true,"stage":"proof","expected_stage":"proof","as_expected":true} +{"mode":"r9-detail","j":0,"o":-1,"o_prime_word":"4503599626321919","violated_relations":0} +{"mode":"hostile","class":"r9-control","attack":"current path: lookup_verify with caller-built relations, admissible() skipped","rejected":false,"stage":"accept","expected_stage":"accept","as_expected":true} +{"mode":"hostile","class":"r9","attack":"verify_from_words on the R9 statement and R9 proof","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile-summary","as_expected":41,"total":41,"peak_rss_kb":5036352} diff --git a/experiments/20261105-32-qwen-deltanet-m2-design/runs/h5-hostile-seed1.jsonl b/experiments/20261105-32-qwen-deltanet-m2-design/runs/h5-hostile-seed1.jsonl index f9f42e11..519cb8b4 100644 --- a/experiments/20261105-32-qwen-deltanet-m2-design/runs/h5-hostile-seed1.jsonl +++ b/experiments/20261105-32-qwen-deltanet-m2-design/runs/h5-hostile-seed1.jsonl @@ -1,4 +1,4 @@ -{"mode":"honest","accept":true,"stage":"accept","statement_words":34827,"proof_bytes":25411968,"prove_s":38.929,"decode_s":0.0001,"derive_s":0.191,"verify_s":7.656,"peak_rss_kb":4433912} +{"mode":"honest","accept":true,"stage":"accept","statement_words":35979,"proof_bytes":25411968,"prove_s":40.219,"decode_s":0.0001,"derive_s":0.211,"verify_s":7.779,"peak_rss_kb":4435608} {"mode":"hostile","class":"huge-element","attack":"S0 = (p+1)/2","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} {"mode":"hostile","class":"class-bound","attack":"S0 = first value outside class (+)","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} {"mode":"hostile","class":"class-bound","attack":"S0 = first value outside class (-)","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} @@ -23,8 +23,17 @@ {"mode":"hostile","class":"huge-element","attack":"S_T = (p+1)/2","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} {"mode":"hostile","class":"class-bound","attack":"S_T = first value outside class (+)","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} {"mode":"hostile","class":"class-bound","attack":"S_T = first value outside class (-)","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"huge-element","attack":"H (conv history) = (p+1)/2","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"class-bound","attack":"H (conv history) = first value outside class (+)","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"class-bound","attack":"H (conv history) = first value outside class (-)","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} {"mode":"hostile","class":"non-canonical","attack":"a_0 word = a_0 + p (second encoding of the same element)","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} {"mode":"hostile","class":"non-canonical","attack":"o_0[0] word = o_0[0] + p","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"hostile","class":"non-canonical","attack":"H word = H + p (conv history)","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} +{"mode":"derive-overflow-detail","old_path_first_overflow_token":2} +{"mode":"hostile","class":"derive-overflow","attack":"every class at its extreme (old witness path: i64 overflow)","rejected":true,"stage":"proof","expected_stage":"proof","as_expected":true} +{"mode":"derive-random-detail","old_path_first_overflow_token":-1} +{"mode":"hostile","class":"derive-random","attack":"every word uniform in its class (fixed xorshift seed)","rejected":true,"stage":"proof","expected_stage":"proof","as_expected":true} +{"mode":"hostile","class":"history-control","attack":"H[0] += 1 in class (M1b reads no history: accept expected here)","rejected":false,"stage":"accept","expected_stage":"accept","as_expected":true} {"mode":"hostile","class":"header","attack":"T word = 5","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} {"mode":"hostile","class":"header","attack":"F word = 10","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} {"mode":"hostile","class":"length","attack":"one trailing word","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} @@ -34,4 +43,4 @@ {"mode":"r9-detail","j":0,"o":-1,"o_prime_word":"4503599626321919","violated_relations":0} {"mode":"hostile","class":"r9-control","attack":"current path: lookup_verify with caller-built relations, admissible() skipped","rejected":false,"stage":"accept","expected_stage":"accept","as_expected":true} {"mode":"hostile","class":"r9","attack":"verify_from_words on the R9 statement and R9 proof","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} -{"mode":"hostile-summary","as_expected":34,"total":34,"peak_rss_kb":5032968} +{"mode":"hostile-summary","as_expected":41,"total":41,"peak_rss_kb":5032972} diff --git a/experiments/20261105-32-qwen-deltanet-m2-design/runs/model.json b/experiments/20261105-32-qwen-deltanet-m2-design/runs/model.json index e3894004..fc1b5832 100644 --- a/experiments/20261105-32-qwen-deltanet-m2-design/runs/model.json +++ b/experiments/20261105-32-qwen-deltanet-m2-design/runs/model.json @@ -66,7 +66,7 @@ "arm": "m1", "D": 128, "T": 4, - "o_private": false, + "o_witness": false, "norm": false, "gate_columns": 2338816, "range_cells": 0, @@ -83,7 +83,7 @@ "arm": "m1b", "D": 128, "T": 4, - "o_private": false, + "o_witness": false, "norm": false, "gate_columns": 1074176, "range_cells": 67072, @@ -103,7 +103,7 @@ "arm": "A", "D": 128, "T": 4, - "o_private": false, + "o_witness": false, "norm": false, "gate_columns": 1074176, "range_cells": 67072, @@ -120,13 +120,16 @@ ], "key_parts": [], "digit_entries": 102400, + "rng_multiplicity_places": 155648, + "remainder_multiplicity_places": 0, + "multiplicity_row_places": 155648, "log_length": 21 }, "armB_m1b_shape": { "arm": "B", "D": 128, "T": 4, - "o_private": false, + "o_witness": false, "norm": false, "gate_columns": 263168, "range_cells": 67072, @@ -146,6 +149,9 @@ 16384 ], "digit_entries": 102400, + "rng_multiplicity_places": 155648, + "remainder_multiplicity_places": 69632, + "multiplicity_row_places": 225280, "log_length": 19 }, "ratio_columns_per_cell": 0.349, @@ -159,7 +165,7 @@ "arm": "A", "D": 128, "T": 16, - "o_private": true, + "o_witness": true, "norm": true, "gate_columns": 4377056, "range_cells": 272384, @@ -176,6 +182,9 @@ ], "key_parts": [], "digit_entries": 348160, + "rng_multiplicity_places": 172032, + "remainder_multiplicity_places": 0, + "multiplicity_row_places": 172032, "log_length": 23, "prove_s_estimate": null }, @@ -183,7 +192,7 @@ "arm": "B", "D": 128, "T": 16, - "o_private": true, + "o_witness": true, "norm": true, "gate_columns": 1056800, "range_cells": 272384, @@ -203,13 +212,25 @@ 16384 ], "digit_entries": 348160, + "rng_multiplicity_places": 172032, + "remainder_multiplicity_places": 77824, + "multiplicity_row_places": 249856, "log_length": 21, - "prove_s_estimate": 39.2 + "prove_s_estimate": 39.2, + "single_part_layout": { + "key_part": 524288, + "columns_used": 1859616, + "log_length": 21 + } }, "max_T_log22": { "A": 14, "B": 41 }, + "max_T_log22_without_relation_cap": { + "A": 14, + "B": 41 + }, "layer_extrapolation": { "A": { "relations_per_token_per_layer": 27128832, @@ -222,6 +243,7 @@ "columns_per_token_48_deltanet_layers": 231245568 } }, + "fits_2_21_armB_T16": true, "pass_": true, "note": "prove_s_estimate is linear in table length from #47/#63 points; indicative only. Layer extrapolation = 48 V heads x per-head-per-token; DeltaNet recurrence + gated norm only (no matmuls)." }, diff --git a/research/operators/qwen_deltanet_m2/README.md b/research/operators/qwen_deltanet_m2/README.md index 277cf545..f695273d 100644 --- a/research/operators/qwen_deltanet_m2/README.md +++ b/research/operators/qwen_deltanet_m2/README.md @@ -15,6 +15,9 @@ Reproduce (CPU, about 12 min on 6 cores, about 4.5 GB RAM): ``` python3 research/operators/qwen_deltanet_m2/record.py build honest hostile model ranges +python3 research/operators/qwen_deltanet_m2/record.py ubsan # hostile suite under UBSan, about 4 min ``` +Post-review (#66): the statement words now include the conv history (3 x 384, class |H| < 2^31). The verifier's derivation is witness-free (`build(z, true)`), and every failure is a returned verdict, never exit(1). The hostile suite has 41 cases. + Research security settings only (target 10 bits). diff --git a/research/operators/qwen_deltanet_m2/admit_proto.cpp b/research/operators/qwen_deltanet_m2/admit_proto.cpp index 9db592ec..d61bd48a 100644 --- a/research/operators/qwen_deltanet_m2/admit_proto.cpp +++ b/research/operators/qwen_deltanet_m2/admit_proto.cpp @@ -4,19 +4,34 @@ // every integer itself, derives every relation target from the decoded integers, and only then runs // the unchanged proof verifier (lookup.h). There is no separate admissible() a caller can skip, and // no entry point that accepts caller-built relations. -// The statement is the M1b one (one head, D = 128, T = 4, F = 12). Usage: +// The statement is the M1b one (one head, D = 128, T = 4, F = 12), plus the conv history block that +// the M2 statement adds (DESIGN.md section 2, post-review #66): the last 3 pre-conv inputs of each of +// the head's 384 conv channels before token 0, public and class-bounded. The M1b relations do not +// read it (M1b has no conv; in M2 the verifier's conv recompute reads it), so here it tests decode +// and admission only. Usage: // admit_proto.bin honest prove from the fixture; verify_from_words; one JSON line // admit_proto.bin hostile honest run + every pre-registered hostile encoding #include "m1b_builder.h" // The statement on the wire, in this order: -// T, D, F | S0[D*D] | per token t: a, beta, k[D], v[D], q[D], o[D] | S_T[D*D] -// The verifier fixes the shape (T, D, F) itself; the header must equal it. +// T, D, F | S0[D*D] | H[3 * 3D] | per token t: a, beta, k[D], v[D], q[D], o[D] | S_T[D*D] +// H[tau * 3D + ch] = pre-conv input of channel ch at token -1 - tau (tau = 0, 1, 2; ch < 3D: the +// q, k and v channels). The verifier fixes the shape (T, D, F) itself; the header must equal it. struct Shape { int T = 4, D = 128, F = 12; }; +constexpr i64 XBOUND = i64(1) << 31; // pre-conv inputs, as xq, xk, xv in DESIGN.md section 2 +static size_t hist_words(const Shape &s) { return size_t(3) * 3 * s.D; } static u64 enc(i64 x) { return fi(x).v; } +// Honest conv history for the M1b fixture: filler values in class (the first 3 * 3D entries of S0, +// times 2^12). Nothing in the M1b relation list reads them. +static std::vector history_of(const Fixture &z) { + std::vector h(size_t(3) * 3 * z.D); + for (size_t i = 0; i < h.size(); i++) h[i] = z.S0[i % z.S0.size()] * 4096; + return h; +} static std::vector encode(const Fixture &z) { std::vector w = {u64(z.T), u64(z.D), u64(z.F)}; for (auto s : z.S0) w.push_back(enc(s)); + for (auto x : history_of(z)) w.push_back(enc(x)); for (int t = 0; t < z.T; t++) { w.push_back(enc(z.a[t])); w.push_back(enc(z.beta[t])); for (auto x : z.k[t]) w.push_back(enc(x)); @@ -28,7 +43,8 @@ static std::vector encode(const Fixture &z) { return w; } static size_t w_S0(const Shape &, size_t c) { return 3 + c; } -static size_t w_tok(const Shape &s, int t) { return 3 + size_t(s.D) * s.D + size_t(t) * (2 + 4 * s.D); } +static size_t w_H(const Shape &s, size_t i) { return 3 + size_t(s.D) * s.D + i; } +static size_t w_tok(const Shape &s, int t) { return 3 + size_t(s.D) * s.D + hist_words(s) + size_t(t) * (2 + 4 * s.D); } static size_t w_a(const Shape &s, int t) { return w_tok(s, t); } static size_t w_beta(const Shape &s, int t) { return w_tok(s, t) + 1; } static size_t w_k(const Shape &s, int t, int i) { return w_tok(s, t) + 2 + i; } @@ -37,12 +53,16 @@ static size_t w_q(const Shape &s, int t, int i) { return w_tok(s, t) + 2 + 2 * s static size_t w_o(const Shape &s, int t, int i) { return w_tok(s, t) + 2 + 3 * s.D + i; } static size_t w_ST(const Shape &s, size_t c) { return w_tok(s, s.T) + c; } -enum class Verdict { ACCEPT, DECODE, PROOF }; -static const char *stage_name(Verdict x) { return x == Verdict::ACCEPT ? "accept" : x == Verdict::DECODE ? "decode" : "proof"; } +// DERIVE: the relation derivation or sizing refused (a reject, never a process exit). With in-class +// inputs and the witness-free derivation it should not happen; it is a verdict so that it cannot +// become exit(1) or undefined behaviour. +enum class Verdict { ACCEPT, DECODE, DERIVE, PROOF }; +static const char *stage_name(Verdict x) { return x == Verdict::ACCEPT ? "accept" : x == Verdict::DECODE ? "decode" : x == Verdict::DERIVE ? "derive" : "proof"; } struct VerifyTiming { double decode_s = 0, derive_s = 0, verify_s = 0; std::string why; }; // One word as a signed integer of a class: canonical (< p), then lo <= x <= hi. A word above p/2 -// reads as negative. +// reads as negative. P / 2 rounds down, so the word (p - 1) / 2 is the largest positive integer and +// (p + 1) / 2 the most negative one. static bool decode(u64 w, i64 lo, i64 hi, i64 &out) { if (w >= P) return false; const i64 x = w > P / 2 ? -(i64)(P - w) : (i64)w; @@ -51,7 +71,7 @@ static bool decode(u64 w, i64 lo, i64 hi, i64 &out) { } // Sizing as in the M1b test, a function of the derived statement only (the M1b schedule held at its // first try in #63; a schedule search would also be a pure function of the shape). -struct Sized { LookupRun run; unsigned L = 0; }; +struct Sized { LookupRun run; unsigned L = 0; bool ok = true; }; static Sized size_run(const Built &b) { unsigned L = 1; while ((u64(1) << L) < b.cols.size()) L++; { @@ -66,19 +86,33 @@ static Sized size_run(const Built &b) { RegionSpec rs; rs.rows = {b.rc_rows}; rs.first_place = {b.rc_base}; rs.columns = RCOLS; const std::vector further = {{1, 1, L, L}, {0, 0, L, L}}; Sized s{lookup_size(set, L, L + 1, L + 1, further, b.rel, rs), L}; - check(s.run.product.plan->holds(), "plan holds (M1b schedule)"); + s.ok = s.run.product.plan->holds(); // the caller decides: the prover stops, the verifier rejects return s; } +// The verifier's sizing: the shape alone fixes L, the region and the relation count (the M2 build +// fixes them as constants), so lookup_size()'s guards cannot fire on any statement that decoded. +// They are checked here first anyway, so that a reject is returned instead of a BLK_REQUIRE exit. +static bool size_preconditions(const Built &b) { + if (b.rel.empty() || b.rel.size() > (u64(1) << 23)) return false; + unsigned L = 1; while ((u64(1) << L) < b.cols.size()) L++; + if (L > 22) return false; + for (const auto &a : b.rel) { + if (wide_fault(a)) return false; + for (const auto &t : a.terms) if (t.column >= b.cols.size()) return false; + } + return true; +} // THE ENTRY POINT. Everything a caller supplies is untrusted: the statement words and the proof bytes. // (1) fix the shape; (2) decode every word as a canonical field element and read it as an integer of // its class, rejecting anything outside; (3) derive every relation target from the decoded integers -// (relation-only build); (4) size and run the unchanged proof verifier. +// (relation-only build, no witness: see m1b_builder.h); (4) size and run the unchanged proof +// verifier. Every failure is a returned verdict; nothing here exits the process. // Class bounds (the M1b admit, now inside the verifier): |S0|, |S_T| < 2^18; a, beta in [0, 2^F]; -// |k|, |q| <= 2^F; |v|, |o| < 2^31. +// |k|, |q| <= 2^F; |v|, |o| < 2^31; conv history |H| < 2^31. static Verdict verify_from_words(const Shape &sh, const std::vector &w, const u8 *proof, size_t bytes, VerifyTiming *tm = nullptr) { auto t0 = std::chrono::steady_clock::now(); - const size_t DD = size_t(sh.D) * sh.D, want = 3 + DD + size_t(sh.T) * (2 + 4 * sh.D) + DD; + const size_t DD = size_t(sh.D) * sh.D, want = 3 + DD + hist_words(sh) + size_t(sh.T) * (2 + 4 * sh.D) + DD; if (w.size() != want) { if (tm) tm->why = "statement length"; return Verdict::DECODE; } if (w[0] != u64(sh.T) || w[1] != u64(sh.D) || w[2] != u64(sh.F)) { if (tm) tm->why = "header differs from the verifier's shape"; return Verdict::DECODE; } const i64 one = i64(1) << sh.F; @@ -86,6 +120,8 @@ static Verdict verify_from_words(const Shape &sh, const std::vector &w, con size_t p = 3; bool ok = true; auto take = [&](size_t n, i64 lo, i64 hi) { std::vector r(n, 0); for (size_t i = 0; i < n && ok; i++) ok = decode(w[p++], lo, hi, r[i]); return r; }; z.S0 = take(DD, -(BOUND - 1), BOUND - 1); + const std::vector H = take(hist_words(sh), -(XBOUND - 1), XBOUND - 1); // M2: read by the conv recompute + (void)H; for (int t = 0; t < sh.T && ok; t++) { z.a.push_back(take(1, 0, one)[0]); z.beta.push_back(take(1, 0, one)[0]); z.k.push_back(take(sh.D, -one, one)); z.v.push_back(take(sh.D, -(VBOUND - 1), VBOUND - 1)); @@ -98,8 +134,10 @@ static Verdict verify_from_words(const Shape &sh, const std::vector &w, con // are not read by the relation list. Shapes only. z.S.assign(sh.T, std::vector(DD, 0)); z.S[sh.T - 1] = ST; auto t1 = std::chrono::steady_clock::now(); - g_relonly = true; Built b = build(z); g_relonly = false; + Built b = build(z, true); + if (!size_preconditions(b)) { if (tm) tm->why = "derived statement outside the sizing guards"; return Verdict::DERIVE; } Sized s = size_run(b); + if (!s.ok) { if (tm) tm->why = "no plan holds for the derived statement"; return Verdict::DERIVE; } if (tm) tm->derive_s = secs(t1); auto t2 = std::chrono::steady_clock::now(); const char *why = nullptr; @@ -108,6 +146,41 @@ static Verdict verify_from_words(const Shape &sh, const std::vector &w, con return acc ? Verdict::ACCEPT : Verdict::PROOF; } +// Control for the derive-overflow cases: would the FIRST prototype's verifier path (the full witness +// recurrence with its checks off) overflow i64 on this statement? Same arithmetic as build(), with +// every product and sum checked. Returns the token at which the first overflow happens, or -1. +static int old_path_overflow_token(const Fixture &z) { + const int D = z.D; const i64 one = i64(1) << z.F; std::vector S = z.S0, Sn(size_t(D) * D); + auto mul = [](i64 a, i64 b, bool &bad) { i64 r; bad |= __builtin_mul_overflow(a, b, &r); return r; }; + auto add = [](i64 a, i64 b, bool &bad) { i64 r; bad |= __builtin_add_overflow(a, b, &r); return r; }; + for (int t = 0; t < z.T; t++) { + bool bad = false; std::vector delta(D); + for (int j = 0; j < D; j++) { + i64 Pj = 0; for (int i = 0; i < D; i++) Pj = add(Pj, mul(S[i * D + j], z.k[t][i], bad), bad); + const i64 aP = mul(Pj, z.a[t], bad); const i64 M = fdiv(aP, one * one); + delta[j] = fdiv(mul(add(z.v[t][j], -M, bad), z.beta[t], bad), one); + } + for (int i = 0; i < D; i++) for (int j = 0; j < D; j++) Sn[i * D + j] = fdiv(add(mul(z.a[t], S[i * D + j], bad), mul(z.k[t][i], delta[j], bad), bad), one); + for (int j = 0; j < D; j++) { i64 s = 0; for (int i = 0; i < D; i++) s = add(s, mul(Sn[i * D + j], z.q[t][i], bad), bad); } + if (bad) return t; + S = Sn; + } + return -1; +} +// The words of a statement, decoded without class checks (the hostile statements below are in class). +static Fixture fixture_of_words(const Shape &sh, const std::vector &w) { + auto rd = [](u64 x) { return x > P / 2 ? -(i64)(P - x) : (i64)x; }; + Fixture z; z.T = sh.T, z.D = sh.D, z.F = sh.F; const size_t DD = size_t(sh.D) * sh.D; + for (size_t c = 0; c < DD; c++) z.S0.push_back(rd(w[w_S0(sh, c)])); + for (int t = 0; t < sh.T; t++) { + z.a.push_back(rd(w[w_a(sh, t)])); z.beta.push_back(rd(w[w_beta(sh, t)])); + std::vector k, v, q, o; + for (int i = 0; i < sh.D; i++) k.push_back(rd(w[w_k(sh, t, i)])), v.push_back(rd(w[w_v(sh, t, i)])), q.push_back(rd(w[w_q(sh, t, i)])), o.push_back(rd(w[w_o(sh, t, i)])); + z.k.push_back(k); z.v.push_back(v); z.q.push_back(q); z.o.push_back(o); + } + return z; +} + static Table table_of(const Built &b, unsigned L) { Table tab(1, 3, u64(1) << L); for (size_t c = 0; c < b.cols.size(); c++) for (int r = 0; r < 3; r++) tab.set(r, c, E::from(b.cols[c][r])); @@ -124,8 +197,19 @@ int main(int argc, char **argv) { Built b = build(z); for (int t = 0; t < z.T; t++) check(b.M[t] == z.M[t] && b.delta[t] == z.delta[t] && b.S[t] == z.S[t] && b.o[t] == z.o[t], "C++ lowering equals reference.py"); Sized s = size_run(b); + check(s.ok, "plan holds (M1b schedule)"); Table tab = table_of(b, s.L); check(products_hold_all(tab) && violated(b.rel, tab) == 0, "honest table"); + { // the witness-free derivation gives the prover's relation list exactly (columns, terms, targets) + const Built v = build(z, true); + bool same = v.cols.size() == b.cols.size() && v.rel.size() == b.rel.size(); + for (size_t i = 0; same && i < b.rel.size(); i++) { + same = v.rel[i].target.v == b.rel[i].target.v && v.rel[i].terms.size() == b.rel[i].terms.size(); + for (size_t k = 0; same && k < b.rel[i].terms.size(); k++) + same = v.rel[i].terms[k].row == b.rel[i].terms[k].row && v.rel[i].terms[k].column == b.rel[i].terms[k].column && v.rel[i].terms[k].coefficient.v == b.rel[i].terms[k].coefficient.v; + } + check(same, "witness-free derivation equals the prover's relation list"); + } auto tp = std::chrono::steady_clock::now(); Writer proof = prove(s.run, b, tab, false); const double prove_s = secs(tp); const std::vector words = encode(z); auto pb = proof.bytes(); @@ -150,7 +234,8 @@ int main(int argc, char **argv) { // One element per class; out_pos / out_neg = the first integers outside the class on each side. const std::vector cls = { {"S0", w_S0(sh, 0), BOUND, -BOUND}, {"a", w_a(sh, 1), one + 1, -1}, {"beta", w_beta(sh, 1), one + 1, -1}, {"k", w_k(sh, 1, 0), one + 1, -one - 1}, - {"v", w_v(sh, 1, 0), VBOUND, -VBOUND}, {"q", w_q(sh, 1, 0), one + 1, -one - 1}, {"o", w_o(sh, sh.T - 1, 0), VBOUND, -VBOUND}, {"S_T", w_ST(sh, 0), BOUND, -BOUND}}; + {"v", w_v(sh, 1, 0), VBOUND, -VBOUND}, {"q", w_q(sh, 1, 0), one + 1, -one - 1}, {"o", w_o(sh, sh.T - 1, 0), VBOUND, -VBOUND}, {"S_T", w_ST(sh, 0), BOUND, -BOUND}, + {"H (conv history)", w_H(sh, 3 * sh.D * 2 + 5), XBOUND, -XBOUND}}; for (const Cls &c : cls) { report("huge-element", std::string(c.name) + " = (p+1)/2", verify_from_words(sh, with(c.idx, HALF), pb.data(), pb.size()), Verdict::DECODE); report("class-bound", std::string(c.name) + " = first value outside class (+)", verify_from_words(sh, with(c.idx, enc(c.out_pos)), pb.data(), pb.size()), Verdict::DECODE); @@ -161,7 +246,41 @@ int main(int argc, char **argv) { report("non-canonical", "a_0 word = a_0 + p (second encoding of the same element)", verify_from_words(sh, with(idx, words[idx] + P), pb.data(), pb.size()), Verdict::DECODE); const size_t io = w_o(sh, 0, 0); if (words[io] < (u64(1) << 32)) report("non-canonical", "o_0[0] word = o_0[0] + p", verify_from_words(sh, with(io, words[io] + P), pb.data(), pb.size()), Verdict::DECODE); + size_t ih = w_H(sh, 0); while (words[ih] >= (u64(1) << 32)) ih++; + report("non-canonical", "H word = H + p (conv history)", verify_from_words(sh, with(ih, words[ih] + P), pb.data(), pb.size()), Verdict::DECODE); + } + // Derive-step hostile cases (post-review #66 should-fix 3): in-class but inconsistent statements + // whose witness, if the verifier computed it, overflows i64 within T = 4 tokens (|S0| near 2^18, + // k = q = 2^F, a = beta = 2^F, |v| near 2^31: a P_j is about 2^49 at token 0 and 2^62 at token 1, + // and a P_j passes 2^63 at token 2: signed-overflow UB in the old witness path). The witness-free derivation must not crash or exit: it returns a verdict, and the + // proof verifier rejects (the honest proof does not prove this statement). + { + auto w = words; + for (size_t c = 0; c < size_t(sh.D) * sh.D; c++) w[w_S0(sh, c)] = enc((c & 1) ? BOUND - 1 : -(BOUND - 1)); + for (int t = 0; t < sh.T; t++) { + w[w_a(sh, t)] = enc(one); w[w_beta(sh, t)] = enc(one); + for (int i = 0; i < sh.D; i++) { w[w_k(sh, t, i)] = enc(one); w[w_q(sh, t, i)] = enc(one); w[w_v(sh, t, i)] = enc((i & 1) ? VBOUND - 1 : -(VBOUND - 1)); w[w_o(sh, t, i)] = enc(VBOUND - 1); } + } + std::printf("{\"mode\":\"derive-overflow-detail\",\"old_path_first_overflow_token\":%d}\n", old_path_overflow_token(fixture_of_words(sh, w))); + report("derive-overflow", "every class at its extreme (old witness path: i64 overflow)", verify_from_words(sh, w, pb.data(), pb.size()), Verdict::PROOF); + } + { + auto w = words; u64 st = 0x9e3779b97f4a7c15ULL; + auto rnd = [&](i64 lo, i64 hi) { st ^= st << 13; st ^= st >> 7; st ^= st << 17; return lo + i64(st % u64(hi - lo + 1)); }; + for (size_t c = 0; c < size_t(sh.D) * sh.D; c++) w[w_S0(sh, c)] = enc(rnd(-(BOUND - 1), BOUND - 1)); + for (size_t i = 0; i < hist_words(sh); i++) w[w_H(sh, i)] = enc(rnd(-(XBOUND - 1), XBOUND - 1)); + for (int t = 0; t < sh.T; t++) { + w[w_a(sh, t)] = enc(rnd(0, one)); w[w_beta(sh, t)] = enc(rnd(0, one)); + for (int i = 0; i < sh.D; i++) { w[w_k(sh, t, i)] = enc(rnd(-one, one)); w[w_q(sh, t, i)] = enc(rnd(-one, one)); w[w_v(sh, t, i)] = enc(rnd(-(VBOUND - 1), VBOUND - 1)); w[w_o(sh, t, i)] = enc(rnd(-(VBOUND - 1), VBOUND - 1)); } + } + for (size_t c = 0; c < size_t(sh.D) * sh.D; c++) w[w_ST(sh, c)] = enc(rnd(-(BOUND - 1), BOUND - 1)); + std::printf("{\"mode\":\"derive-random-detail\",\"old_path_first_overflow_token\":%d}\n", old_path_overflow_token(fixture_of_words(sh, w))); + report("derive-random", "every word uniform in its class (fixed xorshift seed)", verify_from_words(sh, w, pb.data(), pb.size()), Verdict::PROOF); } + // Conv history in class but changed: nothing in the M1b relation list reads it, so this ACCEPTS + // here. Recorded as a control, not a defence: in M2 the conv recompute reads H, so k, q, v change + // and the proof verifier must reject. That case belongs to the M2 build's H4 suite. + report("history-control", "H[0] += 1 in class (M1b reads no history: accept expected here)", verify_from_words(sh, with(w_H(sh, 0), enc(history_of(z)[0] + 1)), pb.data(), pb.size()), Verdict::ACCEPT); report("header", "T word = 5", verify_from_words(sh, with(0, 5), pb.data(), pb.size()), Verdict::DECODE); report("header", "F word = 10", verify_from_words(sh, with(2, 10), pb.data(), pb.size()), Verdict::DECODE); { auto w = words; w.push_back(0); report("length", "one trailing word", verify_from_words(sh, w, pb.data(), pb.size()), Verdict::DECODE); } diff --git a/research/operators/qwen_deltanet_m2/count_model.py b/research/operators/qwen_deltanet_m2/count_model.py index a4d2c83f..a0bd33d9 100644 --- a/research/operators/qwen_deltanet_m2/count_model.py +++ b/research/operators/qwen_deltanet_m2/count_model.py @@ -9,7 +9,7 @@ B remainders lookup-exact: each 12-bit remainder limb is ONE product cell that is a direct LKP key into the 2^12 range table; a 24-bit remainder is two such limbs (coefficients 1 and 2^12) in the floor relation itself, so no recomposition relation is needed. -M2 adds (DESIGN.md section 4): private head output o (RNG cell), the proved gated RMSNorm of o +M2 adds (DESIGN.md section 4): head output o as a witness RNG cell (not in the statement; computable from public inputs at M2), the proved gated RMSNorm of o (o*o and o*Rc gates, isqrt and reciprocal scalars with exact limbs, w RNG cell, y floor), T = 16. Usage: count_model.py [--json out.json] """ @@ -42,7 +42,7 @@ def rem(s, width, n=1): s.key(width // 12 * n) return width // 12 -def head(arm, D=128, T=4, F=12, o_private=False, norm=False): +def head(arm, D=128, T=4, F=12, o_witness=False, norm=False): c = C(arm) q19 = arm == 'm1' # M1: 19-bit range decomposition of S', M, delta (no RNG) for t in range(T): @@ -80,7 +80,7 @@ def head(arm, D=128, T=4, F=12, o_private=False, norm=False): # output o[j] = floor(sum_i g4.c / 2^F) for _ in range(D): r = c.rem(F) - if o_private: + if o_witness: c.rcell(); c.rel(D + r + 1) # -2^F o (cell) + sum g4.c - r = 0 else: c.rel(D + r) # target -2^F o @@ -91,7 +91,7 @@ def head(arm, D=128, T=4, F=12, o_private=False, norm=False): return c def gated_norm(c, D, F): - """Proved gated RMSNorm of the private o (DESIGN.md section 4.4). Per token: + """Proved gated RMSNorm of the witness o (DESIGN.md section 4.4). Per token: Vx = sum o^2; sigma = isqrt(Vx + eps); Rc = floor(2^36 / sigma); w = floor(o Rc / 2^24); y = floor(cg w / 2^F) with cg public (verifier-derived from gain, SiLU(z), sqrt(D)); y public.""" # scalars: sigma (2 exact 12-bit limbs), e1 = Vx + eps - sigma^2 (2 limbs), e2 = sigma^2 + 2 sigma - Vx - eps (2), @@ -145,14 +145,48 @@ def pieces(n, quantum): # RNG digits must fit one row: digit_columns (pow2 of cell rows) x Np (16 at N = 10) dc = P2(sum(rng_rows)) if rng_rows else 0 L = max(L, math.ceil(math.log2(dc * 16)) if dc else 0) - return dict(columns_used=total, columns_nonregion=c.cols, rng_rows=rng_rows, key_parts=key_parts, digit_entries=2 * sum(rng_rows) * 10, log_length=L) + # Multiplicity planes (post-review, #66 should-fix 1). Each LKP instance sends its multiplicities + # as planes x 2^d places in row 0 of a message (rng_planes_places in blocks/iop/rng.h). RNG's + # digit lookup (rng_lookup): one part of 2^(1 + log dc + log Np) keys, d = max(b0, b1) + 1 = 13, + # planes = part + 1. The remainder lookup (arm B): parts as above, one function, d = 12, planes = + # the least with all keys < 2^planes (lkp_fault). The design puts both planes vectors in ONE + # multiplicity row (RNG's message 3, the remainder planes after RNG's), so that row must hold + # their sum; a separate message would need only the larger of the two. Either way it bounds L. + rng_mult = rem_mult = 0 + if dc: + v = 1 + int(math.log2(dc)) + 4 + rng_mult = (v + 1) << 13 + if key_parts: + nkeys = sum(key_parts); planes = nkeys.bit_length() # least planes with nkeys < 2^planes + assert all(int(math.log2(p)) < planes for p in key_parts) + rem_mult = planes << 12 + mult_row = rng_mult + rem_mult + L = max(L, math.ceil(math.log2(mult_row)) if mult_row else 0) + return dict(columns_used=total, columns_nonregion=c.cols, rng_rows=rng_rows, key_parts=key_parts, digit_entries=2 * sum(rng_rows) * 10, + rng_multiplicity_places=rng_mult, remainder_multiplicity_places=rem_mult, multiplicity_row_places=mult_row, log_length=L) + +def single_part_layout(c): + """Review cross-check (#66): arm B with the keys as ONE power-of-two part (no LkpPublic::parts), + padded up. Returns the laid length and log-length, with the same block placement as layout().""" + def pieces(n, quantum): + n = math.ceil(n / quantum) * quantum; out = []; b = 1 << n.bit_length() + while n: + b >>= 1 + if n & b: out.append(b); n -= b + return out + rng_rows = pieces(math.ceil(c.rcells / 16), 1024) + blocks = sorted([r * 16 for r in rng_rows] + [P2(c.keys)], reverse=True) + pos = 0 + for b in blocks: pos = math.ceil(pos / b) * b + b + total = pos + c.cols + return dict(key_part=P2(c.keys), columns_used=total, log_length=math.ceil(math.log2(total))) def summary(arm, **kw): c = head(arm, **kw); D, T = kw.get('D', 128), kw.get('T', 4) lay = layout(c, arm, T) cells = D * D * T per_cell = (c.cols + c.rcells + c.keys) / cells - return dict(arm=arm, D=D, T=T, o_private=kw.get('o_private', False), norm=kw.get('norm', False), + return dict(arm=arm, D=D, T=T, o_witness=kw.get('o_witness', False), norm=kw.get('norm', False), gate_columns=c.cols, range_cells=c.rcells, lkp_keys=c.keys, relations=c.rels, terms=c.terms, product_columns_per_state_cell_per_token=round(per_cell, 3), relations_per_state_cell_per_token=round(c.rels / cells, 3), **lay) @@ -180,19 +214,26 @@ def main(): res['H2'] = dict(armA_m1b_shape=A4, armB_m1b_shape=B4, ratio_columns_per_cell=round(ratio_cols, 3), ratio_relations=round(ratio_rels, 3), ratio_terms=round(B4['terms'] / A4['terms'], 3), pass_=ratio_cols <= 0.5 and ratio_rels <= 0.5, note='arm A at the M1b shape equals M1b (same lowering); arm B swaps exact bits for direct LKP keys') - m2 = dict(D=128, o_private=True, norm=True) + m2 = dict(D=128, o_witness=True, norm=True) A16 = summary('A', T=16, **m2); B16 = summary('B', T=16, **m2) - maxT = {} + maxT = {}; maxT_nocap = {} for arm in ('A', 'B'): + # The relations <= 2^23 condition is lookup_size()'s own guard; it was not in the PREREG's + # metric text (a deviation, listed in RESULTS.md). maxT_nocap drops it to show it does not bind. T = 1 while summary(arm, T=T + 1, **m2)['log_length'] <= 22 and summary(arm, T=T + 1, **m2)['relations'] <= 2 ** 23: T += 1 maxT[arm] = T + T = 1 + while summary(arm, T=T + 1, **m2)['log_length'] <= 22: T += 1 + maxT_nocap[arm] = T layer = {arm: dict(relations_per_token_per_layer=48 * s['relations'] // 16) for arm, s in (('A', A16), ('B', B16))} for arm, s in (('A', A16), ('B', B16)): layer[arm]['columns_per_token_per_layer'] = 48 * (s['gate_columns'] + s['range_cells'] + s['lkp_keys']) // 16 layer[arm]['columns_per_token_48_deltanet_layers'] = 48 * layer[arm]['columns_per_token_per_layer'] s['prove_s_estimate'] = estimate_prove_s(s['log_length'], s['relations']) if s['log_length'] <= 22 else None - res['H3'] = dict(armA_T16=A16, armB_T16=B16, max_T_log22=maxT, layer_extrapolation=layer, pass_=B16['log_length'] <= 22, + B16['single_part_layout'] = single_part_layout(head('B', T=16, **m2)) + res['H3'] = dict(armA_T16=A16, armB_T16=B16, max_T_log22=maxT, max_T_log22_without_relation_cap=maxT_nocap, layer_extrapolation=layer, + fits_2_21_armB_T16=B16['log_length'] <= 21, pass_=B16['log_length'] <= 22, note='prove_s_estimate is linear in table length from #47/#63 points; indicative only. Layer extrapolation = 48 V heads x per-head-per-token; DeltaNet recurrence + gated norm only (no matmuls).') # remainder savings (hard rule c), at the M2 shape res['remainder_savings'] = dict( diff --git a/research/operators/qwen_deltanet_m2/evidence.json b/research/operators/qwen_deltanet_m2/evidence.json index e704c221..7dbba70b 100644 --- a/research/operators/qwen_deltanet_m2/evidence.json +++ b/research/operators/qwen_deltanet_m2/evidence.json @@ -1,15 +1,15 @@ { "platform": "Linux-6.8.0-100-generic-x86_64-with-glibc2.39", "python": "3.12.3", - "cores": "0-5", + "cores": "10-15", "compiler": "g++ (Ubuntu 13.3.0-6ubuntu2~24.04.1) 13.3.0", - "base": "b6186e6ccb687348e3be66fec51bb3756879b8dc", + "base": "4f7a876151c309f0159e88b18c8e286d4e1909c9", "sources": { - "research/operators/qwen_deltanet_m2/count_model.py": "9136d5ab4a6ebbc8999c1829f1be2ef39469d019abf16a9699411f3002271499", - "research/operators/qwen_deltanet_m2/head_reference.py": "97f0586c1878969035c0bbd00f1cc684522b003c49db35bdb9acf35b576f5626", - "research/operators/qwen_deltanet_m2/record.py": "25fe5d53ceba293114a1c1bf2c018db08c910784efde3761247e112a3f8ddf2a", - "research/operators/qwen_deltanet_m2/admit_proto.cpp": "8c6a780efc965e87be6433e466e5326e38d37223e9f716269267fb40ace0611d", - "research/operators/qwen_deltanet_m2/m1b_builder.h": "a66d4b372400a20af89869fffb2db8c9c118b99644740699cd9939f6154c47f0", + "research/operators/qwen_deltanet_m2/count_model.py": "0a1349252df326109cf619825c22239a48a04a4ab59e3d0617510022c883fcf7", + "research/operators/qwen_deltanet_m2/head_reference.py": "1999ef56efd4752dfe493ea29cd8930b0c8e7bc3c9ee267a5c7f4ccbce3f6ff7", + "research/operators/qwen_deltanet_m2/record.py": "17ca9221213f9c1f33c489a2c6ec3788f652f5bab767a0403588e526a44ba69e", + "research/operators/qwen_deltanet_m2/admit_proto.cpp": "9c12e1b18ed50c68741d9c9a49a3c2a49584585aaeafbf66cedfd7826932a3cc", + "research/operators/qwen_deltanet_m2/m1b_builder.h": "434e3534097c3bd89ca3f140072e5c3ead9a7fc9d12a05108dfa7bc59625669c", "research/operators/qwen_deltanet_lookup/lookup.h": "04bc74b28fb34f5a3a20ce7413d1310297c7fbb2f22a8da7aa9ae5caaaefd770", "research/operators/qwen_deltanet/reference.py": "f6fbd580bf834afd2d6280036ddf52d410be094824299e89c61eb2a1b80edaf0" }, @@ -232,19 +232,19 @@ "research/operators/qwen_deltanet_m2/work/admit_proto.bin" ], "exit": 0, - "seconds": 32.701, + "seconds": 33.782, "stdout": "", - "stderr": "In file included from research/operators/qwen_deltanet_m2/admit_proto.cpp:10:\nresearch/operators/qwen_deltanet_m2/m1b_builder.h:48:13: warning: \u2018bool admissible(const Fixture&)\u2019 defined but not used [-Wunused-function]\n 48 | static bool admissible(const Fixture &z) {\n | ^~~~~~~~~~\n" + "stderr": "In file included from research/operators/qwen_deltanet_m2/admit_proto.cpp:14:\nresearch/operators/qwen_deltanet_m2/m1b_builder.h:53:13: warning: \u2018bool admissible(const Fixture&)\u2019 defined but not used [-Wunused-function]\n 53 | static bool admissible(const Fixture &z) {\n | ^~~~~~~~~~\n" }, "honest": { "accepted": 10, "of": 10, "prove_s": { "n": 10, - "median": 39.912, - "sd": 0.2351061462403741, - "min": 39.636, - "max": 40.478 + "median": 40.416, + "sd": 0.8835618823828911, + "min": 39.38, + "max": 42.162 }, "decode_s": { "n": 10, @@ -255,17 +255,17 @@ }, "derive_s": { "n": 10, - "median": 0.182, - "sd": 0.006961002482082273, - "min": 0.17, - "max": 0.196 + "median": 0.2265, + "sd": 0.008504247304860215, + "min": 0.212, + "max": 0.236 }, "verify_s": { "n": 10, - "median": 7.6129999999999995, - "sd": 0.11390912557336605, - "min": 7.527, - "max": 7.861 + "median": 7.793, + "sd": 0.23127926841807492, + "min": 7.506, + "max": 8.295 }, "proof_bytes": { "n": 10, @@ -276,29 +276,41 @@ }, "peak_rss_kb": { "n": 10, - "median": 4433912.0, - "sd": 53.57279574975012, - "min": 4433784, - "max": 4433912 + "median": 4435606.0, + "sd": 24.81576559814793, + "min": 4435528, + "max": 4435608 } }, "hostile": { "exit": 0, - "seconds": 106.764, - "as_expected": 34, - "total": 34, + "seconds": 129.884, + "as_expected": 41, + "total": 41, "by_class": { "huge-element": [ - 8, - 8 + 9, + 9 ], "class-bound": [ - 16, - 16 + 18, + 18 ], "non-canonical": [ - 2, - 2 + 3, + 3 + ], + "derive-overflow": [ + 1, + 1 + ], + "derive-random": [ + 1, + 1 + ], + "history-control": [ + 1, + 1 ], "header": [ 2, @@ -325,8 +337,8 @@ }, "model": { "exit": 0, - "seconds": 0.884, - "stdout": "{\n \"H1\": {\n \"checks\": {\n \"m1\": {\n \"columns_used\": [\n 2338816,\n 2338816,\n true\n ],\n \"log_length\": [\n 22,\n 22,\n true\n ],\n \"relations\": [\n 4761088,\n 4761088,\n true\n ],\n \"terms\": [\n 11430400,\n 11430400,\n true\n ],\n \"range_cells\": [\n 0,\n 0,\n true\n ]\n },\n \"m1b\": {\n \"columns_used\": [\n 1310720,\n 1310720,\n true\n ],\n \"columns_nonregion\": [\n 1074176,\n 1074176,\n true\n ],\n \"log_length\": [\n 21,\n 21,\n true\n ],\n \"relations\": [\n 2231808,\n 2231808,\n true\n ],\n \"terms\": [\n 5174272,\n 5174272,\n true\n ],\n \"range_cells\": [\n 67072,\n 67072,\n true\n ]\n }\n },\n \"pass_\": true,\n \"model_m1\": {\n \"arm\": \"m1\",\n \"D\": 128,\n \"T\": 4,\n \"o_private\": false,\n \"norm\": false,\n \"gate_columns\": 2338816,\n \"range_cells\": 0,\n \"lkp_keys\": 0,\n \"relations\": 4761088,\n \"terms\": 11430400,\n \"product_columns_per_state_cell_per_token\": 35.688,\n \"relations_per_state_cell_per_token\": 72.648,\n \"columns_used\": 2338816,\n \"layout\": 2338816,\n \"log_length\": 22\n },\n \"model_m1b\": {\n \"arm\": \"m1b\",\n \"D\": 128,\n \"T\": 4,\n \"o_private\": false,\n \"norm\": false,\n \"gate_columns\": 1074176,\n \"range_cells\": 67072,\n \"lkp_keys\": 0,\n \"relations\": 2231808,\n \"terms\": 5174272,\n \"product_columns_per_state_cell_per_token\": 17.414,\n \"relations_per_state_cell_per_token\": 34.055,\n \"columns_used\": 1310720,\n \"columns_nonregion\": 1074176,\n \"rng_rows\": 8192,\n \"log_length\": 21\n }\n },\n \"H2\": {\n \"armA_m1b_shape\": {\n \"arm\": \"A\",\n \"D\": 128,\n \"T\": 4,\n \"o_private\": false,\n \"norm\": false,\n \"gate_columns\": 1074176,\n \"range_cells\": 67072,\n \"lkp_keys\": 0,\n \"relations\": 2231808,\n \"terms\": 5174272,\n \"product_columns_per_state_cell_per_token\": 17.414,\n \"relations_per_state_cell_per_token\": 34.055,\n \"columns_used\": 1156096,\n \"columns_nonregion\": 1074176,\n \"rng_rows\": [\n 4096,\n 1024\n ],\n \"key_parts\": [],\n \"digit_entries\": 102400,\n \"log_length\": 21\n },\n \"armB_m1b_shape\": {\n \"arm\": \"B\",\n \"D\": 128,\n \"T\": 4,\n \"o_private\": false,\n \"norm\": false,\n \"gate_columns\": 263168,\n \"range_cells\": 67072,\n \"lkp_keys\": 67584,\n \"relations\": 609792,\n \"terms\": 1186816,\n \"product_columns_per_state_cell_per_token\": 6.07,\n \"relations_per_state_cell_per_token\": 9.305,\n \"columns_used\": 427008,\n \"columns_nonregion\": 263168,\n \"rng_rows\": [\n 4096,\n 1024\n ],\n \"key_parts\": [\n 65536,\n 16384\n ],\n \"digit_entries\": 102400,\n \"log_length\": 19\n },\n \"ratio_columns_per_cell\": 0.349,\n \"ratio_relations\": 0.273,\n \"ratio_terms\": 0.229,\n \"pass_\": true,\n \"note\": \"arm A at the M1b shape equals M1b (same lowering); arm B swaps exact bits for direct LKP keys\"\n },\n \"H3\": {\n \"armA_T16\": {\n \"arm\": \"A\",\n \"D\": 128,\n \"T\": 16,\n \"o_private\": true,\n \"norm\": true,\n \"gate_columns\": 4377056,\n \"range_cells\": 272384,\n \"lkp_keys\": 0,\n \"relations\": 9042944,\n \"terms\": 21110960,\n \"product_columns_per_state_cell_per_token\": 17.736,\n \"relations_per_state_cell_per_token\": 34.496,\n \"columns_used\": 4655584,\n \"columns_nonregion\": 4377056,\n \"rng_rows\": [\n 16384,\n 1024\n ],\n \"key_parts\": [],\n \"digit_entries\": 348160,\n \"log_length\": 23,\n \"prove_s_estimate\": null\n },\n \"armB_T16\": {\n \"arm\": \"B\",\n \"D\": 128,\n \"T\": 16,\n \"o_private\": true,\n \"norm\": true,\n \"gate_columns\": 1056800,\n \"range_cells\": 272384,\n \"lkp_keys\": 276688,\n \"relations\": 2402432,\n \"terms\": 4786704,\n \"product_columns_per_state_cell_per_token\": 6.126,\n \"relations_per_state_cell_per_token\": 9.165,\n \"columns_used\": 1613856,\n \"columns_nonregion\": 1056800,\n \"rng_rows\": [\n 16384,\n 1024\n ],\n \"key_parts\": [\n 262144,\n 16384\n ],\n \"digit_entries\": 348160,\n \"log_length\": 21,\n \"prove_s_estimate\": 39.2\n },\n \"max_T_log22\": {\n \"A\": 14,\n \"B\": 41\n },\n \"layer_extrapolation\": {\n \"A\": {\n \"relations_per_token_per_layer\": 27128832,\n \"columns_per_token_per_layer\": 13948320,\n \"columns_per_token_48_deltanet_layers\": 669519360\n },\n \"B\": {\n \"relations_per_token_per_layer\": 7207296,\n \"columns_per_token_per_layer\": 4817616,\n \"columns_per_token_48_deltanet_layers\": 231245568\n }\n },\n \"pass_\": true,\n \"note\": \"prove_s_estimate is linear in table length from #47/#63 points; indicative only. Layer extrapolation = 48 V heads x per-head-per-token; DeltaNet recurrence + gated norm only (no matmuls).\"\n },\n \"remainder_savings\": {\n \"armA_T16_columns\": 4649440,\n \"armB_T16_columns\": 1605872,\n \"relations_A\": 9042944,\n \"relations_B\": 2402432,\n \"terms_A\": 21110960,\n \"terms_B\": 4786704\n }\n}\n" + "seconds": 1.292, + "stdout": "{\n \"H1\": {\n \"checks\": {\n \"m1\": {\n \"columns_used\": [\n 2338816,\n 2338816,\n true\n ],\n \"log_length\": [\n 22,\n 22,\n true\n ],\n \"relations\": [\n 4761088,\n 4761088,\n true\n ],\n \"terms\": [\n 11430400,\n 11430400,\n true\n ],\n \"range_cells\": [\n 0,\n 0,\n true\n ]\n },\n \"m1b\": {\n \"columns_used\": [\n 1310720,\n 1310720,\n true\n ],\n \"columns_nonregion\": [\n 1074176,\n 1074176,\n true\n ],\n \"log_length\": [\n 21,\n 21,\n true\n ],\n \"relations\": [\n 2231808,\n 2231808,\n true\n ],\n \"terms\": [\n 5174272,\n 5174272,\n true\n ],\n \"range_cells\": [\n 67072,\n 67072,\n true\n ]\n }\n },\n \"pass_\": true,\n \"model_m1\": {\n \"arm\": \"m1\",\n \"D\": 128,\n \"T\": 4,\n \"o_witness\": false,\n \"norm\": false,\n \"gate_columns\": 2338816,\n \"range_cells\": 0,\n \"lkp_keys\": 0,\n \"relations\": 4761088,\n \"terms\": 11430400,\n \"product_columns_per_state_cell_per_token\": 35.688,\n \"relations_per_state_cell_per_token\": 72.648,\n \"columns_used\": 2338816,\n \"layout\": 2338816,\n \"log_length\": 22\n },\n \"model_m1b\": {\n \"arm\": \"m1b\",\n \"D\": 128,\n \"T\": 4,\n \"o_witness\": false,\n \"norm\": false,\n \"gate_columns\": 1074176,\n \"range_cells\": 67072,\n \"lkp_keys\": 0,\n \"relations\": 2231808,\n \"terms\": 5174272,\n \"product_columns_per_state_cell_per_token\": 17.414,\n \"relations_per_state_cell_per_token\": 34.055,\n \"columns_used\": 1310720,\n \"columns_nonregion\": 1074176,\n \"rng_rows\": 8192,\n \"log_length\": 21\n }\n },\n \"H2\": {\n \"armA_m1b_shape\": {\n \"arm\": \"A\",\n \"D\": 128,\n \"T\": 4,\n \"o_witness\": false,\n \"norm\": false,\n \"gate_columns\": 1074176,\n \"range_cells\": 67072,\n \"lkp_keys\": 0,\n \"relations\": 2231808,\n \"terms\": 5174272,\n \"product_columns_per_state_cell_per_token\": 17.414,\n \"relations_per_state_cell_per_token\": 34.055,\n \"columns_used\": 1156096,\n \"columns_nonregion\": 1074176,\n \"rng_rows\": [\n 4096,\n 1024\n ],\n \"key_parts\": [],\n \"digit_entries\": 102400,\n \"rng_multiplicity_places\": 155648,\n \"remainder_multiplicity_places\": 0,\n \"multiplicity_row_places\": 155648,\n \"log_length\": 21\n },\n \"armB_m1b_shape\": {\n \"arm\": \"B\",\n \"D\": 128,\n \"T\": 4,\n \"o_witness\": false,\n \"norm\": false,\n \"gate_columns\": 263168,\n \"range_cells\": 67072,\n \"lkp_keys\": 67584,\n \"relations\": 609792,\n \"terms\": 1186816,\n \"product_columns_per_state_cell_per_token\": 6.07,\n \"relations_per_state_cell_per_token\": 9.305,\n \"columns_used\": 427008,\n \"columns_nonregion\": 263168,\n \"rng_rows\": [\n 4096,\n 1024\n ],\n \"key_parts\": [\n 65536,\n 16384\n ],\n \"digit_entries\": 102400,\n \"rng_multiplicity_places\": 155648,\n \"remainder_multiplicity_places\": 69632,\n \"multiplicity_row_places\": 225280,\n \"log_length\": 19\n },\n \"ratio_columns_per_cell\": 0.349,\n \"ratio_relations\": 0.273,\n \"ratio_terms\": 0.229,\n \"pass_\": true,\n \"note\": \"arm A at the M1b shape equals M1b (same lowering); arm B swaps exact bits for direct LKP keys\"\n },\n \"H3\": {\n \"armA_T16\": {\n \"arm\": \"A\",\n \"D\": 128,\n \"T\": 16,\n \"o_witness\": true,\n \"norm\": true,\n \"gate_columns\": 4377056,\n \"range_cells\": 272384,\n \"lkp_keys\": 0,\n \"relations\": 9042944,\n \"terms\": 21110960,\n \"product_columns_per_state_cell_per_token\": 17.736,\n \"relations_per_state_cell_per_token\": 34.496,\n \"columns_used\": 4655584,\n \"columns_nonregion\": 4377056,\n \"rng_rows\": [\n 16384,\n 1024\n ],\n \"key_parts\": [],\n \"digit_entries\": 348160,\n \"rng_multiplicity_places\": 172032,\n \"remainder_multiplicity_places\": 0,\n \"multiplicity_row_places\": 172032,\n \"log_length\": 23,\n \"prove_s_estimate\": null\n },\n \"armB_T16\": {\n \"arm\": \"B\",\n \"D\": 128,\n \"T\": 16,\n \"o_witness\": true,\n \"norm\": true,\n \"gate_columns\": 1056800,\n \"range_cells\": 272384,\n \"lkp_keys\": 276688,\n \"relations\": 2402432,\n \"terms\": 4786704,\n \"product_columns_per_state_cell_per_token\": 6.126,\n \"relations_per_state_cell_per_token\": 9.165,\n \"columns_used\": 1613856,\n \"columns_nonregion\": 1056800,\n \"rng_rows\": [\n 16384,\n 1024\n ],\n \"key_parts\": [\n 262144,\n 16384\n ],\n \"digit_entries\": 348160,\n \"rng_multiplicity_places\": 172032,\n \"remainder_multiplicity_places\": 77824,\n \"multiplicity_row_places\": 249856,\n \"log_length\": 21,\n \"prove_s_estimate\": 39.2,\n \"single_part_layout\": {\n \"key_part\": 524288,\n \"columns_used\": 1859616,\n \"log_length\": 21\n }\n },\n \"max_T_log22\": {\n \"A\": 14,\n \"B\": 41\n },\n \"max_T_log22_without_relation_cap\": {\n \"A\": 14,\n \"B\": 41\n },\n \"layer_extrapolation\": {\n \"A\": {\n \"relations_per_token_per_layer\": 27128832,\n \"columns_per_token_per_layer\": 13948320,\n \"columns_per_token_48_deltanet_layers\": 669519360\n },\n \"B\": {\n \"relations_per_token_per_layer\": 7207296,\n \"columns_per_token_per_layer\": 4817616,\n \"columns_per_token_48_deltanet_layers\": 231245568\n }\n },\n \"fits_2_21_armB_T16\": true,\n \"pass_\": true,\n \"note\": \"prove_s_estimate is linear in table length from #47/#63 points; indicative only. Layer extrapolation = 48 V heads x per-head-per-token; DeltaNet recurrence + gated norm only (no matmuls).\"\n },\n \"remainder_savings\": {\n \"armA_T16_columns\": 4649440,\n \"armB_T16_columns\": 1605872,\n \"relations_A\": 9042944,\n \"relations_B\": 2402432,\n \"terms_A\": 21110960,\n \"terms_B\": 4786704\n }\n}\n" }, "ranges": { "exit": 0, @@ -342,5 +354,13 @@ "exit": 0, "seconds": 14.606, "stdout": "}\n{\"dist\": \"R1\", \"seed\": 5, \"all_fit\": true, \"S\": 5098, \"delta\": 13253, \"o\": 349, \"w\": 2233, \"sigma\": 788, \"Rc\": 1145324612, \"div_S\": 0.002, \"div_y\": 0.5294}\n{\"dist\": \"R2\", \"seed\": 1, \"all_fit\": true, \"S\": 11188, \"delta\": 25142, \"o\": 928, \"w\": 1571, \"sigma\": 3624, \"Rc\": 313787565, \"div_S\": 0.0081, \"div_y\": 0.1251}\n{\"dist\": \"R2\", \"seed\": 2, \"all_fit\": true, \"S\": 11758, \"delta\": 28507, \"o\": 825, \"w\": 1689, \"sigma\": 2790, \"Rc\": 255462738, \"div_S\": 0.0048, \"div_y\": 0.1207}\n{\"dist\": \"R2\", \"seed\": 3, \"all_fit\": true, \"S\": 12287, \"delta\": 25702, \"o\": 888, \"w\": 1589, \"sigma\": 3527, \"Rc\": 432197966, \"div_S\": 0.0074, \"div_y\": 0.1141}\n{\"dist\": \"R2\", \"seed\": 4, \"all_fit\": true, \"S\": 14726, \"delta\": 24582, \"o\": 1195, \"w\": 1434, \"sigma\": 4659, \"Rc\": 173534032, \"div_S\": 0.0168, \"div_y\": 0.1266}\n{\"dist\": \"R2\", \"seed\": 5, \"all_fit\": true, \"S\": 10419, \"delta\": 26959, \"o\": 746, \"w\": 1993, \"sigma\": 1925, \"Rc\": 648296950, \"div_S\": 0.002, \"div_y\": 0.2653}\n{\"dist\": \"R3\", \"seed\": 1, \"all_fit\": true, \"S\": 74704, \"delta\": 237244, \"o\": 4257, \"w\": 3007, \"sigma\": 6232, \"Rc\": 98876944, \"div_S\": 0.0116, \"div_y\": 0.1067}\n{\"dist\": \"R3\", \"seed\": 2, \"all_fit\": true, \"S\": 200463, \"delta\": 582625, \"o\": 8271, \"w\": 3928, \"sigma\": 8875, \"Rc\": 1347440720, \"div_S\": 0.026, \"div_y\": 0.5988}\n{\"dist\": \"R3\", \"seed\": 3, \"all_fit\": true, \"S\": 104250, \"delta\": 273909, \"o\": 7204, \"w\": 3602, \"sigma\": 8191, \"Rc\": 160185260, \"div_S\": 0.014, \"div_y\": 0.1117}\n{\"dist\": \"R3\", \"seed\": 4, \"all_fit\": true, \"S\": 77280, \"delta\": 187351, \"o\": 4159, \"w\": 2626, \"sigma\": 7425, \"Rc\": 100614167, \"div_S\": 0.0223, \"div_y\": 0.1019}\n{\"dist\": \"R3\", \"seed\": 5, \"all_fit\": true, \"S\": 100699, \"delta\": 253724, \"o\": 4045, \"w\": 3879, \"sigma\": 4536, \"Rc\": 512831915, \"div_S\": 0.0092, \"div_y\": 0.3549}\n{\"dist\": \"R4\", \"seed\": 1, \"all_fit\": true, \"S\": 15048, \"delta\": 23158, \"o\": 2407, \"w\": 1914, \"sigma\": 5415, \"Rc\": 313787565, \"div_S\": 0.0086, \"div_y\": 0.0935}\n{\"dist\": \"R4\", \"seed\": 2, \"all_fit\": true, \"S\": 13066, \"delta\": 19867, \"o\": 1544, \"w\": 1994, \"sigma\": 4195, \"Rc\": 255462738, \"div_S\": 0.0045, \"div_y\": 0.1556}\n{\"dist\": \"R4\", \"seed\": 3, \"all_fit\": true, \"S\": 17887, \"delta\": 21502, \"o\": 1987, \"w\": 2276, \"sigma\": 4684, \"Rc\": 432197966, \"div_S\": 0.0081, \"div_y\": 0.1039}\n{\"dist\": \"R4\", \"seed\": 4, \"all_fit\": true, \"S\": 15666, \"delta\": 20861, \"o\": 2171, \"w\": 1959, \"sigma\": 6339, \"Rc\": 139958201, \"div_S\": 0.0167, \"div_y\": 0.1008}\n{\"dist\": \"R4\", \"seed\": 5, \"all_fit\": true, \"S\": 11611, \"delta\": 27684, \"o\": 1208, \"w\": 2375, \"sigma\": 3031, \"Rc\": 487372175, \"div_S\": 0.0023, \"div_y\": 0.25}\n{\"kernel\": \"delta\", \"q_unscaled\": false, \"steps\": 1024, \"n\": 20, \"fit\": 20, \"max\": {\"S\": 200463, \"M\": 137315, \"delta\": 582625, \"delta_lo\": 262143, \"delta_hi\": 2, \"o\": 8271, \"w\": 3928, \"v\": 1022655, \"k_hat\": 2743, \"q_hat\": 260, \"xin\": 1022655, \"y\": 98012, \"cg\": 227005, \"sigma\": 8875, \"e1\": 13545, \"e2\": 16637, \"Rc\": 1347440720, \"rr\": 6759, \"gap\": 7166, \"Vx\": 78764591}, \"worst_div_S\": 0.02597577748053581, \"worst_div_y\": 0.5987994346756061, \"seconds\": 14.5}\n" + }, + "hostile_ubsan": { + "exit": 0, + "seconds": 222.579, + "as_expected": 41, + "total": 41, + "ubsan_reports": 0, + "stderr": "" } } diff --git a/research/operators/qwen_deltanet_m2/head_reference.py b/research/operators/qwen_deltanet_m2/head_reference.py index 7abd09ba..fe003935 100644 --- a/research/operators/qwen_deltanet_m2/head_reference.py +++ b/research/operators/qwen_deltanet_m2/head_reference.py @@ -3,7 +3,7 @@ outputs to the gated-norm output (the M2 statement of DESIGN.md), plus a float64 twin. Chosen research contract, not the publisher's numerics. The public-only stage (conv1d, SiLU, q/k L2 -norm, q scale, decay and beta) is what the M2 verifier RECOMPUTES from public integers; the private +norm, q scale, decay and beta) is what the M2 verifier RECOMPUTES from public integers; the witness stage (recurrence, o, gated RMSNorm) is what the M2 relation proves. This file measures the ranges the DESIGN's cell classes must hold (PREREG H4). diff --git a/research/operators/qwen_deltanet_m2/m1b_builder.h b/research/operators/qwen_deltanet_m2/m1b_builder.h index fb1c91e1..934c9c0f 100644 --- a/research/operators/qwen_deltanet_m2/m1b_builder.h +++ b/research/operators/qwen_deltanet_m2/m1b_builder.h @@ -1,9 +1,14 @@ // SPDX-License-Identifier: Apache-2.0 // Research-only. The M1b statement builder and prover (research/operators/qwen_deltanet_lookup/ // test.cpp, PR #63), split out as a header for the M2 design prototype admit_proto.cpp. Unchanged -// except: a relation-only switch (g_relonly) that turns off the honest-witness assertions when the -// verifier builds the relations (the column allocation and the relation list do not depend on it), -// and indices of the last token's output floors for the R9 attack. +// except: a relation-only mode for the verifier (build(z, true)), and indices of the last token's +// output floors for the R9 attack. +// Relation-only mode (post-review, #66 should-fix 3): the verifier's build computes NO witness. +// Every witness value (S, P_j, a P, M, delta, S', the o sums and all remainders) is replaced by 0, +// so no signed product of in-class but inconsistent public values can overflow, and no check() can +// end the process. The column allocation and the relation list (terms, coefficients, targets) do +// not depend on witness values, so they are identical to the prover's. Targets read only public +// integers: S0, a, beta, k, v, q, o, S_T. The largest is 2^24 |v| < 2^55 for in-class v. #pragma once #include #include @@ -77,9 +82,9 @@ struct Built { u64 rc_base = 0, rc_rows = 0; // region: product columns [rc_base, rc_base + 16 rc_rows) std::vector rcell_state, rcell_M, rcell_dlo, rcell_dhi; // [t] -> range-cell index of S_{t+1}[0,0], M[0], delta_lo[0], delta_hi[0] }; -static bool g_relonly = false; // relation-only build (verifier side): witness checks off, columns allocated identically -static Built build(const Fixture &z) { +static Built build(const Fixture &z, const bool relonly = false) { Built b; const int D = z.D, FB = z.F; const i64 one = i64(1) << FB; + const bool g_relonly = relonly; // witness checks off and witness values zero (see the header) auto col = [&](i64 x, i64 y) { b.cols.push_back({fi(x), fi(y), fi(x) * fi(y)}); return u64(b.cols.size() - 1); }; auto rel = [&](std::vector t, i64 target) { b.rel.push_back({std::move(t), fi(target)}); return u64(b.rel.size() - 1); }; // Unsigned width-bit decomposition of n (honest n must fit; builder asserts). Returns the @@ -99,7 +104,7 @@ static Built build(const Fixture &z) { // range cell k is product column base + k, laid for RNG as cell (k mod rows, k div rows) of a // rows x 16 matrix. Until then a term names it by RTAG + k. auto rcell = [&](i64 x) { check(g_relonly || (x > -BOUND && x < BOUND), "honest range cell outside its contract"); b.rcv.push_back(x); b.rc.push_back(RTAG + b.rcv.size() - 1); return u64(b.rcv.size() - 1); }; - std::vector S = z.S0; std::vector canon(D * D); + std::vector S = relonly ? std::vector(size_t(D) * D, 0) : z.S0; std::vector canon(D * D); b.state_col.resize(z.T); b.state_rbits.resize(z.T); b.state_qbits.resize(z.T); b.rcell_state.resize(z.T); b.rcell_M.resize(z.T); b.rcell_dlo.resize(z.T); b.rcell_dhi.resize(z.T); b.rel_a.resize(z.T); b.rel_beta.resize(z.T); b.rel_k0.resize(z.T); b.rel_q0.resize(z.T); for (int t = 0; t < z.T; t++) { @@ -109,7 +114,7 @@ static Built build(const Fixture &z) { for (int i = 0; i < D; i++) for (int j = 0; j < D; j++) { const int c = i * D + j; g1[c] = col(S[c], a); - if (t == 0) { canon[c] = g1[c]; u64 r = rel({{0, g1[c], F{1}}}, S[c]); if (c == 0) b.rel_S00 = r; } + if (t == 0) { canon[c] = g1[c]; u64 r = rel({{0, g1[c], F{1}}}, z.S0[c]); if (c == 0) b.rel_S00 = r; } else rel({{0, g1[c], F{1}}, {0, canon[c], fi(-1)}}, 0); b.rel_a[t].push_back(rel({{1, g1[c], F{1}}}, a)); g2[c] = col(S[c], z.k[t][i]); @@ -120,7 +125,7 @@ static Built build(const Fixture &z) { std::vector M(D), delta(D); std::vector dcell(D); for (int j = 0; j < D; j++) { i64 Pj = 0; std::vector pt; - for (int i = 0; i < D; i++) { Pj += S[i * D + j] * z.k[t][i]; pt.push_back({2, g2[i * D + j], fi(-1)}); } + for (int i = 0; i < D; i++) { if (!relonly) Pj += S[i * D + j] * z.k[t][i]; pt.push_back({2, g2[i * D + j], fi(-1)}); } u64 g5 = col(Pj, a); pt.insert(pt.begin(), AffineTerm{0, g5, F{1}}); rel(pt, 0); b.rel_a[t].push_back(rel({{1, g5, F{1}}}, a)); const i64 aP = Pj * a; M[j] = fdiv(aP, i64(1) << (2 * FB)); const i64 rM = aP - M[j] * (i64(1) << (2 * FB)); @@ -136,7 +141,7 @@ static Built build(const Fixture &z) { u64 rr = rel({{0, b.rc[mc], F{1}}, {0, g6, F{1}}}, z.v[t][j]); if (j == 0) { b.rel_v0_floor.push_back(rf); b.rel_v0_range.push_back(rr); } b.rel_beta[t].push_back(rel({{1, g6, F{1}}}, beta)); - const i64 num = (z.v[t][j] - M[j]) * beta; delta[j] = fdiv(num, one); const i64 rd = num - delta[j] * one; + const i64 num = relonly ? 0 : (z.v[t][j] - M[j]) * beta; delta[j] = fdiv(num, one); const i64 rd = num - delta[j] * one; const i64 dhi = fdiv(delta[j], BOUND), dlo = delta[j] - dhi * BOUND; // two limbs, dlo in [0, 2^18) // delta lives in g3[0][j].b; created below, so reserve now dcell[j] = col(z.k[t][0], delta[j]); @@ -158,7 +163,7 @@ static Built build(const Fixture &z) { rel({{0, g3, F{1}}}, z.k[t][i]); rel({{1, g3, F{1}}, {1, dcell[j], fi(-1)}}, 0); } - const i64 num = a * S[c] + z.k[t][i] * delta[j]; Sn[c] = fdiv(num, one); const i64 r = num - Sn[c] * one; + const i64 num = relonly ? 0 : a * S[c] + z.k[t][i] * delta[j]; Sn[c] = fdiv(num, one); const i64 r = num - Sn[c] * one; g4[c] = col(Sn[c], z.q[t][i]); u64 rq = rel({{1, g4[c], F{1}}}, z.q[t][i]); if (i == 0) b.rel_q0[t].push_back(rq); @@ -170,7 +175,7 @@ static Built build(const Fixture &z) { std::vector o(D); for (int j = 0; j < D; j++) { i64 sum = 0; std::vector ot; - for (int i = 0; i < D; i++) { sum += Sn[i * D + j] * z.q[t][i]; ot.push_back({2, g4[i * D + j], fi(-1)}); } + for (int i = 0; i < D; i++) { if (!relonly) sum += Sn[i * D + j] * z.q[t][i]; ot.push_back({2, g4[i * D + j], fi(-1)}); } o[j] = fdiv(sum, one); auto ob = bits(sum - o[j] * one, FB, ot, 1); if (t == z.T - 1) { b.o_rbits_last.push_back(ob); b.o_rbit0_last.push_back(ob[0]); b.o_rem_last.push_back(sum - o[j] * one); } u64 r = rel(ot, -one * z.o[t][j]); if (j == 0) b.rel_o0.push_back(r); diff --git a/research/operators/qwen_deltanet_m2/record.py b/research/operators/qwen_deltanet_m2/record.py index dea1d91b..530c84f4 100644 --- a/research/operators/qwen_deltanet_m2/record.py +++ b/research/operators/qwen_deltanet_m2/record.py @@ -1,10 +1,11 @@ #!/usr/bin/env python3 """CPU receipt for the Qwen DeltaNet M2 design prototypes (experiments/20261105-32-qwen-deltanet-m2-design). -Usage: record.py [build] [honest] [hostile] [model] [ranges] +Usage: record.py [build] [honest] [hostile] [ubsan] [model] [ranges] build fixtures (M1 reference.py, seeds 1..10) + compile admit_proto.cpp honest H5a: one honest proof per seed through verify_from_words (pinned to 6 cores) hostile H5b/c: hostile public encodings + R9 on seed 1 + ubsan post-review: the hostile suite again, built with -fsanitize=undefined (no recover) model H1-H3: count_model.py ranges H4: head_reference.py range suite Writes evidence.json here and raw lines to the experiment's runs/. @@ -75,6 +76,19 @@ def run_bin(fx, mode): ev['hostile'] = dict(exit=c['exit'], seconds=c['seconds'], as_expected=sum(l['as_expected'] for l in hs), total=len(hs), by_class=by, stderr=c['stderr']) print(json.dumps(ev['hostile']), flush=True); save() +if 'ubsan' in only: + ub = work / 'admit_proto_ubsan.bin' + c = sh(['g++', '-std=c++17', '-O2', '-fno-ipa-modref', '-fsanitize=undefined', '-fno-sanitize-recover=undefined', '-DBLK_FIELD=2', '-DBLK_HASH=1', + '-I', 'blocks', f'{rel}/admit_proto.cpp', '-o', str(ub.relative_to(root))]) + assert c['exit'] == 0, c['stderr'] + c = sh(['taskset', '-c', CORES, str(ub.relative_to(root)), str((work / 'seed1.bin').relative_to(root)), 'hostile']) + (exp / 'h5-hostile-seed1-ubsan.jsonl').write_text(c['stdout']) + lines = [json.loads(l) for l in c['stdout'].splitlines() if l.startswith('{')] + hs = [l for l in lines if l.get('mode') == 'hostile'] + ev['hostile_ubsan'] = dict(exit=c['exit'], seconds=c['seconds'], as_expected=sum(l['as_expected'] for l in hs), total=len(hs), + ubsan_reports=c['stderr'].count('runtime error'), stderr=c['stderr']) + print(json.dumps({k: v for k, v in ev['hostile_ubsan'].items() if k != 'stderr'}), flush=True); save() + if not only or 'model' in only: c = sh([sys.executable, f'{rel}/count_model.py', '--json', str((exp / 'model.json').relative_to(root))]) ev['model'] = dict(exit=c['exit'], seconds=c['seconds'], stdout=c['stdout'][-6000:]); print(c['stdout']); save() From 8ec7c79969ca4ca6044dc4c8b79fe4385910fe8c Mon Sep 17 00:00:00 2001 From: wakesync <60821182+wakesync@users.noreply.github.com> Date: Thu, 5 Nov 2026 17:27:39 +0000 Subject: [PATCH 6/6] experiments: Qwen M2 design, second review: disclose D5 deviation, checked i64 arithmetic on the verifier path (Part of #32) - RESULTS: PREREG D5 registered a recombination relation for 24-bit arm-B remainders; DESIGN/count_model fold both limbs into the floor relation. Disclosed: +512 relations at the M1b shape, B/A stays 0.273 - m1b_builder.h: every i64 mul/add/sub in build() checked; overflow -> Built::overflow -> derive reject (UB at aP = Pj * a found by UBSan in review 2) - hostile suite: review-2 all-extremes statement through the witness-free entry (proof reject) and the checked witness path (derive reject); 44/44, 0 UBSan reports - DESIGN 4.5/7: no unchecked arithmetic on public inputs + UBSan run of the hostile suite is an M2 build requirement; listed as open risk - H5 rerun: honest 10/10, hostile 44/44 --- .../DESIGN.md | 11 +++- .../ENV.json | 2 +- .../RESULTS.md | 27 ++++++--- .../runs/h5-honest.jsonl | 20 +++---- .../runs/h5-hostile-seed1-ubsan.jsonl | 8 ++- .../runs/h5-hostile-seed1.jsonl | 8 ++- research/operators/qwen_deltanet_m2/README.md | 2 +- .../qwen_deltanet_m2/admit_proto.cpp | 24 +++++++- .../operators/qwen_deltanet_m2/evidence.json | 56 +++++++++---------- .../operators/qwen_deltanet_m2/m1b_builder.h | 43 +++++++++----- 10 files changed, 129 insertions(+), 72 deletions(-) diff --git a/experiments/20261105-32-qwen-deltanet-m2-design/DESIGN.md b/experiments/20261105-32-qwen-deltanet-m2-design/DESIGN.md index 734a87a9..e59c6ca4 100644 --- a/experiments/20261105-32-qwen-deltanet-m2-design/DESIGN.md +++ b/experiments/20261105-32-qwen-deltanet-m2-design/DESIGN.md @@ -141,7 +141,7 @@ The M1b result showed remainder bits are 12 of the 17.4 product columns per stat **Arm B: lookup-exact remainders.** - Each 12-bit remainder (F = 12) is ONE product cell [r, 0, 0] in a key region of row a. It is a direct key of a second LKP instance into the public range table {0, .., 2^12 - 1}: LkpTablePart with empty rows, one function, unshifted. -- A 24-bit remainder (the M floor, 2^2F) is two such cells, r = lo + 2^12 hi. Both terms go directly into the floor relation, so no extra relation is needed. +- A 24-bit remainder (the M floor, 2^2F) is two such cells, r = lo + 2^12 hi. Both terms go directly into the floor relation, so no extra relation is needed. (This departs from PREREG D5, which registered two limbs plus one recombination relation. The deviation is disclosed in RESULTS.md and is worth 512 relations at the M1b shape. No ratio or verdict moves.) - An exact interval [0, 2^12) on each limb gives r in [0, 2^24) exactly, so floor uniqueness is unchanged from M1b. This is an exact range proof, not RNG's approximate one, so the floor-uniqueness argument of M1b goes through as is. The keys come in parts (LkpPublic::parts, allowed for one unshifted function). The key region is laid like RNG's cells, and LKP's key claim becomes a laid_form on message 0, as `rng_key_row` does for the digits. @@ -211,6 +211,13 @@ The H4 range suite found no exact limb outside its interval and no negative exac Every failure is a returned verdict (decode, derive or proof). Nothing on the verifier path calls exit() or a BLK_REQUIRE that a statement can reach: the entry checks lookup_size()'s guards on the derived statement first and returns "derive" if one would fire. (Post-review, #66 should-fix 3: the first prototype ran the full witness path with its checks off, so in-class but inconsistent inputs could reach i64 overflow, and a failed sizing called exit(1). The prototype now uses a witness-free derivation and checks that it gives the prover's relation list exactly.) +**Build requirement: no unchecked arithmetic on public inputs, plus a sanitizer run.** A second review of #66 ran UBSan on the first prototype. On an in-class statement with every public value at its class extreme (S0 = 2^18 - 1, k = a = 2^12, v = o = 2^31 - 1), the verifier's derivation overflowed signed i64 at `aP = Pj * a` in m1b_builder.h. No soundness break was seen: the relation targets do not read the overflowed witness, and at -O2 the statement was rejected at the proof stage. But it was undefined behaviour on attacker-chosen input, inside the verifier. The M2 verifier must therefore: +- compute no witness value from public inputs (the derivation is witness-free); +- do every integer operation that touches a public input with checked arithmetic (`__builtin_*_overflow` or a 128-bit type with an explicit bound), and turn an overflow into a reject, never a wrap or an exit; +- run the full hostile suite, including the all-extremes statements, under `-fsanitize=undefined -fno-sanitize-recover` in CI, with 0 reports as the pass condition. + +The prototype now does all three. `build()` routes every i64 product, sum and difference through checked helpers that set an overflow flag, the entry rejects at "derive" when the flag is set, and `record.py ubsan` runs the suite under UBSan. The review's statement is in the hostile suite twice. Through the witness-free entry it passes derive and is rejected at the proof stage. Through the old witness path, kept as a checked control, it is rejected at derive. + There is no `admissible()` that a caller runs separately, and no entry point that accepts caller-built relations or a caller-built `LookupRun`. The low-level `lookup_verify(run, ...)` stays a library internal. **The M2 build must not export it as a verifier.** The prototype measured this on the M1b statement (RESULTS.md H5): @@ -255,6 +262,6 @@ The build lane must keep these, and register numbers in its own PREREG before co - one remainder key off-table (arm B: key 2^12, which only a hostile LKP prover can send) - the limb re-split - **H3 range:** H4 of this lane re-run on the build's reference, 20/20. -- **H4 admission:** this lane's H5 suite on the M2 statement words, plus a huge element in xq, ar, z and the conv history H, and the R9 analogue on y. All rejected by `verify_m2` alone. Also: H changed in class (must fail at the proof verifier, since the conv recompute reads it), and the derive-overflow cases (every class at its extreme; every word random in class), which must return a verdict and never exit or overflow (run under UBSan). +- **H4 admission:** this lane's H5 suite on the M2 statement words, plus a huge element in xq, ar, z and the conv history H, and the R9 analogue on y. All rejected by `verify_m2` alone. Also: H changed in class (must fail at the proof verifier, since the conv recompute reads it), and the derive-overflow cases (every class at its extreme; every word random in class), which must return a verdict and never exit or overflow. The whole suite runs under `-fsanitize=undefined -fno-sanitize-recover` with 0 reports (section 4.5: no unchecked arithmetic on public inputs). - **H2 hostile, two lookups:** the challenge-replay case of section 4.3 (the digit lookup's alpha and sumcheck challenges reused in the remainder lookup; the two instances swapped), rejected. - **Kill:** no honest T = 16 arm B proof in 6 h, then fall back to arm A at T = 8 and report. diff --git a/experiments/20261105-32-qwen-deltanet-m2-design/ENV.json b/experiments/20261105-32-qwen-deltanet-m2-design/ENV.json index 013070a2..803168f2 100644 --- a/experiments/20261105-32-qwen-deltanet-m2-design/ENV.json +++ b/experiments/20261105-32-qwen-deltanet-m2-design/ENV.json @@ -20,6 +20,6 @@ }, "proof_parameters": "as #63: Goldilocks, target 10 bits, 6 work bits on QUERY, ToyCoins(100); log-length 21, stages {1,1,21,21},{0,0,21,21}; RNG digits 12+11 bits (2D = 2^23), 16 columns, N = 10", "seeds": {"h5_honest": [1,2,3,4,5,6,7,8,9,10], "h5_hostile": [1], "h4_ranges": {"dists": ["R1","R2","R3","R4"], "seeds": [1,2,3,4,5], "steps": 1024, "generator_seed": "1000 + seed"}}, - "runs": {"h5_honest": 10, "h5_hostile": 41, "h5_hostile_registered_run": 34, "h5_hostile_ubsan": 41, "h4_range_runs": 20, "h4_secondary_runs": 40, "model": 1}, + "runs": {"h5_honest": 10, "h5_hostile": 44, "h5_hostile_registered_run": 34, "h5_hostile_ubsan": 44, "h4_range_runs": 20, "h4_secondary_runs": 40, "model": 1}, "raw": ["runs/h5-honest.jsonl", "runs/h5-hostile-seed1.jsonl", "runs/h5-hostile-seed1-ubsan.jsonl", "runs/model.json", "runs/ranges-head.json", "runs/ranges-head-q-unscaled.json", "runs/ranges-head-delta-kernel.json", "../../research/operators/qwen_deltanet_m2/evidence.json"] } diff --git a/experiments/20261105-32-qwen-deltanet-m2-design/RESULTS.md b/experiments/20261105-32-qwen-deltanet-m2-design/RESULTS.md index dcbb628f..72767c16 100644 --- a/experiments/20261105-32-qwen-deltanet-m2-design/RESULTS.md +++ b/experiments/20261105-32-qwen-deltanet-m2-design/RESULTS.md @@ -170,25 +170,29 @@ The row bounds the table length but does not bind at any shape here. Counts afte **Derive step (should-fix 3).** The verifier's derivation is now witness-free (`build(z, true)` in m1b_builder.h). It reads only decoded public integers, so no in-class statement can make it overflow. Every failure, including sizing, is a returned verdict ("derive"), not exit(1). In honest mode the prototype checks that the witness-free derivation gives the prover's relation list exactly (columns, terms, coefficients, targets). New hostile cases: - **derive-overflow**: every class at its extreme (|S0| = 2^18 - 1 alternating, k = q = a = beta = 2^F, |v| = 2^31 - 1 alternating, o = 2^31 - 1). A checked replay of the OLD witness path on this statement overflows i64 first at token 2. New path: returns a verdict, rejected by the proof verifier. - **derive-random**: every word uniform in its class (fixed seed). Old path: no overflow on this draw. New path: rejected by the proof verifier. -- The whole hostile suite was also run under `-fsanitize=undefined -fno-sanitize-recover`: 41/41 as expected, 0 UBSan reports, exit 0 (`runs/h5-hostile-seed1-ubsan.jsonl`). +- **Checked arithmetic (second review).** A second independent review ran UBSan on the first prototype. It found signed i64 overflow at `aP = Pj * a` (m1b_builder.h) on an in-class statement with every value at its extreme, all one sign: S0 = 2^18 - 1, k = a = 2^12, v = o = 2^31 - 1. Every i64 product, sum and difference in `build()` now goes through checked helpers. An overflow sets a flag, and the verifier rejects at "derive" instead of wrapping. The review's statement is now in the suite twice: + - through the witness-free entry: no overflow, rejected by the proof verifier; + - through the old witness path, kept as a checked control: the overflow is caught (first at token 2) and the statement is rejected at derive. + The alternating-sign extremes statement above was also added through the checked witness path, and is rejected at derive. +- The whole hostile suite was also run under `-fsanitize=undefined -fno-sanitize-recover`: **44/44** as expected, **0 UBSan reports**, exit 0 (`runs/h5-hostile-seed1-ubsan.jsonl`, `record.py ubsan`). **H5 rerun on the new code** (seeds 1..10, pinned to 6 cores, same loaded VM): | metric | n | median | SD | min | max | |---|---|---|---|---|---| | accepted | 10 | **10/10** | | | | -| derive relations + size (s) | 10 | 0.227 | 0.009 | 0.212 | 0.236 | -| proof verify (s) | 10 | 7.79 | 0.23 | 7.51 | 8.30 | -| prove (s) | 10 | 40.42 | 0.88 | 39.38 | 42.16 | +| derive relations + size (s) | 10 | 0.222 | 0.013 | 0.207 | 0.249 | +| proof verify (s) | 10 | 7.75 | 0.13 | 7.51 | 7.85 | +| prove (s) | 10 | 40.74 | 0.42 | 39.95 | 41.48 | | proof bytes | 10 | 25,411,392 | 313 | 25,410,848 | 25,411,968 | -| peak RSS (KB) | 10 | 4,435,606 | 25 | 4,435,528 | 4,435,608 | +| peak RSS (KB) | 10 | 4,435,608 | 46 | 4,435,480 | 4,435,608 | -Proof bytes are identical to the first run, seed by seed. The derivation is about 0.05 s slower than the first prototype's (0.182 s). The review expected witness-free to be faster. It is not, because the cost is relation and column allocation, which is unchanged, and the box was more loaded (verify and prove medians also rose about 2%). This is indicative only. +These are the numbers with checked arithmetic. The intermediate pass, before checked arithmetic, gave derive 0.227 s, verify 7.79 s and prove 40.42 s. Proof bytes are identical to the first run, seed by seed. The derivation is about 0.04 s slower than the first prototype's (0.182 s). The review expected witness-free to be faster. It is not, because the cost is relation and column allocation, which is unchanged, and the box was more loaded (verify and prove medians also rose about 2%). This is indicative only. -Hostile, seed 1: **41/41 at the expected stage** (was 34/34): +Hostile, seed 1: **44/44 at the expected stage** (was 34/34): - the original 34, unchanged verdicts; - conv history: a huge element, the first value outside the class on both sides, and a non-canonical word (4 cases), all rejected at decode; -- derive-overflow and derive-random (2), rejected by the proof verifier, no exit, no UB; +- derive-overflow (4) and derive-random (1): the witness-free entry rejects at the proof verifier; the checked witness-path control rejects at derive; no exit, no UB; - history control (1): H changed in class is ACCEPTED here, as expected, because the M1b relations read no history. That is a control, not a defence. In M2 the conv recompute reads H. The "H changed in class must fail at the proof verifier" case is in the build's suite. ## Kill criterion @@ -200,15 +204,20 @@ Not triggered. The lane started at 14:33Z. H5 honest accept came at about 15:10Z - **H1.** The registered comparison quantity for M1b "columns used" was 1,141,248 in the PREREG text, which is non-region plus range cells. The model matches both the measured laid-out length 1,310,720 and the non-region count 1,074,176; 1,141,248 = 1,074,176 + 67,072 also holds. No threshold moves. - **H5.** The class-bound test was registered as "bound + 1 for one-sided". It ran both sides for every class (16 cases instead of 8), which is stricter. - **H4.** The PREREG listed the float divergence of y as a metric without a threshold. It is reported, and it is large (see H4). The q-unscaled and delta-kernel runs are secondary and unregistered. +- **D5, 24-bit remainders in arm B (second review).** PREREG D5 registered a 24-bit (2F) remainder as two 12-bit key limbs **plus one recombination relation**. DESIGN.md 4.3 and count_model.py instead put both limbs directly into the floor relation, with coefficients 1 and 2^12, and have no recombination relation. The two are equivalent: the floor relation reads lo + 2^12 hi either way. The direct form saves one relation per 24-bit remainder. This was not disclosed in the first version of this file. Cost of the registered form: + - M1b shape (T = 4): one M floor per value column per token, so +512 relations (609,792 to 610,304). Relations B / A stay **0.273** (0.2735), and columns are unchanged (0.349). + - M2 head (T = 16): the M floor and the norm's w floor per j per token (+4,096), plus the norm's 24-bit scalars if recombined too. That is under 0.3% of 2.40M relations, the ratio stays 0.266, and the table stays 2^21. + - No threshold or verdict moves. The build may use either form, and should state which. - **H3 (post-review).** The largest-T loop in count_model.py also required relations <= 2^23. That is lookup_size()'s own guard, but it was not in the PREREG's metric text ("the largest T that fits log-length 22"). It does not bind: without it the largest T is still 14 (A) and 41 (B), now also reported in model.json. - **H3 (post-review).** The count model first omitted the multiplicity row of the second (remainder) lookup. It now sizes it. No count, log-length or verdict changes (see "Post-review corrections"). -- **H5 (post-review).** The prototype was changed after the registered runs (conv history words, witness-free derivation, verdicts instead of exits, 7 more hostile cases). H5 was rerun on it and passes the same registered thresholds. The registered results.yaml metrics still point at e08e5ce2, the commit of the original receipts. +- **H5 (post-review).** The prototype was changed after the registered runs (conv history words, witness-free derivation, verdicts instead of exits, checked i64 arithmetic, 10 more hostile cases). H5 was rerun on it and passes the same registered thresholds. The registered results.yaml metrics still point at e08e5ce2, the commit of the original receipts. - No amendments to PREREG.md. ## Open risks - **M2 is redundant as a standalone proof** (DESIGN.md section 0). All its inputs are public. Its value is as a rehearsal for M3, and it de-risks none of conv1d, the gate tables or the q/k norm. - **No M2 proof exists.** Arm B needs a second LKP instance whose keys are product-oracle cells. LKP supports parts and range tables, and RNG already composes a lookup whose key claim is a laid form on a sent message. But a lookup keyed directly on message-0 cells has not been built in this repo, and the two instances' transcript separation (DESIGN.md section 4.3) is a requirement on that build, untested here. That is the build lane's main risk. +- **Unchecked arithmetic on public inputs (second review).** The first prototype's verifier derivation overflowed signed i64 (UB) at `aP = Pj * a` on an in-class all-extremes statement. No soundness break was seen. It is now fixed in the prototype: the derivation is witness-free, every i64 operation in `build()` is checked and rejects at "derive" on overflow, and the hostile suite is clean under UBSan. For the M2 build this is a requirement (DESIGN.md 4.5): no unchecked arithmetic on public inputs, and a UBSan run of the hostile suite. It stays an open risk until the build's verifier passes that run. - **Verifier-recomputed nonlinearities must be bit-exact** across prover and verifier. The prototype uses float64 numpy at table points. The build needs pinned public tables (DESIGN.md section 3). - **y fidelity is poor at F = 12** (worst 1.2 absolute) because o is small in integer units. This is a fidelity issue for M0/#8, not soundness, but it argues for a finer o scale before any fidelity claim. - **The layer extrapolation is 48x one head.** Per-token cost for 48 layers (231M product cells, arm B) means M3 needs chunked state commitment. M2 does not address that. diff --git a/experiments/20261105-32-qwen-deltanet-m2-design/runs/h5-honest.jsonl b/experiments/20261105-32-qwen-deltanet-m2-design/runs/h5-honest.jsonl index c77e0cad..a63d90d0 100644 --- a/experiments/20261105-32-qwen-deltanet-m2-design/runs/h5-honest.jsonl +++ b/experiments/20261105-32-qwen-deltanet-m2-design/runs/h5-honest.jsonl @@ -1,10 +1,10 @@ -{"seed": 1, "exit": 0, "wall": 49.882, "mode": "honest", "accept": true, "stage": "accept", "statement_words": 35979, "proof_bytes": 25411968, "prove_s": 40.519, "decode_s": 0.0001, "derive_s": 0.236, "verify_s": 7.761, "peak_rss_kb": 4435528, "stderr": ""} -{"seed": 2, "exit": 0, "wall": 49.604, "mode": "honest", "accept": true, "stage": "accept", "statement_words": 35979, "proof_bytes": 25411232, "prove_s": 40.291, "decode_s": 0.0001, "derive_s": 0.217, "verify_s": 7.741, "peak_rss_kb": 4435604, "stderr": ""} -{"seed": 3, "exit": 0, "wall": 49.491, "mode": "honest", "accept": true, "stage": "accept", "statement_words": 35979, "proof_bytes": 25411424, "prove_s": 40.313, "decode_s": 0.0001, "derive_s": 0.217, "verify_s": 7.563, "peak_rss_kb": 4435608, "stderr": ""} -{"seed": 4, "exit": 0, "wall": 51.676, "mode": "honest", "accept": true, "stage": "accept", "statement_words": 35979, "proof_bytes": 25411648, "prove_s": 42.162, "decode_s": 0.0001, "derive_s": 0.228, "verify_s": 7.911, "peak_rss_kb": 4435604, "stderr": ""} -{"seed": 5, "exit": 0, "wall": 50.069, "mode": "honest", "accept": true, "stage": "accept", "statement_words": 35979, "proof_bytes": 25411360, "prove_s": 40.534, "decode_s": 0.0001, "derive_s": 0.212, "verify_s": 7.914, "peak_rss_kb": 4435604, "stderr": ""} -{"seed": 6, "exit": 0, "wall": 48.98, "mode": "honest", "accept": true, "stage": "accept", "statement_words": 35979, "proof_bytes": 25411488, "prove_s": 39.707, "decode_s": 0.0001, "derive_s": 0.236, "verify_s": 7.642, "peak_rss_kb": 4435608, "stderr": ""} -{"seed": 7, "exit": 0, "wall": 51.106, "mode": "honest", "accept": true, "stage": "accept", "statement_words": 35979, "proof_bytes": 25411200, "prove_s": 41.522, "decode_s": 0.0001, "derive_s": 0.218, "verify_s": 8.001, "peak_rss_kb": 4435604, "stderr": ""} -{"seed": 8, "exit": 0, "wall": 51.238, "mode": "honest", "accept": true, "stage": "accept", "statement_words": 35979, "proof_bytes": 25411168, "prove_s": 41.175, "decode_s": 0.0001, "derive_s": 0.232, "verify_s": 8.295, "peak_rss_kb": 4435608, "stderr": ""} -{"seed": 9, "exit": 0, "wall": 48.452, "mode": "honest", "accept": true, "stage": "accept", "statement_words": 35979, "proof_bytes": 25411648, "prove_s": 39.38, "decode_s": 0.0001, "derive_s": 0.226, "verify_s": 7.506, "peak_rss_kb": 4435608, "stderr": ""} -{"seed": 10, "exit": 0, "wall": 49.073, "mode": "honest", "accept": true, "stage": "accept", "statement_words": 35979, "proof_bytes": 25410848, "prove_s": 39.603, "decode_s": 0.0001, "derive_s": 0.227, "verify_s": 7.825, "peak_rss_kb": 4435608, "stderr": ""} +{"seed": 1, "exit": 0, "wall": 50.295, "mode": "honest", "accept": true, "stage": "accept", "statement_words": 35979, "proof_bytes": 25411968, "prove_s": 40.905, "decode_s": 0.0001, "derive_s": 0.216, "verify_s": 7.847, "peak_rss_kb": 4435480, "stderr": ""} +{"seed": 2, "exit": 0, "wall": 50.414, "mode": "honest", "accept": true, "stage": "accept", "statement_words": 35979, "proof_bytes": 25411232, "prove_s": 41.015, "decode_s": 0.0001, "derive_s": 0.239, "verify_s": 7.775, "peak_rss_kb": 4435604, "stderr": ""} +{"seed": 3, "exit": 0, "wall": 49.99, "mode": "honest", "accept": true, "stage": "accept", "statement_words": 35979, "proof_bytes": 25411424, "prove_s": 40.701, "decode_s": 0.0001, "derive_s": 0.231, "verify_s": 7.697, "peak_rss_kb": 4435608, "stderr": ""} +{"seed": 4, "exit": 0, "wall": 50.199, "mode": "honest", "accept": true, "stage": "accept", "statement_words": 35979, "proof_bytes": 25411648, "prove_s": 40.779, "decode_s": 0.0001, "derive_s": 0.226, "verify_s": 7.844, "peak_rss_kb": 4435604, "stderr": ""} +{"seed": 5, "exit": 0, "wall": 49.243, "mode": "honest", "accept": true, "stage": "accept", "statement_words": 35979, "proof_bytes": 25411360, "prove_s": 40.216, "decode_s": 0.0001, "derive_s": 0.219, "verify_s": 7.508, "peak_rss_kb": 4435608, "stderr": ""} +{"seed": 6, "exit": 0, "wall": 50.859, "mode": "honest", "accept": true, "stage": "accept", "statement_words": 35979, "proof_bytes": 25411488, "prove_s": 41.475, "decode_s": 0.0001, "derive_s": 0.207, "verify_s": 7.832, "peak_rss_kb": 4435532, "stderr": ""} +{"seed": 7, "exit": 0, "wall": 49.214, "mode": "honest", "accept": true, "stage": "accept", "statement_words": 35979, "proof_bytes": 25411200, "prove_s": 39.947, "decode_s": 0.0001, "derive_s": 0.212, "verify_s": 7.75, "peak_rss_kb": 4435608, "stderr": ""} +{"seed": 8, "exit": 0, "wall": 50.084, "mode": "honest", "accept": true, "stage": "accept", "statement_words": 35979, "proof_bytes": 25411168, "prove_s": 40.872, "decode_s": 0.0001, "derive_s": 0.214, "verify_s": 7.606, "peak_rss_kb": 4435608, "stderr": ""} +{"seed": 9, "exit": 0, "wall": 49.639, "mode": "honest", "accept": true, "stage": "accept", "statement_words": 35979, "proof_bytes": 25411648, "prove_s": 40.528, "decode_s": 0.0001, "derive_s": 0.249, "verify_s": 7.529, "peak_rss_kb": 4435608, "stderr": ""} +{"seed": 10, "exit": 0, "wall": 49.96, "mode": "honest", "accept": true, "stage": "accept", "statement_words": 35979, "proof_bytes": 25410848, "prove_s": 40.595, "decode_s": 0.0001, "derive_s": 0.225, "verify_s": 7.75, "peak_rss_kb": 4435608, "stderr": ""} diff --git a/experiments/20261105-32-qwen-deltanet-m2-design/runs/h5-hostile-seed1-ubsan.jsonl b/experiments/20261105-32-qwen-deltanet-m2-design/runs/h5-hostile-seed1-ubsan.jsonl index 9f6aeb6b..e49f9c35 100644 --- a/experiments/20261105-32-qwen-deltanet-m2-design/runs/h5-hostile-seed1-ubsan.jsonl +++ b/experiments/20261105-32-qwen-deltanet-m2-design/runs/h5-hostile-seed1-ubsan.jsonl @@ -1,4 +1,4 @@ -{"mode":"honest","accept":true,"stage":"accept","statement_words":35979,"proof_bytes":25411968,"prove_s":66.807,"decode_s":0.0002,"derive_s":0.366,"verify_s":13.683,"peak_rss_kb":4438952} +{"mode":"honest","accept":true,"stage":"accept","statement_words":35979,"proof_bytes":25411968,"prove_s":66.452,"decode_s":0.0002,"derive_s":0.346,"verify_s":13.310,"peak_rss_kb":4438944} {"mode":"hostile","class":"huge-element","attack":"S0 = (p+1)/2","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} {"mode":"hostile","class":"class-bound","attack":"S0 = first value outside class (+)","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} {"mode":"hostile","class":"class-bound","attack":"S0 = first value outside class (-)","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} @@ -31,6 +31,10 @@ {"mode":"hostile","class":"non-canonical","attack":"H word = H + p (conv history)","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} {"mode":"derive-overflow-detail","old_path_first_overflow_token":2} {"mode":"hostile","class":"derive-overflow","attack":"every class at its extreme (old witness path: i64 overflow)","rejected":true,"stage":"proof","expected_stage":"proof","as_expected":true} +{"mode":"hostile","class":"derive-overflow","attack":"same statement, witness-path derivation with checked arithmetic: reject at derive","rejected":true,"stage":"derive","expected_stage":"derive","as_expected":true} +{"mode":"derive-review2-detail","old_path_first_overflow_token":2} +{"mode":"hostile","class":"derive-overflow","attack":"review-2 all-extremes statement, witness-free entry","rejected":true,"stage":"proof","expected_stage":"proof","as_expected":true} +{"mode":"hostile","class":"derive-overflow","attack":"review-2 all-extremes statement, witness path with checked arithmetic: reject at derive","rejected":true,"stage":"derive","expected_stage":"derive","as_expected":true} {"mode":"derive-random-detail","old_path_first_overflow_token":-1} {"mode":"hostile","class":"derive-random","attack":"every word uniform in its class (fixed xorshift seed)","rejected":true,"stage":"proof","expected_stage":"proof","as_expected":true} {"mode":"hostile","class":"history-control","attack":"H[0] += 1 in class (M1b reads no history: accept expected here)","rejected":false,"stage":"accept","expected_stage":"accept","as_expected":true} @@ -43,4 +47,4 @@ {"mode":"r9-detail","j":0,"o":-1,"o_prime_word":"4503599626321919","violated_relations":0} {"mode":"hostile","class":"r9-control","attack":"current path: lookup_verify with caller-built relations, admissible() skipped","rejected":false,"stage":"accept","expected_stage":"accept","as_expected":true} {"mode":"hostile","class":"r9","attack":"verify_from_words on the R9 statement and R9 proof","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} -{"mode":"hostile-summary","as_expected":41,"total":41,"peak_rss_kb":5036352} +{"mode":"hostile-summary","as_expected":44,"total":44,"peak_rss_kb":5036288} diff --git a/experiments/20261105-32-qwen-deltanet-m2-design/runs/h5-hostile-seed1.jsonl b/experiments/20261105-32-qwen-deltanet-m2-design/runs/h5-hostile-seed1.jsonl index 519cb8b4..3fb7c89d 100644 --- a/experiments/20261105-32-qwen-deltanet-m2-design/runs/h5-hostile-seed1.jsonl +++ b/experiments/20261105-32-qwen-deltanet-m2-design/runs/h5-hostile-seed1.jsonl @@ -1,4 +1,4 @@ -{"mode":"honest","accept":true,"stage":"accept","statement_words":35979,"proof_bytes":25411968,"prove_s":40.219,"decode_s":0.0001,"derive_s":0.211,"verify_s":7.779,"peak_rss_kb":4435608} +{"mode":"honest","accept":true,"stage":"accept","statement_words":35979,"proof_bytes":25411968,"prove_s":40.732,"decode_s":0.0001,"derive_s":0.215,"verify_s":7.596,"peak_rss_kb":4435608} {"mode":"hostile","class":"huge-element","attack":"S0 = (p+1)/2","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} {"mode":"hostile","class":"class-bound","attack":"S0 = first value outside class (+)","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} {"mode":"hostile","class":"class-bound","attack":"S0 = first value outside class (-)","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} @@ -31,6 +31,10 @@ {"mode":"hostile","class":"non-canonical","attack":"H word = H + p (conv history)","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} {"mode":"derive-overflow-detail","old_path_first_overflow_token":2} {"mode":"hostile","class":"derive-overflow","attack":"every class at its extreme (old witness path: i64 overflow)","rejected":true,"stage":"proof","expected_stage":"proof","as_expected":true} +{"mode":"hostile","class":"derive-overflow","attack":"same statement, witness-path derivation with checked arithmetic: reject at derive","rejected":true,"stage":"derive","expected_stage":"derive","as_expected":true} +{"mode":"derive-review2-detail","old_path_first_overflow_token":2} +{"mode":"hostile","class":"derive-overflow","attack":"review-2 all-extremes statement, witness-free entry","rejected":true,"stage":"proof","expected_stage":"proof","as_expected":true} +{"mode":"hostile","class":"derive-overflow","attack":"review-2 all-extremes statement, witness path with checked arithmetic: reject at derive","rejected":true,"stage":"derive","expected_stage":"derive","as_expected":true} {"mode":"derive-random-detail","old_path_first_overflow_token":-1} {"mode":"hostile","class":"derive-random","attack":"every word uniform in its class (fixed xorshift seed)","rejected":true,"stage":"proof","expected_stage":"proof","as_expected":true} {"mode":"hostile","class":"history-control","attack":"H[0] += 1 in class (M1b reads no history: accept expected here)","rejected":false,"stage":"accept","expected_stage":"accept","as_expected":true} @@ -43,4 +47,4 @@ {"mode":"r9-detail","j":0,"o":-1,"o_prime_word":"4503599626321919","violated_relations":0} {"mode":"hostile","class":"r9-control","attack":"current path: lookup_verify with caller-built relations, admissible() skipped","rejected":false,"stage":"accept","expected_stage":"accept","as_expected":true} {"mode":"hostile","class":"r9","attack":"verify_from_words on the R9 statement and R9 proof","rejected":true,"stage":"decode","expected_stage":"decode","as_expected":true} -{"mode":"hostile-summary","as_expected":41,"total":41,"peak_rss_kb":5032972} +{"mode":"hostile-summary","as_expected":44,"total":44,"peak_rss_kb":5032912} diff --git a/research/operators/qwen_deltanet_m2/README.md b/research/operators/qwen_deltanet_m2/README.md index f695273d..cdc958cb 100644 --- a/research/operators/qwen_deltanet_m2/README.md +++ b/research/operators/qwen_deltanet_m2/README.md @@ -18,6 +18,6 @@ python3 research/operators/qwen_deltanet_m2/record.py build honest hostile model python3 research/operators/qwen_deltanet_m2/record.py ubsan # hostile suite under UBSan, about 4 min ``` -Post-review (#66): the statement words now include the conv history (3 x 384, class |H| < 2^31). The verifier's derivation is witness-free (`build(z, true)`), and every failure is a returned verdict, never exit(1). The hostile suite has 41 cases. +Post-review (#66): the statement words now include the conv history (3 x 384, class |H| < 2^31). The verifier's derivation is witness-free (`build(z, true)`), and every failure is a returned verdict, never exit(1). The hostile suite has 44 cases. Every i64 operation in `build()` is checked (overflow is a "derive" reject), and `record.py ubsan` runs the suite under UBSan. Research security settings only (target 10 bits). diff --git a/research/operators/qwen_deltanet_m2/admit_proto.cpp b/research/operators/qwen_deltanet_m2/admit_proto.cpp index d61bd48a..dd79a2d3 100644 --- a/research/operators/qwen_deltanet_m2/admit_proto.cpp +++ b/research/operators/qwen_deltanet_m2/admit_proto.cpp @@ -110,7 +110,10 @@ static bool size_preconditions(const Built &b) { // verifier. Every failure is a returned verdict; nothing here exits the process. // Class bounds (the M1b admit, now inside the verifier): |S0|, |S_T| < 2^18; a, beta in [0, 2^F]; // |k|, |q| <= 2^F; |v|, |o| < 2^31; conv history |H| < 2^31. -static Verdict verify_from_words(const Shape &sh, const std::vector &w, const u8 *proof, size_t bytes, VerifyTiming *tm = nullptr) { +// witness_path: derive with the first prototype's witness path (honest checks off), now with checked +// arithmetic. A control for the second review's UB finding only; the entry's derivation is +// witness-free. Either way an overflow is a "derive" reject, never UB. +static Verdict verify_from_words(const Shape &sh, const std::vector &w, const u8 *proof, size_t bytes, VerifyTiming *tm = nullptr, bool witness_path = false) { auto t0 = std::chrono::steady_clock::now(); const size_t DD = size_t(sh.D) * sh.D, want = 3 + DD + hist_words(sh) + size_t(sh.T) * (2 + 4 * sh.D) + DD; if (w.size() != want) { if (tm) tm->why = "statement length"; return Verdict::DECODE; } @@ -134,7 +137,8 @@ static Verdict verify_from_words(const Shape &sh, const std::vector &w, con // are not read by the relation list. Shapes only. z.S.assign(sh.T, std::vector(DD, 0)); z.S[sh.T - 1] = ST; auto t1 = std::chrono::steady_clock::now(); - Built b = build(z, true); + Built b = witness_path ? build(z, false, true) : build(z, true); + if (b.overflow) { if (tm) tm->why = "checked arithmetic overflowed while deriving the statement"; return Verdict::DERIVE; } if (!size_preconditions(b)) { if (tm) tm->why = "derived statement outside the sizing guards"; return Verdict::DERIVE; } Sized s = size_run(b); if (!s.ok) { if (tm) tm->why = "no plan holds for the derived statement"; return Verdict::DERIVE; } @@ -195,6 +199,7 @@ int main(int argc, char **argv) { check(z.T == sh.T && z.D == sh.D && z.F == sh.F, "fixture shape"); // Prover side, unchanged from M1b: witness from the fixture, cell by cell against reference.py. Built b = build(z); + check(!b.overflow, "honest witness arithmetic fits i64"); for (int t = 0; t < z.T; t++) check(b.M[t] == z.M[t] && b.delta[t] == z.delta[t] && b.S[t] == z.S[t] && b.o[t] == z.o[t], "C++ lowering equals reference.py"); Sized s = size_run(b); check(s.ok, "plan holds (M1b schedule)"); @@ -263,6 +268,21 @@ int main(int argc, char **argv) { } std::printf("{\"mode\":\"derive-overflow-detail\",\"old_path_first_overflow_token\":%d}\n", old_path_overflow_token(fixture_of_words(sh, w))); report("derive-overflow", "every class at its extreme (old witness path: i64 overflow)", verify_from_words(sh, w, pb.data(), pb.size()), Verdict::PROOF); + report("derive-overflow", "same statement, witness-path derivation with checked arithmetic: reject at derive", verify_from_words(sh, w, pb.data(), pb.size(), nullptr, true), Verdict::DERIVE); + } + // The second review's statement (#66, UBSan at m1b_builder.h aP = Pj * a): S0 = 2^18 - 1, k = a = + // 2^12, v = o = 2^31 - 1, all of one sign. Witness-free entry: no overflow, proof stage rejects. + // Witness path with checked arithmetic: the overflow is caught and the statement rejected at derive. + { + auto w = words; + for (size_t c = 0; c < size_t(sh.D) * sh.D; c++) w[w_S0(sh, c)] = enc(BOUND - 1); + for (int t = 0; t < sh.T; t++) { + w[w_a(sh, t)] = enc(one); w[w_beta(sh, t)] = enc(one); + for (int i = 0; i < sh.D; i++) { w[w_k(sh, t, i)] = enc(one); w[w_q(sh, t, i)] = enc(one); w[w_v(sh, t, i)] = enc(VBOUND - 1); w[w_o(sh, t, i)] = enc(VBOUND - 1); } + } + std::printf("{\"mode\":\"derive-review2-detail\",\"old_path_first_overflow_token\":%d}\n", old_path_overflow_token(fixture_of_words(sh, w))); + report("derive-overflow", "review-2 all-extremes statement, witness-free entry", verify_from_words(sh, w, pb.data(), pb.size()), Verdict::PROOF); + report("derive-overflow", "review-2 all-extremes statement, witness path with checked arithmetic: reject at derive", verify_from_words(sh, w, pb.data(), pb.size(), nullptr, true), Verdict::DERIVE); } { auto w = words; u64 st = 0x9e3779b97f4a7c15ULL; diff --git a/research/operators/qwen_deltanet_m2/evidence.json b/research/operators/qwen_deltanet_m2/evidence.json index 7dbba70b..7b757504 100644 --- a/research/operators/qwen_deltanet_m2/evidence.json +++ b/research/operators/qwen_deltanet_m2/evidence.json @@ -8,8 +8,8 @@ "research/operators/qwen_deltanet_m2/count_model.py": "0a1349252df326109cf619825c22239a48a04a4ab59e3d0617510022c883fcf7", "research/operators/qwen_deltanet_m2/head_reference.py": "1999ef56efd4752dfe493ea29cd8930b0c8e7bc3c9ee267a5c7f4ccbce3f6ff7", "research/operators/qwen_deltanet_m2/record.py": "17ca9221213f9c1f33c489a2c6ec3788f652f5bab767a0403588e526a44ba69e", - "research/operators/qwen_deltanet_m2/admit_proto.cpp": "9c12e1b18ed50c68741d9c9a49a3c2a49584585aaeafbf66cedfd7826932a3cc", - "research/operators/qwen_deltanet_m2/m1b_builder.h": "434e3534097c3bd89ca3f140072e5c3ead9a7fc9d12a05108dfa7bc59625669c", + "research/operators/qwen_deltanet_m2/admit_proto.cpp": "f76d96d34cd7cc9d2af7ec2783ac1e08c42db6f49d7aa270afcdc3c5b522a056", + "research/operators/qwen_deltanet_m2/m1b_builder.h": "397fe87c2ddf8724303fb27eed561f682cf4ca86169c9daec92655c6d0e74043", "research/operators/qwen_deltanet_lookup/lookup.h": "04bc74b28fb34f5a3a20ce7413d1310297c7fbb2f22a8da7aa9ae5caaaefd770", "research/operators/qwen_deltanet/reference.py": "f6fbd580bf834afd2d6280036ddf52d410be094824299e89c61eb2a1b80edaf0" }, @@ -232,19 +232,19 @@ "research/operators/qwen_deltanet_m2/work/admit_proto.bin" ], "exit": 0, - "seconds": 33.782, + "seconds": 33.17, "stdout": "", - "stderr": "In file included from research/operators/qwen_deltanet_m2/admit_proto.cpp:14:\nresearch/operators/qwen_deltanet_m2/m1b_builder.h:53:13: warning: \u2018bool admissible(const Fixture&)\u2019 defined but not used [-Wunused-function]\n 53 | static bool admissible(const Fixture &z) {\n | ^~~~~~~~~~\n" + "stderr": "In file included from research/operators/qwen_deltanet_m2/admit_proto.cpp:14:\nresearch/operators/qwen_deltanet_m2/m1b_builder.h:59:13: warning: \u2018bool admissible(const Fixture&)\u2019 defined but not used [-Wunused-function]\n 59 | static bool admissible(const Fixture &z) {\n | ^~~~~~~~~~\n" }, "honest": { "accepted": 10, "of": 10, "prove_s": { "n": 10, - "median": 40.416, - "sd": 0.8835618823828911, - "min": 39.38, - "max": 42.162 + "median": 40.74, + "sd": 0.42419415104145136, + "min": 39.947, + "max": 41.475 }, "decode_s": { "n": 10, @@ -255,17 +255,17 @@ }, "derive_s": { "n": 10, - "median": 0.2265, - "sd": 0.008504247304860215, - "min": 0.212, - "max": 0.236 + "median": 0.222, + "sd": 0.013019643278950124, + "min": 0.207, + "max": 0.249 }, "verify_s": { "n": 10, - "median": 7.793, - "sd": 0.23127926841807492, - "min": 7.506, - "max": 8.295 + "median": 7.75, + "sd": 0.12624473586385043, + "min": 7.508, + "max": 7.847 }, "proof_bytes": { "n": 10, @@ -276,17 +276,17 @@ }, "peak_rss_kb": { "n": 10, - "median": 4435606.0, - "sd": 24.81576559814793, - "min": 4435528, + "median": 4435608.0, + "sd": 44.34410896612987, + "min": 4435480, "max": 4435608 } }, "hostile": { "exit": 0, - "seconds": 129.884, - "as_expected": 41, - "total": 41, + "seconds": 136.033, + "as_expected": 44, + "total": 44, "by_class": { "huge-element": [ 9, @@ -301,8 +301,8 @@ 3 ], "derive-overflow": [ - 1, - 1 + 4, + 4 ], "derive-random": [ 1, @@ -337,7 +337,7 @@ }, "model": { "exit": 0, - "seconds": 1.292, + "seconds": 1.348, "stdout": "{\n \"H1\": {\n \"checks\": {\n \"m1\": {\n \"columns_used\": [\n 2338816,\n 2338816,\n true\n ],\n \"log_length\": [\n 22,\n 22,\n true\n ],\n \"relations\": [\n 4761088,\n 4761088,\n true\n ],\n \"terms\": [\n 11430400,\n 11430400,\n true\n ],\n \"range_cells\": [\n 0,\n 0,\n true\n ]\n },\n \"m1b\": {\n \"columns_used\": [\n 1310720,\n 1310720,\n true\n ],\n \"columns_nonregion\": [\n 1074176,\n 1074176,\n true\n ],\n \"log_length\": [\n 21,\n 21,\n true\n ],\n \"relations\": [\n 2231808,\n 2231808,\n true\n ],\n \"terms\": [\n 5174272,\n 5174272,\n true\n ],\n \"range_cells\": [\n 67072,\n 67072,\n true\n ]\n }\n },\n \"pass_\": true,\n \"model_m1\": {\n \"arm\": \"m1\",\n \"D\": 128,\n \"T\": 4,\n \"o_witness\": false,\n \"norm\": false,\n \"gate_columns\": 2338816,\n \"range_cells\": 0,\n \"lkp_keys\": 0,\n \"relations\": 4761088,\n \"terms\": 11430400,\n \"product_columns_per_state_cell_per_token\": 35.688,\n \"relations_per_state_cell_per_token\": 72.648,\n \"columns_used\": 2338816,\n \"layout\": 2338816,\n \"log_length\": 22\n },\n \"model_m1b\": {\n \"arm\": \"m1b\",\n \"D\": 128,\n \"T\": 4,\n \"o_witness\": false,\n \"norm\": false,\n \"gate_columns\": 1074176,\n \"range_cells\": 67072,\n \"lkp_keys\": 0,\n \"relations\": 2231808,\n \"terms\": 5174272,\n \"product_columns_per_state_cell_per_token\": 17.414,\n \"relations_per_state_cell_per_token\": 34.055,\n \"columns_used\": 1310720,\n \"columns_nonregion\": 1074176,\n \"rng_rows\": 8192,\n \"log_length\": 21\n }\n },\n \"H2\": {\n \"armA_m1b_shape\": {\n \"arm\": \"A\",\n \"D\": 128,\n \"T\": 4,\n \"o_witness\": false,\n \"norm\": false,\n \"gate_columns\": 1074176,\n \"range_cells\": 67072,\n \"lkp_keys\": 0,\n \"relations\": 2231808,\n \"terms\": 5174272,\n \"product_columns_per_state_cell_per_token\": 17.414,\n \"relations_per_state_cell_per_token\": 34.055,\n \"columns_used\": 1156096,\n \"columns_nonregion\": 1074176,\n \"rng_rows\": [\n 4096,\n 1024\n ],\n \"key_parts\": [],\n \"digit_entries\": 102400,\n \"rng_multiplicity_places\": 155648,\n \"remainder_multiplicity_places\": 0,\n \"multiplicity_row_places\": 155648,\n \"log_length\": 21\n },\n \"armB_m1b_shape\": {\n \"arm\": \"B\",\n \"D\": 128,\n \"T\": 4,\n \"o_witness\": false,\n \"norm\": false,\n \"gate_columns\": 263168,\n \"range_cells\": 67072,\n \"lkp_keys\": 67584,\n \"relations\": 609792,\n \"terms\": 1186816,\n \"product_columns_per_state_cell_per_token\": 6.07,\n \"relations_per_state_cell_per_token\": 9.305,\n \"columns_used\": 427008,\n \"columns_nonregion\": 263168,\n \"rng_rows\": [\n 4096,\n 1024\n ],\n \"key_parts\": [\n 65536,\n 16384\n ],\n \"digit_entries\": 102400,\n \"rng_multiplicity_places\": 155648,\n \"remainder_multiplicity_places\": 69632,\n \"multiplicity_row_places\": 225280,\n \"log_length\": 19\n },\n \"ratio_columns_per_cell\": 0.349,\n \"ratio_relations\": 0.273,\n \"ratio_terms\": 0.229,\n \"pass_\": true,\n \"note\": \"arm A at the M1b shape equals M1b (same lowering); arm B swaps exact bits for direct LKP keys\"\n },\n \"H3\": {\n \"armA_T16\": {\n \"arm\": \"A\",\n \"D\": 128,\n \"T\": 16,\n \"o_witness\": true,\n \"norm\": true,\n \"gate_columns\": 4377056,\n \"range_cells\": 272384,\n \"lkp_keys\": 0,\n \"relations\": 9042944,\n \"terms\": 21110960,\n \"product_columns_per_state_cell_per_token\": 17.736,\n \"relations_per_state_cell_per_token\": 34.496,\n \"columns_used\": 4655584,\n \"columns_nonregion\": 4377056,\n \"rng_rows\": [\n 16384,\n 1024\n ],\n \"key_parts\": [],\n \"digit_entries\": 348160,\n \"rng_multiplicity_places\": 172032,\n \"remainder_multiplicity_places\": 0,\n \"multiplicity_row_places\": 172032,\n \"log_length\": 23,\n \"prove_s_estimate\": null\n },\n \"armB_T16\": {\n \"arm\": \"B\",\n \"D\": 128,\n \"T\": 16,\n \"o_witness\": true,\n \"norm\": true,\n \"gate_columns\": 1056800,\n \"range_cells\": 272384,\n \"lkp_keys\": 276688,\n \"relations\": 2402432,\n \"terms\": 4786704,\n \"product_columns_per_state_cell_per_token\": 6.126,\n \"relations_per_state_cell_per_token\": 9.165,\n \"columns_used\": 1613856,\n \"columns_nonregion\": 1056800,\n \"rng_rows\": [\n 16384,\n 1024\n ],\n \"key_parts\": [\n 262144,\n 16384\n ],\n \"digit_entries\": 348160,\n \"rng_multiplicity_places\": 172032,\n \"remainder_multiplicity_places\": 77824,\n \"multiplicity_row_places\": 249856,\n \"log_length\": 21,\n \"prove_s_estimate\": 39.2,\n \"single_part_layout\": {\n \"key_part\": 524288,\n \"columns_used\": 1859616,\n \"log_length\": 21\n }\n },\n \"max_T_log22\": {\n \"A\": 14,\n \"B\": 41\n },\n \"max_T_log22_without_relation_cap\": {\n \"A\": 14,\n \"B\": 41\n },\n \"layer_extrapolation\": {\n \"A\": {\n \"relations_per_token_per_layer\": 27128832,\n \"columns_per_token_per_layer\": 13948320,\n \"columns_per_token_48_deltanet_layers\": 669519360\n },\n \"B\": {\n \"relations_per_token_per_layer\": 7207296,\n \"columns_per_token_per_layer\": 4817616,\n \"columns_per_token_48_deltanet_layers\": 231245568\n }\n },\n \"fits_2_21_armB_T16\": true,\n \"pass_\": true,\n \"note\": \"prove_s_estimate is linear in table length from #47/#63 points; indicative only. Layer extrapolation = 48 V heads x per-head-per-token; DeltaNet recurrence + gated norm only (no matmuls).\"\n },\n \"remainder_savings\": {\n \"armA_T16_columns\": 4649440,\n \"armB_T16_columns\": 1605872,\n \"relations_A\": 9042944,\n \"relations_B\": 2402432,\n \"terms_A\": 21110960,\n \"terms_B\": 4786704\n }\n}\n" }, "ranges": { @@ -357,9 +357,9 @@ }, "hostile_ubsan": { "exit": 0, - "seconds": 222.579, - "as_expected": 41, - "total": 41, + "seconds": 233.724, + "as_expected": 44, + "total": 44, "ubsan_reports": 0, "stderr": "" } diff --git a/research/operators/qwen_deltanet_m2/m1b_builder.h b/research/operators/qwen_deltanet_m2/m1b_builder.h index 934c9c0f..0c8532ad 100644 --- a/research/operators/qwen_deltanet_m2/m1b_builder.h +++ b/research/operators/qwen_deltanet_m2/m1b_builder.h @@ -9,6 +9,12 @@ // end the process. The column allocation and the relation list (terms, coefficients, targets) do // not depend on witness values, so they are identical to the prover's. Targets read only public // integers: S0, a, beta, k, v, q, o, S_T. The largest is 2^24 |v| < 2^55 for in-class v. +// Checked arithmetic (second review of #66): every i64 product, sum and difference in build() goes +// through mul/add/sub, which set Built::overflow instead of overflowing (signed overflow is UB). The +// verifier rejects a statement whose derivation set it (verdict "derive"); the prover stops. The +// first prototype's verifier derivation ran the witness path and overflowed at aP = Pj * a on an +// in-class all-extremes statement (UBSan); build(z, false, true) keeps that path as a checked +// control. fi() negates through u64, so it has no INT64_MIN case either. #pragma once #include #include @@ -24,7 +30,7 @@ using namespace blk; static void check(bool ok, const char *s) { if (!ok) { std::fprintf(stderr, "FAIL: %s\n", s); std::exit(1); } } -static F fi(i64 x) { return x >= 0 ? F{u64(x)} : F{P - u64(-x)}; } +static F fi(i64 x) { return x >= 0 ? F{u64(x) % P} : F{(P - (u64(0) - u64(x)) % P) % P}; } static i64 fdiv(i64 a, i64 b) { i64 q = a / b; if ((a % b) != 0 && ((a < 0) != (b < 0))) --q; return q; } constexpr i64 BOUND = i64(1) << 18; // honest contract for S', M, delta limbs constexpr u64 RTAG = u64(1) << 40; @@ -77,14 +83,21 @@ struct Built { u64 rel_S00 = 0, rel_S4_00 = 0; std::vector o_rbit0_last, o_rel_last; std::vector> o_rbits_last; std::vector o_rem_last; // last token: o remainder bit-0 column and remainder, per j std::vector> M, delta, S, o; + bool overflow = false; // a checked i64 operation would have overflowed std::vector rc; // columns of the range cells, in range-cell order std::vector rcv; // their honest values u64 rc_base = 0, rc_rows = 0; // region: product columns [rc_base, rc_base + 16 rc_rows) std::vector rcell_state, rcell_M, rcell_dlo, rcell_dhi; // [t] -> range-cell index of S_{t+1}[0,0], M[0], delta_lo[0], delta_hi[0] }; -static Built build(const Fixture &z, const bool relonly = false) { +// relonly: the verifier's witness-free derivation. nocheck: the witness path with the honest-value +// checks off (the first prototype's verifier derivation; kept only as a checked control). +static Built build(const Fixture &z, const bool relonly = false, const bool nocheck = false) { Built b; const int D = z.D, FB = z.F; const i64 one = i64(1) << FB; - const bool g_relonly = relonly; // witness checks off and witness values zero (see the header) + const bool g_relonly = relonly || nocheck; // honest-value checks off + // Checked i64 arithmetic: on overflow, flag it and return 0 (the flagged build is never used). + auto mul = [&](i64 x, i64 y) { i64 r; if (__builtin_mul_overflow(x, y, &r)) { b.overflow = true; return i64(0); } return r; }; + auto add = [&](i64 x, i64 y) { i64 r; if (__builtin_add_overflow(x, y, &r)) { b.overflow = true; return i64(0); } return r; }; + auto sub = [&](i64 x, i64 y) { i64 r; if (__builtin_sub_overflow(x, y, &r)) { b.overflow = true; return i64(0); } return r; }; auto col = [&](i64 x, i64 y) { b.cols.push_back({fi(x), fi(y), fi(x) * fi(y)}); return u64(b.cols.size() - 1); }; auto rel = [&](std::vector t, i64 target) { b.rel.push_back({std::move(t), fi(target)}); return u64(b.rel.size() - 1); }; // Unsigned width-bit decomposition of n (honest n must fit; builder asserts). Returns the @@ -95,7 +108,7 @@ static Built build(const Fixture &z, const bool relonly = false) { for (unsigned i = 0; i < width; i++) { i64 x = (n >> i) & 1; u64 c = col(x, x); rel({{0, c, F{1}}, {1, c, fi(-1)}}, 0); rel({{2, c, F{1}}, {0, c, fi(-1)}}, 0); - terms.push_back({0, c, fi(coef * (i64(1) << i))}); out.push_back(c); + terms.push_back({0, c, fi(mul(coef, i64(1) << i))}); out.push_back(c); } return out; }; @@ -125,24 +138,24 @@ static Built build(const Fixture &z, const bool relonly = false) { std::vector M(D), delta(D); std::vector dcell(D); for (int j = 0; j < D; j++) { i64 Pj = 0; std::vector pt; - for (int i = 0; i < D; i++) { if (!relonly) Pj += S[i * D + j] * z.k[t][i]; pt.push_back({2, g2[i * D + j], fi(-1)}); } + for (int i = 0; i < D; i++) { if (!relonly) Pj = add(Pj, mul(S[i * D + j], z.k[t][i])); pt.push_back({2, g2[i * D + j], fi(-1)}); } u64 g5 = col(Pj, a); pt.insert(pt.begin(), AffineTerm{0, g5, F{1}}); rel(pt, 0); b.rel_a[t].push_back(rel({{1, g5, F{1}}}, a)); - const i64 aP = Pj * a; M[j] = fdiv(aP, i64(1) << (2 * FB)); const i64 rM = aP - M[j] * (i64(1) << (2 * FB)); + const i64 aP = mul(Pj, a); M[j] = fdiv(aP, i64(1) << (2 * FB)); const i64 rM = sub(aP, mul(M[j], i64(1) << (2 * FB))); check(g_relonly || (M[j] > -BOUND && M[j] < BOUND), "honest M out of range (statement rejected)"); - u64 g6 = col(z.v[t][j] - M[j], beta); if (j == 0) b.g6_first.push_back(g6); + u64 g6 = col(sub(z.v[t][j], M[j]), beta); if (j == 0) b.g6_first.push_back(g6); // 2^2F * (v - g6.a) + r - g5.c = 0 std::vector ft = {{0, g6, fi(-(i64(1) << (2 * FB)))}, {2, g5, fi(-1)}}; auto mb = bits(rM, 2 * FB, ft, 1); if (j == 0) b.m_rbit0.push_back(mb[0]); - u64 rf = rel(ft, -(i64(1) << (2 * FB)) * z.v[t][j]); + u64 rf = rel(ft, mul(-(i64(1) << (2 * FB)), z.v[t][j])); // M + 2^18 = bits, M = v - g6.a // M (range cell) + g6.a = v const u64 mc = rcell(M[j]); if (j == 0) b.rcell_M[t] = mc; u64 rr = rel({{0, b.rc[mc], F{1}}, {0, g6, F{1}}}, z.v[t][j]); if (j == 0) { b.rel_v0_floor.push_back(rf); b.rel_v0_range.push_back(rr); } b.rel_beta[t].push_back(rel({{1, g6, F{1}}}, beta)); - const i64 num = relonly ? 0 : (z.v[t][j] - M[j]) * beta; delta[j] = fdiv(num, one); const i64 rd = num - delta[j] * one; - const i64 dhi = fdiv(delta[j], BOUND), dlo = delta[j] - dhi * BOUND; // two limbs, dlo in [0, 2^18) + const i64 num = relonly ? 0 : mul(sub(z.v[t][j], M[j]), beta); delta[j] = fdiv(num, one); const i64 rd = sub(num, mul(delta[j], one)); + const i64 dhi = fdiv(delta[j], BOUND), dlo = sub(delta[j], mul(dhi, BOUND)); // two limbs, dlo in [0, 2^18) // delta lives in g3[0][j].b; created below, so reserve now dcell[j] = col(z.k[t][0], delta[j]); u64 r0 = rel({{0, dcell[j], F{1}}}, z.k[t][0]); b.rel_k0[t].push_back(r0); @@ -163,7 +176,7 @@ static Built build(const Fixture &z, const bool relonly = false) { rel({{0, g3, F{1}}}, z.k[t][i]); rel({{1, g3, F{1}}, {1, dcell[j], fi(-1)}}, 0); } - const i64 num = relonly ? 0 : a * S[c] + z.k[t][i] * delta[j]; Sn[c] = fdiv(num, one); const i64 r = num - Sn[c] * one; + const i64 num = relonly ? 0 : add(mul(a, S[c]), mul(z.k[t][i], delta[j])); Sn[c] = fdiv(num, one); const i64 r = sub(num, mul(Sn[c], one)); g4[c] = col(Sn[c], z.q[t][i]); u64 rq = rel({{1, g4[c], F{1}}}, z.q[t][i]); if (i == 0) b.rel_q0[t].push_back(rq); @@ -175,10 +188,10 @@ static Built build(const Fixture &z, const bool relonly = false) { std::vector o(D); for (int j = 0; j < D; j++) { i64 sum = 0; std::vector ot; - for (int i = 0; i < D; i++) { if (!relonly) sum += Sn[i * D + j] * z.q[t][i]; ot.push_back({2, g4[i * D + j], fi(-1)}); } - o[j] = fdiv(sum, one); auto ob = bits(sum - o[j] * one, FB, ot, 1); - if (t == z.T - 1) { b.o_rbits_last.push_back(ob); b.o_rbit0_last.push_back(ob[0]); b.o_rem_last.push_back(sum - o[j] * one); } - u64 r = rel(ot, -one * z.o[t][j]); if (j == 0) b.rel_o0.push_back(r); + for (int i = 0; i < D; i++) { if (!relonly) sum = add(sum, mul(Sn[i * D + j], z.q[t][i])); ot.push_back({2, g4[i * D + j], fi(-1)}); } + o[j] = fdiv(sum, one); const i64 ro = sub(sum, mul(o[j], one)); auto ob = bits(ro, FB, ot, 1); + if (t == z.T - 1) { b.o_rbits_last.push_back(ob); b.o_rbit0_last.push_back(ob[0]); b.o_rem_last.push_back(ro); } + u64 r = rel(ot, mul(-one, z.o[t][j])); if (j == 0) b.rel_o0.push_back(r); if (t == z.T - 1) b.o_rel_last.push_back(r); } if (t == z.T - 1) for (int c = 0; c < D * D; c++) { u64 r = rel({{0, g4[c], F{1}}}, z.S[t][c]); if (c == 0) b.rel_S4_00 = r; }