Skip to content

multi-stark bump - #522

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

multi-stark bump#522
gabriel-barrett wants to merge 4 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.
@gabriel-barrett

Copy link
Copy Markdown
Member Author

!benchmark aiur fresh

@argument-ci-bot

argument-ci-bot Bot commented Jul 28, 2026

Copy link
Copy Markdown

!benchmark — main vs 7099d9d

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 48.575 s 46.693 s -3.9% 🟢 35.120 36.540 +4.0% 🟢 96.36 GiB 96.27 GiB -0.1% 10.548 s 10.569 s +0.2% 167.5 ms 167.1 ms -0.2% 25.88 MiB 25.88 MiB +0.0% 36.21B 36.21B +0.0%
ByteArray.utf8DecodeChar?_utf8EncodeChar_append 47.937 s 46.092 s -3.9% 🟢 60.290 62.700 +4.0% 🟢 102.03 GiB 102.10 GiB +0.1% 9.078 s 9.112 s +0.4% 179.6 ms 191.9 ms +6.9% (1.07× slower) ⚠️ 25.96 MiB 25.96 MiB +0.0% 38.11B 38.11B +0.0%
Char.ofOrdinal_le_of_le 37.956 s 36.577 s -3.6% 🟢 75.400 78.250 +3.8% 🟢 79.82 GiB 79.92 GiB +0.1% 6.793 s 6.573 s -3.2% 🟢 198.3 ms 179.3 ms -9.6% (1.11× faster) 🟢 25.90 MiB 25.90 MiB +0.0% 29.15B 29.15B +0.0%
Vector.extract_append._proof_2 27.943 s 26.779 s -4.2% 🟢 51.570 53.810 +4.3% 🟢 53.13 GiB 53.15 GiB +0.0% 5.626 s 5.842 s +3.8% ⚠️ 161.3 ms 157.6 ms -2.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.479 s 22.482 s -4.2% 🟢 84.030 87.760 +4.4% 🟢 50.08 GiB 50.07 GiB -0.0% 3.440 s 3.457 s +0.5% 161.2 ms 172.5 ms +7.0% (1.07× slower) ⚠️ 25.72 MiB 25.72 MiB +0.0% 15.71B 15.71B +0.0%
String.split 22.145 s 21.371 s -3.5% 🟢 88.010 91.200 +3.6% 🟢 48.06 GiB 48.04 GiB -0.1% 3.142 s 3.139 s -0.1% 178.6 ms 159.1 ms -10.9% (1.12× faster) 🟢 25.92 MiB 25.92 MiB +0.0% 14.22B 14.22B +0.0%
List.mergeSort 15.381 s 14.885 s -3.2% 🟢 103.900 107.360 +3.3% 🟢 31.72 GiB 31.77 GiB +0.2% 2.247 s 2.236 s -0.5% 172.5 ms 156.0 ms -9.6% (1.11× faster) 🟢 25.77 MiB 25.77 MiB +0.0% 10.46B 10.46B +0.0%
Vector.append 5.095 s 4.984 s -2.2% 111.090 113.570 +2.2% 8.35 GiB 8.33 GiB -0.2% 604.5 ms 612.9 ms +1.4% 161.5 ms 151.8 ms -6.0% (1.06× faster) 🟢 24.38 MiB 24.38 MiB +0.0% 2.21B 2.21B +0.0%
Nat.gcd_comm 4.247 s 4.149 s -2.3% 97.720 100.030 +2.4% 7.88 GiB 7.51 GiB -4.6% 🟢 475.0 ms 473.2 ms -0.4% 147.7 ms 154.1 ms +4.3% ⚠️ 23.99 MiB 23.99 MiB +0.0% 1.56B 1.56B +0.0%
String.append 3.149 s 3.050 s -3.1% 🟢 112.110 115.730 +3.2% 🟢 6.63 GiB 6.55 GiB -1.3% 357.4 ms 352.8 ms -1.3% 148.6 ms 142.1 ms -4.4% 🟢 23.30 MiB 23.30 MiB +0.0% 869.46M 869.46M +0.0%
Int.gcd 2.564 s 2.551 s -0.5% 89.320 89.780 +0.5% 4.85 GiB 4.84 GiB -0.1% 289.6 ms 290.0 ms +0.2% 139.3 ms 132.7 ms -4.7% 🟢 22.92 MiB 22.92 MiB +0.0% 539.56M 539.56M +0.0%
Nat.sub_le_of_le_add 2.360 s 2.398 s +1.6% 80.530 79.230 -1.6% 4.92 GiB 4.88 GiB -0.9% 285.0 ms 284.3 ms -0.2% 157.3 ms 147.4 ms -6.3% (1.07× faster) 🟢 23.39 MiB 23.39 MiB +0.0% 451.92M 451.92M +0.0%
Nat.add_comm 1.284 s 1.337 s +4.1% ⚠️ 39.710 38.150 -3.9% ⚠️ 3.96 GiB 4.62 GiB +16.6% (1.17× larger) ⚠️ 197.4 ms 197.3 ms -0.1% 129.2 ms 125.8 ms -2.6% 21.85 MiB 21.85 MiB +0.0% 48.99M 48.99M +0.0%

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

Workflow logs

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
@gabriel-barrett

Copy link
Copy Markdown
Member Author

!benchmark aiur-recursive fresh

@argument-ci-bot

argument-ci-bot Bot commented Jul 28, 2026

Copy link
Copy Markdown

!benchmark — main vs ad3cffa

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 4.544 s 4.540 s -0.1% 14.64 GiB 14.63 GiB -0.1% 10.91 MiB 10.57 MiB -3.2% 🟢 70.3 ms 67.2 ms -4.5% 🟢 448.5 ms 448.2 ms -0.1% 3.94B 3.97B +0.6% 87.8 ms 98.9 ms +12.6% (1.13× slower) ⚠️ 846.23 KiB 846.23 KiB +0.0% 6.3 ms 6.6 ms +5.1% (1.05× slower) ⚠️ 361.79 MiB 370.93 MiB +2.5%
square-q100-b1 3.424 s 3.480 s +1.6% 9.62 GiB 9.67 GiB +0.5% 10.84 MiB 10.50 MiB -3.2% 🟢 73.4 ms 67.6 ms -8.0% (1.09× faster) 🟢 398.8 ms 400.1 ms +0.3% 3.39B 3.30B -2.5% 71.0 ms 74.8 ms +5.3% (1.05× slower) ⚠️ 779.67 KiB 779.67 KiB +0.0% 6.1 ms 6.2 ms +1.3% 255.14 MiB 262.51 MiB +2.9%

2 proofs · 2 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