Skip to content

[Part 2/2] spike(stark): constraint-IR — full capture (incl LogUp) + wire prover/verifier behind constraint-ir feature#757

Draft
MauroToscano wants to merge 3 commits into
spike/constraint-ir-builderfrom
spike/constraint-ir-builder-part2
Draft

[Part 2/2] spike(stark): constraint-IR — full capture (incl LogUp) + wire prover/verifier behind constraint-ir feature#757
MauroToscano wants to merge 3 commits into
spike/constraint-ir-builderfrom
spike/constraint-ir-builder-part2

Conversation

@MauroToscano

Copy link
Copy Markdown
Contributor

Part 2 of 2 — stacked on #739 (base = spike/constraint-ir-builder). Part 1 (#739) is the Plan-B builder + Phase-0 spike + design docs; this PR is Phases 1–2 of the GPU-constraint-eval roadmap (thoughts/gpu-constraint-eval/roadmap.md), CPU-only. GPU phases (4–5) are a separate follow-up.

Goal: capture every table's transition constraints once into a flat single-field Goldilocks IR, then interpret that IR in place of the boxed evaluate path — the prerequisite for evaluating constraints on-device (data residency) without a separate symbolic backend.

Phase 1 — full capture coverage (ea6f6b4d)

  • Capture impls for all ~33 algebraic constraints + the 2 LogUp constraints (LookupBatchedTermConstraint, LookupAccumulatedConstraint), each a 1:1 mirror of the existing evaluate/compute body — added alongside the unchanged boxed path (non-destructive; the boxed path stays the default + differential oracle).
  • IR/builder extended with uniform leaves (challenge / alpha-power / periodic / table-offset), aux + next-row reads; interpreter generalized to eval_program (prover) / eval_program_verifier (verifier OOD); object-safe capture added to TransitionConstraintEvaluator + AIR::constraint_program().

Phase 2 — wire behind a feature (1c923425)

  • New constraint-ir Cargo feature (default OFF). When on, the IR interpreter replaces the per-row compute_transition_prover call in evaluate_transitions and the compute_transition call in the verifier; boxed path remains the default and the oracle.
  • constraint_ir/bridge.rs: the generic→concrete-Goldilocks TypeId seam (mirrors gpu_lde.rs), falling back to the boxed path off the Goldilocks tower.

Review-gap close (7821c612)

  • Self-certifying differential tests for both LogUp absorbed branches (assert absorbed.len()/degree() before differencing) + all 10 Packing variants (capture_fingerprint vs compute_fingerprint_from_step, 1000 random rows).
  • Fixed a --features constraint-ir panic on example AIRs: the default capture now marks ConstraintProgram::complete = false → prover/verifier fall back to the boxed path for any AIR not fully capture-capable (chosen over stubbing the example AIRs), with an assertion that production CPU/EQ programs are complete == true.

Gates (both green)

  • Phase 1: full CPU + EQ programs interpreted over real traces match compute_transition_prover (every LDE row) and compute_transition (verifier OOD) bit-for-bit.
  • Phase 2: cargo test -p lambda-vm-prover --release --features stark/constraint-ir430 passed / 0 failed (incl. all test_prove_elfs_*), identical to the boxed-path baseline. cargo test -p stark --features constraint-ir → 135 passed, no panic.
  • fmt + clippy clean on both feature states.

The capture logic was reviewed bit-for-bit against the originals across all files with no correctness defects found. Note: every production table has an even bus-interaction count (CPU=20, EQ=6), so all hit LookupAccumulated's 2-absorbed branch; the 1-absorbed branch is covered by the synthetic test added here.

…gUp)

