Skip to content

multi-stark bump - #522

Draft
gabriel-barrett wants to merge 8 commits into
mainfrom
multi-stark-overhaul
Draft

multi-stark bump#522
gabriel-barrett wants to merge 8 commits into
mainfrom
multi-stark-overhaul

Conversation

@gabriel-barrett

Copy link
Copy Markdown
Member

Aiur on constraint-IR multi-stark + in-circuit verifier port

Adopts the multi-stark constraint restructure
(argumentcomputer/multi-stark#multi-stark-overhaul,
rev 1c6ef62), where circuits are data instead of Air trait impls:
frontend Expr trees compile to a flat, hash-consed node graph
(graph::ConstraintGraph) that the prover, verifier, and witness
generator evaluate with a dense forward sweep. Three commits, each
building and passing tests on its own:

1. aiur: migrate to constraint-IR multi-stark

Aiur no longer defines AIRs — it builds CircuitInputs (Expr
constraints + lookups) directly.

  • constraints.rs: SymbolicExpressionmulti_stark::expr::Expr;
    the Constraints Air/BaseAir impls are deleted; the per-selector
    boolean constraints formerly emitted in eval are materialized into
    the constraint vector; degree-tracking auxiliary-column allocation is
    kept.
  • synthesis.rs: System<SC, AiurCircuit>System<SC>; the
    AiurCircuit AIR enum is gone; each circuit becomes a
    CircuitInputs. Prove/verify logic unchanged.
  • memory.rs / gadgets: lookups over Expr (pull = negated
    multiplicity); memory transition rules re-expressed as explicit
    constraints via IsTransition / next-row columns.
  • vk_codec.rs: vk wire format v3 — serializes the compiled node
    graph (tagged node stream, constraint roots, layout widths) instead
    of the symbolic AIR. Node degrees are derived on decode, not stored.
  • Adds a Rust end-to-end prove/verify test exercising an auxiliary
    column and the function-channel lookup: honest proof verifies,
    tampered claim rejected.

2. aiur: broaden e2e prove/verify test to call + memory

Extends the e2e test to cover function calls and store/load, so the
function and memory lookup channels are exercised through real proofs.

3. MultiStark: port in-circuit recursive verifier to constraint-IR

The Aiur-DSL recursive STARK verifier now consumes vk v3.

  • SystemDeserialize.lean: parses the per-circuit header + tagged
    node stream + constraint roots. The symbolic reader
    (SymExpr/SysAir/SysLookupAir) is deleted — lookup constraints
    arrive pre-compiled into the roots.
  • Verifier.lean: the symbolic-tree interpreter + LookupAir replay
    is replaced by a forward node sweep mirroring the Rust evaluator:
    one value buffer slot per node, leaves read from the opened rows /
    lookup publics / Lagrange selector evals, roots Horner-folded with α.
    Stage-2 openings are consumed as base columns directly (no
    from_ext_basis except for the quotient). Quotient recombination
    unchanged.
  • crates/ixvm-codegen/src/aiur_multi_stark.rs regenerated
    (lake exe ix codegen).

Testing

  • cargo test -p aiur --release: 5/5 e2e prove/verify programs;
    workspace check/clippy/fmt clean.
  • lake test -- --ignored multi-stark: non-native Goldilocks /
    ExtGoldilocks arithmetic + reduced-opening fold vs reference, 12/12.
  • lake test -- --ignored recursive-verifier: honest proof accepted
    (vk digest + OOD + FRI), codegen'd verifier matches the interpreter,
    tampered proof advice and tampered claim both rejected, 6/6.
  • lake test -- --ignored aiur: execute/interpret/claim/prove/verify
    across the test programs.

Bump multi-stark to the constraint-IR rev (1c6ef62) and adapt Aiur to the
new API, which replaces the AIR/builder machinery with data. Net removal:
Aiur no longer defines AIRs — it builds CircuitInputs (Expr constraints +
Lookups) directly.

- constraints.rs: SymbolicExpression -> multi_stark::expr::Expr; delete the
  Constraints Air/BaseAir impls; materialize the per-selector boolean
  constraints (formerly emitted in eval) into the constraint vector; keep
  the degree-tracking auxiliary-column allocation.
- synthesis.rs: System<SC, AiurCircuit> -> System<SC>; delete the
  AiurCircuit AIR enum; build each circuit as CircuitInputs; retain
  per-circuit lookup slot widths for the witness layout (the old
  circuits[i].air.lookups path is gone). prove/verify logic unchanged.
