Skip to content

refactor: decompose composition poly via squared-coset intermediate#515

Closed
diegokingston wants to merge 7 commits into
mainfrom
feat/eval-form-quotient
Closed

refactor: decompose composition poly via squared-coset intermediate#515
diegokingston wants to merge 7 commits into
mainfrom
feat/eval-form-quotient

Conversation

@diegokingston

Copy link
Copy Markdown
Collaborator

Factor decompose_and_extend_d2 into decompose_d2 (O(N) pointwise decomposition on squared coset) + iFFT(N,g²) + FFT(2N,g) extension. Mathematically identical to the old path.

This factoring exposes the N-point squared-coset representation as an intermediate step, which is the foundation for future eval-form quotient commitment (committing at N points instead of 2N, saving half the composition Merkle tree) once shared/multi-input FRI is available.

Changes:

  • prover: decompose_d2 extracts H₀,H₁ at N squared-coset points
  • prover: inline extension from squared coset to 2N LDE domain
  • prover: rename lde_composition_poly_evaluations → composition_poly_evaluations
  • verifier: factor reconstruct_deep into trace + full variants
  • fri: add commit_phase_from_evaluations_with_injection (unused, infrastructure for future shared FRI injection)
  • test: update decomposition test to verify N-point output

Factor decompose_and_extend_d2 into decompose_d2 (O(N) pointwise
decomposition on squared coset) + iFFT(N,g²) + FFT(2N,g) extension.
Mathematically identical to the old path.

This factoring exposes the N-point squared-coset representation as
an intermediate step, which is the foundation for future eval-form
quotient commitment (committing at N points instead of 2N, saving
half the composition Merkle tree) once shared/multi-input FRI is
available.

Changes:
- prover: decompose_d2 extracts H₀,H₁ at N squared-coset points
- prover: inline extension from squared coset to 2N LDE domain
- prover: rename lde_composition_poly_evaluations → composition_poly_evaluations
- verifier: factor reconstruct_deep into trace + full variants
- fri: add commit_phase_from_evaluations_with_injection (unused,
  infrastructure for future shared FRI injection)
- test: update decomposition test to verify N-point output
@diegokingston

Copy link
Copy Markdown
Collaborator Author

/bench 3

@github-actions

Copy link
Copy Markdown

Codex Code Review

  1. Medium | Potential bug / robustness: Injection length mismatch is only debug-checked and then silently truncated in release builds.
    File: fri/mod.rs:131
    debug_assert_eq!(evals.len(), inj.len()) is not enforced in release; then zip() at fri/mod.rs:137 drops unmatched tail elements. If this path is used, malformed injection size can produce incorrect commitments without an explicit failure.
    Action: enforce a runtime check (assert_eq! or return Result with error) before the loop.

  2. Low | Potential bug (panic edge case): New indexing assumes at least one composition part.
    File: prover.rs:1225
    let num_evals = composition_poly_evaluations[0].len(); can panic if composition_poly_evaluations is empty. Previous code path did not require indexing [0].
    Action: guard empty input explicitly (early return or error).

No additional concrete security vulnerabilities (unsafe/memory/crypto/VM privilege issues) were evident in this diff.

I couldn’t run tests in this environment because cargo attempted rustup writes on a read-only path (/home/runner/.rustup/tmp/...).

@github-actions

Copy link
Copy Markdown

Benchmark — fib_iterative_8M (median of 3)

Table parallelism: 32 (auto = cores / 3)

Metric main PR Δ
Peak heap 69446 MB 67428 MB -2018 MB (-2.9%) ⚪
Prove time 33.966s 33.771s -0.195s (-0.6%) ⚪

✅ No significant change.

⚠️ Heap spread: 7.3% (67165 MB / 72055 MB / 67428 MB)
Consider re-running /bench

Commit: 5e9f05b · Baseline: cached · Runner: self-hosted bench

@claude

claude Bot commented Apr 20, 2026

Copy link
Copy Markdown
Contributor

Three pieces of infrastructure added in this PR are dead code with no call sites: commit_phase_from_evaluations_with_injection (fri/mod.rs:97), reconstruct_trace_deep_evaluation (verifier.rs:824 — only called from reconstruct_full_deep_evaluation, not from the injection path it exists for), and compute_composition_injection_value (verifier.rs:868). The PR description acknowledges these are intentional groundwork for future shared/multi-input FRI injection, which is fine architecturally. However, they will generate compiler dead_code warnings. Please add #[allow(dead_code)] annotations (or gate them behind a Cargo feature) so CI stays clean and the intent is explicit to future contributors who might otherwise delete them.

.unwrap();

let lde_composition_poly_parts_evaluation: Vec<_> = lde_composition_poly_evaluations
let num_evals = composition_poly_evaluations[0].len();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Medium — Bug] composition_poly_evaluations[0] panics if the slice is empty. The previous code used flat_map and handled the empty case gracefully. In practice the prover always produces at least one part, but this is an unguarded index. Either add an assertion earlier (assert!(!composition_poly_evaluations.is_empty(), "...")) or write this as composition_poly_evaluations.first().map_or(0, |p| p.len()).

// After the first fold, inject composition DEEP values if provided.
if first_fold {
if let Some(ref inj) = injection {
debug_assert_eq!(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Low — Security] debug_assert_eq! is compiled out in release builds. If inj.len() != evals.len(), the subsequent zip silently truncates to the shorter length, producing a cryptographically incorrect FRI commitment with no error or diagnostic. This is especially dangerous for crypto code. Please use a hard assert_eq! (or return Err) so the invariant is enforced in all build profiles.

// Decompose H(x) = H₀(x²) + x·H₁(x²) via O(N) pointwise ops,
// then extend each part from the N-point squared coset (g²) to the
// 2N-point LDE domain (g) via iFFT(N) + FFT(2N) per part.
{

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Nit] The inner { ... } block wrapping this branch is unnecessary — there are no temporaries that need early drop or shadowing concerns. Removing it makes the if/else if/else chain visually consistent with the other branches.

let evaluation_point_sym =
Self::query_challenge_to_evaluation_point_sym(*iota, domain);

{

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Nit] The braces { ... } wrapping these two push calls have no scoping purpose — there are no temporaries to contain here. Removing them makes the for loop body cleaner.

8-task plan for replacing per-table independent Merkle trees with
shared batched trees + shared FRI + eval-form quotient commitment.

Expected: ~3x fewer Merkle trees, ~12x fewer FRI instances, 0
composition extension FFTs, ~70% proof size reduction.
Add `commit_main_traces_batched` to `IsStarkProver` that concatenates
columns from multiple tables into a single shared Merkle tree, returning
the tree, root, and a `BatchedLayout` tracking per-table column ranges.
Add batched proving and verification with shared Merkle trees and FRI:

- multi_prove_batched: commits all tables' main, aux, and composition
  poly evaluations in shared trees, alpha-batches per-table DEEP polys,
  and runs one shared FRI instance
- multi_verify_batched: replays the transcript identically, verifies
  OOD consistency per table, reconstructs alpha-batched DEEP at query
  points, and verifies shared FRI + tree openings
- TableProofData: add num_composition_parts field and constructor
- compute_deep_with_composition_from_evals: variant that takes
  composition evaluations directly (no Round2 struct needed)
- Tests: roundtrip test with CPU/ADD/MUL bus tables (uniform sizing)
  and all-padding test; both prove and verify successfully
@diegokingston

Copy link
Copy Markdown
Collaborator Author

/bench 3

@diegokingston
diegokingston deleted the feat/eval-form-quotient branch May 20, 2026 12:49
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