Extends the constraint-ir spike (PR #739) from the minimal Phase-0 algebraic
subset to full coverage: all ~33 algebraic transition constraints across
prover/src/constraints/ and prover/src/tables/, plus the 2 LogUp framework
constraints (LookupBatchedTermConstraint, LookupAccumulatedConstraint) in
crypto/stark/src/lookup.rs.

IR/builder/interpreter additions:
- ir.rs: Periodic/RapChallenge/AlphaPow/TableOffset leaf Ops; ConstraintProgram
  gains num_base for the prover's base/ext split.
- builder.rs: challenge/alpha_power/periodic/table_offset/const_ext leaf
  constructors; emit() now indexes roots by constraint_idx (was emit-order).
- interp.rs: eval_program/eval_program_verifier, the full prover/verifier
  entry points matching compute_transition_prover/compute_transition;
  eval_program_base kept for the Phase-0 diff test.
- traits.rs: AIR::constraint_program(), capturing every transition_constraints()
  entry into one program.
- constraints/transition.rs: object-safe capture() on TransitionConstraintEvaluator
  (default unimplemented!, since most non-production examples don't need it);
  TransitionConstraintAdapter forwards to the wrapped Capture impl; boxed()
  requires Self: Capture so every production constraint must implement it.

Capture impls added for every remaining algebraic constraint struct (cpu.rs,
branch/commit/eq/load/store/memw*/keccak/ec_scalar/ecsm/ecdas, and the
multi-kind mega-constraints mul/dvrm/shift/cpu32/lt), mirroring each
evaluate()/compute() body 1:1. LogUp capture mirrors compute_fingerprint_from_step
/Packing::accumulate_fingerprint_with/Multiplicity::evaluate_with, with is_sender
resolved at capture time (add vs neg) and the accumulated constraint's next-row
reads via aux(1, col).

Gate: prover/src/tests/constraint_ir_tests.rs now captures the full CPU and EQ
table programs and asserts bit-for-bit equality against compute_transition_prover
(every LDE row) and compute_transition (verifier, OOD point) — covering both
the batched-pair and both 1-/2-absorbed LogUp branches. Full prover test suite
(430 tests, incl. test_prove_elfs_*) passes unchanged, confirming the additions
are non-destructive.
…rifier behind a feature

Adds the `constraint-ir` Cargo feature to crypto/stark. Behind it, the
prover (ConstraintEvaluator::evaluate_transitions) and verifier
(IsStarkVerifier::step_2_verify_claimed_composition_polynomial) interpret
the captured ConstraintProgram in place of the boxed
Vec<Box<dyn TransitionConstraintEvaluator>> dispatch loop. The boxed path
stays the default and the differential oracle (falls back automatically
if the type tower isn't lambda_vm's Goldilocks base + degree-3 extension).

- constraint_ir/bridge.rs: the generic Field/FieldExtension -> concrete
  Goldilocks TypeId seam (mirrors crate::gpu_lde's transmute pattern,
  using plain `transmute` on the reference/slice types rather than
  transmute_copy, since references are always pointer-sized regardless
  of the generic pointee).
- constraints/evaluator.rs: ConstraintEvaluator caches the
  AIR::constraint_program() once in `new()` (feature-gated field);
  evaluate_transitions swaps the per-row compute_transition_prover call
  for try_eval_program_prover, falling back to the boxed call if it
  returns false. Requires 'static on the impl block (TypeId needs it).
- verifier.rs: step_2_verify_claimed_composition_polynomial's transition
  evaluation is split into a tiny step_2_compute_transitions helper with
  two #[cfg] sibling bodies (boxed vs IR-interpreted-with-fallback); the
  surrounding boundary/zerofier/composition accounting is untouched.
  IsStarkVerifier and its sole impl gain a 'static bound (needed
  transitively for the TypeId check; safe since there is one implementor).

Gate: cargo test -p lambda-vm-prover --release --features stark/constraint-ir
passes all 430 tests (incl. every test_prove_elfs_* real end-to-end
prove->verify), identical pass count to the default boxed path. fmt/clippy
clean on both feature states; full workspace build unaffected.
…rage, all Packing variants, no-panic fallback

Review of Phases 1-2 found no correctness defects in the existing captures,
but four coverage/robustness gaps:

1. LogUp accumulated-constraint branch coverage. Verified via real
   bus_interactions().len() (not call-site grep counts) that CPU (20) and EQ
   (6) are BOTH even, so split_interactions gives both absorbed_count==2 —
   neither exercises the 1-absorbed branch, and full_table_gate's comment
   claiming otherwise was wrong. No in-repo production table has an odd
   interaction count > 1. Added self-certifying targeted unit tests in
   crypto/stark/src/lookup.rs (logup_capture_tests module) that directly
   construct LookupAccumulatedConstraint with 1- and 2-element absorbed
   vecs, asserting absorbed.len()/degree() up front so the test can't
   silently degrade to the wrong branch if edited later. Also added a
   targeted LookupBatchedTermConstraint test.

2. Packing variant coverage. Only 5/10 Packing variants are ever
   instantiated by production tables, and DWordHHW/DWordBL had no
   differential gate at all (e2e-only). Added a test driving
   capture_fingerprint vs compute_fingerprint_from_step for all 10
   variants over 1000 random rows each.

3. Fixed the inaccurate comment in full_table_gate (prover crate) claiming
   CPU+EQ cover both 1-/2-absorbed branches; corrected to state both are
   2-absorbed and point to the new lookup.rs tests for the real coverage.

4. Fixed `cargo test -p stark --features constraint-ir` panicking: the
   examples/*  and test-only AIRs (not part of the IR migration) rely on
   the default TransitionConstraintEvaluator::capture, which used to call
   unimplemented!(). Changed the default to call a new
   IrBuilder::mark_unsupported() instead (no panic), which marks the
   resulting ConstraintProgram::complete = false. ConstraintEvaluator::new
   and the verifier's step_2_compute_transitions now check `complete` and
   cache/use `None` (always fall back to the boxed path) for any AIR whose
   constraints aren't fully Capture-capable — production lambda_vm AIRs are
   unaffected (added an assertion to full_table_gate confirming
   prog.complete == true for CPU/EQ, plus a regression test in
   transition_tests.rs pinning the default-capture-doesn't-panic behavior).

All new tests pass: cargo test -p stark --features constraint-ir (135/135,
no panics), cargo test -p stark logup_capture_tests (4/4), cargo test
-p lambda-vm-prover --release constraint_ir_tests (6/6) and the full
--release suite with the feature on (430/430, unchanged from baseline).
fmt/clippy clean on both feature states (only pre-existing, untouched
prover_tests.rs warnings remain). Existing committed capture logic is
untouched except the one comment fix.
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