- memory.rs / gadgets{,bytes1,bytes2}.rs: lookups over Expr (pull = negated
  multiplicity), delete Air/BaseAir, re-express memory transition rules as
  explicit Expr constraints (IsTransition / main_next); keep tables,
  execution, and witness generation.
- vk_codec.rs: serialize the new compiled circuit (flat node graph + roots
  + widths) instead of the symbolic AIR. NOTE: the vk wire format changed;
  the Lean mirror Ix/MultiStark/SystemDeserialize.lean must be updated to
  match (not done here).

Adds a Rust end-to-end prove/verify test (f(a,b)=a*b) exercising an
auxiliary column and the function-channel lookup: honest proof verifies,
tampered claim rejected. cargo check/test/clippy/fmt clean across the
workspace.
Adds prove_verify_call_and_memory_roundtrip: f(a,b) = g(a)*b with b routed
through a size-1 memory (Call + Store + Load + Mul). Exercises the
cross-circuit function-channel lookup (f pushes, g's return pulls) and the
memory-channel lookup + the migrated Memory transition constraints. Honest
proof verifies; tampered claim rejected. Test-only; 5 aiur tests pass.
Rewrite the Aiur-DSL recursive STARK verifier to match the new
constraint-IR multi-stark (vk v3 = flat compiled node graph).

- SystemDeserialize.lean: parse per-circuit 8xu32 header + tagged SysNode
  stream (children by NodeId) + zeros roots (compiled lookups skipped via
  the record length prefix; lookup constraints are compiled into zeros).
  Drop SymExpr/SysEntry/SysAir/SysLookupAir and the symbolic reader.
- Verifier.lean: replace the symbolic-tree interpreter + LookupAir replay
  (eval_sym/fingerprint_ext/fold_zeros/fold_sel_bools/fold_lookups/
  ood_comp_tail) with a forward node sweep: sweep_nodes builds a per-node
  value buffer, eval_node reads leaves (Var by 2*source+offset from the
  opened rows, Public from the beta/gamma/acc/next_acc coord vector,
  selectors from Lagrange evals), fold_roots Horner-folds zeros with alpha.
  Stage-2 opened rows fed as base columns directly (no from_ext_basis);
  quotient recombination unchanged.
- Regenerate crates/ixvm-codegen/src/aiur_multi_stark.rs (lake exe ix
  codegen); aiur_ixvm.rs byte-identical.

lake test: multi-stark self-tests 12/12, recursive-verifier e2e 6/6
(honest proof accepted, codegen verifier matches interpreter, tamper
rejected), aiur prove/verify 5/5 programs.
The OOD composition check evaluated the constraint graph with a forward
sweep mirroring the Rust evaluator, appending each node's value to a
List<Ext> buffer via list_concat and reading children back with
list_lookup. In Aiur that buffer is O(n^2): every append rebuilds the
list, and the temporaries dominated the verifier's list traffic
(list_concat.G_2 alone was 16% of total FFT cost).

eval_at interprets the graph directly instead: evaluate node i by
recursing into its children. Aiur memoizes calls by argument pointers,
so each node is computed at most once per opening context and every
further reference (DAG sharing, later constraint roots) is a cache hit;
node fetches share list_drop's cached drop-chain over the single nodes
list. fold_roots evaluates each constraint root through the same cache.
Dead nodes unreachable from the roots are never evaluated at all.

bench-recursive-verifier --execute-only --queries 3 (factorial(5)):
total in-circuit FFT cost 341.4M -> 222.9M (-35%); the sweep-buffer
circuits (sweep_nodes/buf_child and the list_concat.G_2 + memory[4]
traffic) drop out of the profile, leaving Blake3 hashing at ~80% of
verifier cost. aiur_multi_stark.rs regenerated (lake exe ix codegen).

lake test -- --ignored recursive-verifier: 6/6 (codegen'd verifier
matches interpreter; tampered proof and claim still rejected).
@argumentcomputer argumentcomputer deleted a comment from argument-ci-bot Bot Jul 28, 2026
Adopt the direct-logup multi-stark rev: the logUp constraints are no
longer compiled into the constraint graph, so the vk stops carrying
them — measured on the IxVM kernel system, the vk drops from 5.91 MB
to 1.54 MB (-74%; the synthesized logUp machinery was 91% of the
compiled nodes). The recursive verifier's in-circuit cost at toy scale
drops ~12% (less vk hashing and node reading).

- Cargo.toml: TEMPORARY [patch] to the local multi-stark checkout
  (branch direct-logup, b76e43b); swap to the pushed rev before merge.
- vk_codec.rs: the per-circuit header's num_publics slot (a constant,
  derived on decode) now carries constraint_count (user roots +
  (L+3)*D, the observe_shape value), and max_constraint_degree is the
  combined user + analytic-logUp value; the decoder fills the new
  Circuit fields and recomputes the graph's own user-roots max degree.
- SystemDeserialize.lean: the compiled lookups (multiplicity + arg
  NodeIds) are now parsed instead of skipped — they drive the direct
  evaluation; the constraint-count shape limb comes from the header
  (zero_count is no longer observed); record framing asserted.
- Verifier.lean: ood_composition folds the directly-evaluated logUp
  values after the user roots, mirroring Rust's
  lookup::logup_constraint_values: per lookup the two coordinates of
  (beta + fingerprint(gamma, args)) * inv - 1 (fingerprint via
  head-first Horner through the memoized eval_at, so lookup-expression
  evaluation shares the user-constraint cache), then the first-row,
  transition, and last-row accumulator constraints. Coordinate pairs
  multiply in X^2 = 7 with Ext coefficients (pair_mul).
- aiur_multi_stark.rs regenerated (lake exe ix codegen).

lake test -- --ignored {multi-stark, recursive-verifier, aiur} all
pass (honest accepted, codegen matches interpreter, tampered proof and
claim rejected); cargo test -p aiur 5/5 incl. the vk codec round-trip;
workspace clippy/fmt clean.
The residual size gap to the old symbolic-tree vk was pure encoding
width: preorder trees pay zero bytes for structure while the graph
paid two u32 child ids per binary node. Close it by sizing the wire
fields to the actual value ranges:

- node ids, zeros, and lookup references are u16 (per-circuit graphs
  are far below 65k nodes; the encoder asserts),
- Var packs (source, offset) into the tag byte (10 + 2*source +
  offset) with a u16 column index: 7 bytes -> 3,
- constants split small (u16) / big (u64): most are selector weights
  and small literals, 9 bytes -> 3,
- Public index is u8 (num_publics = 8),
- Add/Sub/Mul: 9 bytes -> 5.

Mirrored in SystemDeserialize.lean (tags 10..15 map straight to Var
source/offset constants; u16 reads); aiur_multi_stark.rs regenerated.

IxVM kernel vk progression: 5.91 MB (compiled logUp) -> 1.54 MB
(direct logUp) -> 819 KB dense — 28% below the old symbolic-tree
format (1.13 MB) and 7.2x below where this branch started, with the
same 4.3x hash-consing advantage now paying only ~2x pointer overhead.

lake test -- --ignored {multi-stark, recursive-verifier, aiur} all
pass (honest accepted, codegen matches interpreter, tampers rejected);
cargo test -p aiur 5/5 incl. codec round-trip; clippy/fmt clean.
@argumentcomputer argumentcomputer deleted a comment from argument-ci-bot Bot Jul 29, 2026
@argumentcomputer argumentcomputer deleted a comment from argument-ci-bot Bot Jul 29, 2026
Nothing in the vk is now redundant — every byte is content the
verifier consumes (nodes, zeros, lookups, widths, degree):

- constraint_count and stage_2_width are no longer serialized: both
  derive from the zero/lookup counts (zeros + (L+3)*D and (1+L)*D).
  The Lean reader builds their Fiat-Shamir observation limbs with one
  gl_to_bytes each — cheaper in-circuit than hashing the extra bytes.
- lookup_prefix_len dropped: the Lean verifier never used it, and the
  Rust decoder derives it as 1 + the max node id reachable from the
  lookup expressions (children always precede parents).
- The per-record u32 length prefix dropped: it existed to skip the
  lookups section, which is now fully parsed; records decode from a
  single shared cursor, guarded by the full-consumption check
  (truncated and padded vks are rejected — test updated).
- Counts and widths sized to their ranges: node/zero/lookup counts,
  main/preprocessed widths, and max_constraint_degree are u16
  (preprocessed_height stays u32: the Bytes2 table is exactly 65536).

IxVM kernel vk: 819,329 -> 796,593 bytes. Full progression vs the
5.91 MB this line of work started from: direct logUp 1.54 MB, dense
nodes 819 KB, derivables dropped 797 KB — 30% below main's 1.13 MB
symbolic-tree format.

lake test -- --ignored {multi-stark, recursive-verifier, aiur} all
pass; cargo test -p aiur 5/5; clippy/fmt clean; codegen regenerated.
@argumentcomputer argumentcomputer deleted a comment from argument-ci-bot Bot Jul 29, 2026
@argumentcomputer argumentcomputer deleted a comment from argument-ci-bot Bot Jul 29, 2026
@argumentcomputer argumentcomputer deleted a comment from argument-ci-bot Bot Jul 29, 2026
…t path)

No API or protocol change — the rev only speeds up the prover's
quotient constraint evaluation (~20% faster than the old compiled-logUp
path on a lookup-heavy benchmark). cargo test -p aiur 5/5.
@gabriel-barrett

Copy link
Copy Markdown
Member Author

!benchmark aiur fresh

@gabriel-barrett

Copy link
Copy Markdown
Member Author

!benchmark aiur-recursive fresh

@argument-ci-bot

argument-ci-bot Bot commented Jul 29, 2026

Copy link
Copy Markdown

!benchmark — main vs 8847e0e

backends: aiur=prove · envs: InitStd · set: primary · shard: 0 · baseline: fresh (base-SHA run, bencher bypassed)

aiur · InitStd · prove — main from: base run @ 7ff054b (fresh — bencher bypassed)

constant prove-time (main) prove-time (PR) Δ% throughput (const/s) (main) throughput (const/s) (PR) Δ% peak-ram (main) peak-ram (PR) Δ% execute-time (main) execute-time (PR) Δ% verify-time (main) verify-time (PR) Δ% proof-size (main) proof-size (PR) Δ% fft-cost (main) fft-cost (PR) Δ%
Array.extract_append 49.277 s 46.373 s -5.9% (1.06× faster) 🟢 34.620 36.790 +6.3% (1.06× faster) 🟢 96.30 GiB 96.39 GiB +0.1% 10.378 s 10.351 s -0.3% 168.0 ms 168.2 ms +0.1% 25.88 MiB 25.88 MiB +0.0% 36.21B 36.21B +0.0%
ByteArray.utf8DecodeChar?_utf8EncodeChar_append 50.414 s 45.957 s -8.8% (1.10× faster) 🟢 57.330 62.880 +9.7% (1.10× faster) 🟢 102.06 GiB 102.05 GiB -0.0% 9.454 s 9.280 s -1.8% 162.9 ms 221.2 ms +35.8% (1.36× slower) ⚠️ 25.96 MiB 25.96 MiB +0.0% 38.11B 38.11B +0.0%
Char.ofOrdinal_le_of_le 38.262 s 36.238 s -5.3% (1.06× faster) 🟢 74.800 78.980 +5.6% (1.06× faster) 🟢 79.83 GiB 79.86 GiB +0.0% 6.805 s 6.753 s -0.8% 162.6 ms 199.9 ms +23.0% (1.23× slower) ⚠️ 25.90 MiB 25.90 MiB +0.0% 29.15B 29.15B +0.0%
Vector.extract_append._proof_2 28.725 s 26.639 s -7.3% (1.08× faster) 🟢 50.170 54.090 +7.8% (1.08× faster) 🟢 53.16 GiB 53.15 GiB -0.0% 5.678 s 5.842 s +2.9% 166.4 ms 165.9 ms -0.3% 25.55 MiB 25.55 MiB +0.0% 21.18B 21.18B +0.0%
_private.Init.Data.Range.Polymorphic.SInt.0.Int64.instRxcHasSize_eq 23.427 s 22.719 s -3.0% 🟢 84.220 86.840 +3.1% 🟢 50.09 GiB 50.11 GiB +0.0% 3.472 s 3.536 s +1.8% 174.6 ms 163.6 ms -6.3% (1.07× faster) 🟢 25.72 MiB 25.72 MiB +0.0% 15.71B 15.71B +0.0%
String.split 22.690 s 21.195 s -6.6% (1.07× faster) 🟢 85.900 91.960 +7.1% (1.07× faster) 🟢 48.06 GiB 48.06 GiB +0.0% 3.139 s 3.131 s -0.3% 171.9 ms 165.2 ms -3.9% 🟢 25.92 MiB 25.92 MiB +0.0% 14.22B 14.22B +0.0%
List.mergeSort 15.556 s 14.812 s -4.8% (1.05× faster) 🟢 102.720 107.880 +5.0% (1.05× faster) 🟢 31.73 GiB 31.71 GiB -0.1% 2.263 s 2.285 s +1.0% 161.1 ms 180.1 ms +11.8% (1.12× slower) ⚠️ 25.77 MiB 25.77 MiB +0.0% 10.46B 10.46B +0.0%
Vector.append 5.139 s 4.921 s -4.2% 🟢 110.140 115.010 +4.4% 🟢 8.51 GiB 8.36 GiB -1.8% 603.8 ms 595.8 ms -1.3% 150.7 ms 146.8 ms -2.6% 24.38 MiB 24.38 MiB +0.0% 2.21B 2.21B +0.0%
Nat.gcd_comm 4.337 s 4.132 s -4.7% 🟢 95.680 100.430 +5.0% 🟢 7.57 GiB 7.54 GiB -0.3% 473.0 ms 466.5 ms -1.4% 147.0 ms 145.8 ms -0.8% 23.99 MiB 23.99 MiB +0.0% 1.56B 1.56B +0.0%
String.append 3.172 s 3.181 s +0.3% 111.290 110.970 -0.3% 6.49 GiB 4.93 GiB -24.1% (1.32× smaller) 🟢 355.5 ms 365.6 ms +2.8% 151.0 ms 139.2 ms -7.8% (1.08× faster) 🟢 23.30 MiB 23.30 MiB +0.0% 869.46M 869.46M +0.0%
Int.gcd 2.552 s 2.548 s -0.2% 89.730 89.880 +0.2% 4.85 GiB 5.53 GiB +13.8% (1.14× larger) ⚠️ 289.9 ms 294.9 ms +1.7% 139.4 ms 135.2 ms -3.0% 22.92 MiB 22.92 MiB +0.0% 539.56M 539.56M +0.0%
Nat.sub_le_of_le_add 2.440 s 2.391 s -2.0% 77.860 79.460 +2.1% 4.88 GiB 4.85 GiB -0.5% 281.2 ms 285.5 ms +1.5% 150.4 ms 147.8 ms -1.7% 23.39 MiB 23.39 MiB +0.0% 451.92M 451.92M +0.0%
Nat.add_comm 1.298 s 1.311 s +1.0% 39.290 38.900 -1.0% 4.17 GiB 3.92 GiB -6.1% (1.06× smaller) 🟢 198.3 ms 195.0 ms -1.6% 138.8 ms 127.3 ms -8.3% (1.09× faster) 🟢 21.85 MiB 21.85 MiB +0.0% 48.99M 48.99M +0.0%

13 constants · 4 with regressions · 11 with improvements (|Δ| > 3.0% on any metric).

Workflow logs

@argument-ci-bot

argument-ci-bot Bot commented Jul 29, 2026

Copy link
Copy Markdown

!benchmark — main vs 8847e0e

backends: aiur-recursive · envs: InitStd · set: primary · shard: 0 · baseline: fresh (base-SHA run, bencher bypassed)

aiur-recursive · InitStd — main from: base run @ 7ff054b (fresh — bencher bypassed)

proof recursive-prove-time (main) recursive-prove-time (PR) Δ% recursive-peak-ram (main) recursive-peak-ram (PR) Δ% recursive-proof-size (main) recursive-proof-size (PR) Δ% recursive-verify-time (main) recursive-verify-time (PR) Δ% recursive-execute-time (main) recursive-execute-time (PR) Δ% recursive-fft-cost (main) recursive-fft-cost (PR) Δ% prove-time (main) prove-time (PR) Δ% proof-size (main) proof-size (PR) Δ% verify-time (main) verify-time (PR) Δ% peak-ram (main) peak-ram (PR) Δ%
factorial-q100-b2 5.126 s 4.569 s -10.9% (1.12× faster) 🟢 14.61 GiB 14.62 GiB +0.1% 10.91 MiB 10.88 MiB -0.3% 76.7 ms 68.8 ms -10.3% (1.12× faster) 🟢 462.7 ms 442.0 ms -4.5% 🟢 3.94B 3.84B -2.7% 99.5 ms 100.6 ms +1.2% 846.23 KiB 846.23 KiB +0.0% 6.9 ms 6.5 ms -4.8% (1.05× faster) 🟢 355.63 MiB 364.95 MiB +2.6%
square-q100-b1 3.506 s 3.708 s +5.8% (1.06× slower) ⚠️ 9.66 GiB 9.63 GiB -0.3% 10.84 MiB 10.81 MiB -0.3% 76.5 ms 70.4 ms -8.0% (1.09× faster) 🟢 401.7 ms 408.5 ms +1.7% 3.39B 3.41B +0.6% 71.0 ms 101.1 ms +42.3% (1.42× slower) ⚠️ 779.67 KiB 779.67 KiB +0.0% 6.1 ms 6.2 ms +0.4% 253.34 MiB 270.73 MiB +6.9% (1.07× larger) ⚠️

2 proofs · 1 with regressions · 2 with improvements (|Δ| > 3.0% on any metric).

Workflow logs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant