Skip to content

refactor(eor)!: unify setup prefix and suffix reduction - #398

Open
quangvdao wants to merge 10 commits into
mainfrom
codex/prover-opt-01-suffix-eor
Open

refactor(eor)!: unify setup prefix and suffix reduction#398
quangvdao wants to merge 10 commits into
mainfrom
codex/prover-opt-01-suffix-eor

Conversation

@quangvdao

@quangvdao quangvdao commented Aug 12, 2026

Copy link
Copy Markdown

Summary

This PR replaces the old extension opening reduction, or EOR, prover with one dense implementation for every live EOR source. The same reduction now handles a borrowed setup prefix and a compact recursive suffix. Sparse witnesses and lazy sparse factors are removed.

The new implementation does the following work:

  • It opens a dense setup prefix and a recursive suffix in one EOR when a later evaluation trace fold needs both.
  • It reads balanced suffix digits through a row iterator instead of building a padded base field table.
  • It builds one transparent equality factor for each opening group and shares that full table across the terms in the group.
  • It keeps one final claim for each opening while using one degree two sum-check and one challenge sequence across all groups.
  • It derives the EOR input claim from the tensor partials that were already added to the transcript. A separate full table scan remains enabled in debug builds.
  • It leaves the proof encoding, transcript labels, challenge order, generated schedules, and verifier code unchanged.

The general commitment and NTT work found during the same profiling effort now lives in PR #430. This PR contains only EOR and the tensor and equality helpers that EOR uses.

Normative records:

Diff metadata

  • Base: ca95c81dcc268618980e22357176e22a580f5e80 on main
  • Head: e96f50d8715c680a419a40f01e529850061d655d
  • Commits: 7
  • Files changed: 28
  • Diff: 1,029 insertions and 2,747 deletions
  • Pull request state: open and mergeable

Why this change was needed

The repository had three EOR representations. It had dense tables, sparse witnesses, and a lazy sparse tensor factor. Generated production schedules did not use the sparse witness path. Direct tests and a synthetic benchmark were its only callers.

That unused path had a large cost. Every change to EOR had to preserve separate constructors, table types, compute hooks, fold logic, and tests. The sparse directory alone held more than 1,700 lines. It also hid the two source forms that the live protocol needs:

  • A recursive suffix is naturally stored as compact balanced digits.
  • A setup prefix is already stored as a dense section of the expanded setup matrix.

The new design keeps those source forms until the tensor boundary, then converts both into the same dense extension field reduction.

When Akita runs EOR

The opening method decides whether a fold uses EOR.

Field configuration Evaluation trace behavior
fp32 The extension degree is four. Later evaluation trace folds run EOR.
fp64 The extension degree is two. Later evaluation trace folds run EOR.
fp128 The extension degree is one. Evaluation trace does not need EOR.

Levels 0 and 1 can use subring coefficient packing and bypass EOR. A later level can receive both a recursive witness and a setup prefix. When that level uses evaluation trace over fp32 or fp64, one EOR includes both sources.

This policy does not limit setup offloading to two levels. A later fold may create another setup prefix when the complete schedule selects that edge. The receiving fold must open the prefix and witness with the same scheduled method.

Prover data flow

flowchart TD
    A[Evaluation trace fold] --> B[Opening groups]
    B --> C[Borrowed setup prefix]
    B --> D[Compact suffix digits]
    C --> E[TensorProjectionKernel]
    D --> E
    E --> F[Opening values and tensor partials]
    F --> G[Add values and partials to transcript]
    G --> H[Sample eta and claim coefficients]
    H --> I[Derive the batched input claim from row partials]
    E --> J[Build dense packed witnesses]
    H --> K[Build one equality factor per group]
    J --> L[Create one dense term per opening]
    K --> L
    L --> M[Extend shorter groups over fixed zero coordinates]
    M --> N[Run one degree two sum-check]
    N --> O[Return one final claim per opening]
Loading

Change surface

Area Before After
EOR witness type Dense and sparse variants One dense Vec<E> representation
Recursive suffix A padded Vec<F> could be built before packing Balanced i8 digits are converted as each row is read
Setup prefix Later setup prefixes could not enter tensor EOR EOR borrows the checked prefix from AkitaExpandedSetup
Transparent factor Dense and lazy sparse forms One mapped dense table for each group
Terms in one group Each term could own the complete factor table Terms share one Arc<Vec<E>> until the first fold
Groups with fewer variables Separate physical padding would be needed ExtensionOpeningTables::Cylindrical adds virtual zero fixed variables
Input claim The release prover scanned every witness and factor table again The prover uses the claim derived from transcript bound row partials
Sparse benchmark A synthetic sparse term benchmark remained registered The benchmark and its Cargo target are removed

Tensor sources

Borrowed setup prefix

RecursiveFoldSource::SetupPrefix now implements the same tensor projection interface as a recursive suffix. setup_prefix_base_evals checks the frozen ring dimension, checks that the prefix length is a multiple of the ring dimension, and bounds the slice against the shared expanded setup matrix.

The source borrows that checked field slice. It does not copy the prefix into a second witness. The shared dense tensor helpers then compute column partials and packed extension field evaluations.

Compact recursive suffix

The suffix keeps its balanced digits in SuffixWitnessView. Its TensorColumnSource implementation returns a row iterator that converts each digit to the base field as the contraction consumes it. The prover no longer allocates a complete padded base field table for this operation.

tensor_packed_witness_evals and tensor_column_partials_split_fold are still the canonical tensor operations. Dense slices and compact suffix rows differ only in how they supply a row.

Extension field coefficient access

ExtField now requires from_base_fn and base_coefficient. The existing from_base_slice and to_base_vec methods use those primitives. Tensor transposition can therefore construct and read extension field coordinates without temporary vectors.

One reduction across opening groups

The prover adds the claimed openings and tensor partials to the transcript before it samples eta and the claim batching coefficients. It derives one input claim per opening from the row partials, then combines those claims with the sampled coefficients.

Each opening still creates one ExtensionOpeningReductionTerm. Terms in the same group share the full equality factor returned by EqPolynomial::evals_mapped. Groups with fewer tail variables use ExtensionOpeningTables::Cylindrical. This keeps their native witness tables small while they follow the common challenge sequence.

The degree two sum-check returns one common point rho. The prover then exposes the final witness and factor value for every original opening. The existing later protocol stage binds that complete final vector to the committed witness with a separate challenge.

Dense table ownership and round work

DenseEorFactor has two states:

  • Shared holds the full factor table in an Arc<Vec<E>> at the start of the reduction.
  • Owned holds a term specific folded table after the first challenge.

This is the only copy boundary. Every term in a group reads the same full factor table for the first round. Each term then owns only its half sized folded factor.

For later rounds, fused_fold_and_accumulate folds the witness and factor and computes the next round constant and quadratic coefficients in one pass. The next call reuses those cached coefficients.

The accumulator still follows the field contract. It delays reduction only when HasUnreducedOps::DELAYED_PRODUCT_SUM_IS_EXACT is true. Other fields reduce each product before adding it. The lossy accumulator regression test covers this fallback.

Security and verifier behavior

This PR changes prover storage and computation. It does not change the statement that the verifier checks.

  • The verifier still derives the initial EOR claim from the opening values and tensor partials in the transcript.
  • The verifier still checks every degree two round and the final transparent factor value.
  • The proof still carries one final claim for each original opening.
  • A second independent challenge still binds that final vector to the committed witness.
  • Proof structs, serialization, transcript labels, and challenge order do not change.
  • No verifier file changes in this PR.

The release prover no longer recomputes the input claim by scanning every dense term. It uses the claim independently derived from the transcript bound row partials. Debug builds still recompute the complete table claim and reject any mismatch. This removes duplicate prover work without moving a trust decision into the verifier.

Malformed point dimensions, partial counts, table lengths, setup prefix shapes, group sizes, and virtual domain overflows return AkitaError. The standalone dense constructor still checks that witness and factor tables have the same nonzero power of two length.

Breaking API changes

This is an intentional internal API cut. The repository does not promise backward compatibility.

The PR removes these public or crate visible sparse interfaces:

  • SparseExtensionOpeningWitness
  • TensorPackedWitness
  • SPARSE_TENSOR_FACTOR_MAX_LAZY_ROUNDS
  • Sparse term constructors and lazy factor types
  • Sparse tensor batch compute hooks

It also changes two traits:

  • ExtField implementors must provide allocation free coefficient construction and access through from_base_fn and base_coefficient.
  • TensorColumnSource implementors must provide a row iterator and accept the requested row width.

No wrapper preserves the old sparse API. Dense standalone callers can still use ExtensionOpeningReductionTerm::new and ExtensionOpeningReductionProver::from_dense_tables.

Measured effects

Local microbenchmarks collected while developing the retained source changes showed the following results:

Operation Before After Change
Compact suffix packed witness, fp32, nv24 13.232 ms 10.193 ms 23.0% faster
Compact suffix partial contraction, fp32, nv24 27.967 ms 23.620 ms 15.5% faster
Shared dense partial contraction 22.137 ms 14.390 ms 35.0% faster
Shared dense tensor factor projection 146.388 ms 64.174 ms 56.2% faster

These are focused prover measurements, not end to end proving claims. The final PR removes the temporary benchmark harnesses. The production profile jobs in CI cover complete fp32, fp64, fp128, grouped, recursive, and distributed flows.

Commit map

  • 5e0f4df94 removes the sparse EOR implementation and synthetic benchmark.
  • e21d11572 lets later evaluation trace folds include a dense setup prefix.
  • 60e2b0b0a streams compact suffix rows and adds direct extension field coefficient access.
  • 057c4b7cc builds mapped equality tables in one canonical operation.
  • b3044927a prepares one transparent factor for each opening group.
  • 33a2a926e shares the full dense factor table across terms until the first fold.
  • e96f50d87 removes stale sparse wording and updates the live specifications.

Validation at e96f50d87

Local validation on the exact head passed:

  • cargo fmt --all --check
  • rtk cargo test -p akita-prover --test extension_opening_reduction, with 14 tests passed
  • rtk cargo test -p akita-prover --lib extension_opening_reduction, with 2 tests passed and 333 filtered out

At the time this description was published, 40 GitHub checks had passed and none had failed. The successful checks included Clippy, fuzzing, Jolt compatibility, documentation, security scans, the portable x86 verifier check, native AArch64 NTT tests, the emulated AVX-512 NTT job, and six of the seven production profile jobs.

The following jobs were still running:

  • Bench (3-fp128-base)
  • Test (1/2)
  • Test (2/2)
  • Schedule table drift (all-schedules)
  • Transcript semantics (transcript-blake2b)
  • Transcript semantics (transcript-keccak)

Remaining work

This PR completes the dense EOR consolidation. The approved packed sum-check specification is not implemented here. That work can replace the scalar extension field loops in the dense fold, accumulation, factor fold, and tensor partial contraction without changing the protocol.

The removed benchmark measured a dormant sparse representation. A future EOR benchmark should measure the surviving dense kernel boundary and should land with the packed implementation.

Reviewer map

Suggested review order:

  1. Read specs/subring-coefficient-packing.md for the setup prefix policy.
  2. Read crates/akita-prover/src/protocol/core/extension_opening_reduction.rs for transcript order and group assembly.
  3. Read crates/akita-prover/src/protocol/extension_opening_reduction/term.rs and tables.rs for the canonical prover state.
  4. Read crates/akita-prover/src/backend/recursive/setup_prefix_source.rs for the borrowed setup prefix.
  5. Read crates/akita-prover/src/backend/recursive/witness/tensor.rs for the compact suffix source.
  6. Read crates/akita-types/src/extension_opening_reduction.rs and crates/akita-algebra/src/eq_poly.rs for the shared tensor operations.
  7. Read crates/akita-prover/tests/extension_opening_reduction.rs and the source local tests for correctness and rejection coverage.
  8. Read specs/packed-sumcheck.md for the separate packed follow up.

@github-actions

Copy link
Copy Markdown

Warning

This PR has more than 500 changed lines and does not include a spec.

Large features and architectural changes benefit from a short spec before implementation review.
See CONTRIBUTING.md for the spec workflow.

If this PR is a bug fix, small cleanup, or otherwise does not warrant a spec, feel free to ignore this message.

@cursor

cursor Bot commented Aug 12, 2026

Copy link
Copy Markdown

PR Summary

High Risk
Touches prover-side extension-opening reduction, tensor packing, and claim derivation for recursive openings. Verifier encoding is unchanged, but incorrect tables or claims would break proofs.

Overview
Unifies extension-opening reduction onto a single dense path and drops unused sparse witnesses, lazy tensor factors, TensorPackedWitness, and the sparse EOR benchmark.

Later evaluation-trace folds can now batch a borrowed setup prefix with a compact recursive suffix in one EOR. Prefixes are sliced from the expanded setup matrix; suffix digits stream through a TensorColumnSource row iterator instead of a padded base table.

Terms in a group share one mapped equality factor (EqPolynomial::evals_mapped + Arc until the first fold). The release prover takes the input claim from transcript-bound tensor partials (full-table check stays debug-only). ExtField gains allocation-free from_base_fn / base_coefficient. Proof encoding, transcript labels, and verifier code are unchanged.

Reviewed by Cursor Bugbot for commit f35a8a9. Bugbot is set up for automated code reviews on this repo. Configure here.

@github-actions github-actions Bot added the no-spec PR has no spec file label Aug 12, 2026
@github-actions

github-actions Bot commented Aug 12, 2026

Copy link
Copy Markdown

Documentation blast radius (advisory)

These regions may need doc/spec/book updates based on changed paths.
This is not a merge gate. See docs/documentation.md.

Changed files in this PR: 28

workspace-crates

Workspace membership and public crate surface

Code paths touched:

  • crates/akita-pcs/Cargo.toml

Consider updating:

  • README.md
  • docs/crate-graph.md
  • book/src/how/architecture.md

planner-schedule

Schedule tables, DP fallback, proof-size planning

Code paths touched:

  • crates/akita-planner/README.md

Consider updating:

  • book/src/how/configuration.md
  • book/src/usage/profiling.md
  • specs/planner-*.md
  • specs/PRUNING.md

prover-protocol

Prove pipeline, folds, sumcheck stages

Code paths touched:

  • crates/akita-prover/src/protocol/core.rs
  • crates/akita-prover/src/protocol/core/extension_opening_reduction.rs
  • crates/akita-prover/src/protocol/extension_opening_reduction/dense.rs
  • crates/akita-prover/src/protocol/extension_opening_reduction/mod.rs
  • crates/akita-prover/src/protocol/extension_opening_reduction/sparse/mod.rs
  • crates/akita-prover/src/protocol/extension_opening_reduction/sparse/tables.rs
  • crates/akita-prover/src/protocol/extension_opening_reduction/sparse/tensor_factor.rs
  • crates/akita-prover/src/protocol/extension_opening_reduction/sparse/term.rs
  • crates/akita-prover/src/protocol/extension_opening_reduction/sparse/tests.rs
  • crates/akita-prover/src/protocol/extension_opening_reduction/sparse/witness.rs
  • crates/akita-prover/src/protocol/extension_opening_reduction/tables.rs
  • crates/akita-prover/src/protocol/extension_opening_reduction/term.rs

Consider updating:

  • book/src/how/proving/**
  • book/src/how/recursion.md
  • specs/packed-sumcheck.md
  • specs/archive/2026-Q3/terminal-fold-cutover.md
  • specs/archive/2026-Q3/protocol-field-geometry-cutover.md

commitment-setup

Setup expansion and Ajtai commitment

Code paths touched:

  • crates/akita-prover/src/backend/recursive/setup_prefix_source.rs
  • crates/akita-prover/src/backend/recursive/witness.rs
  • crates/akita-prover/src/backend/recursive/witness/tensor.rs

Consider updating:

  • book/src/how/commitment.md
  • book/src/usage/commitment-api.md
  • specs/setup-*.md

field-algebra

Field traits, packed SIMD, extension towers

Code paths touched:

  • crates/akita-algebra/src/eq_poly.rs
  • crates/akita-field/src/ext/lift.rs
  • crates/akita-field/src/ext/tests.rs

Consider updating:

  • book/src/foundations/rings-and-fields.md
  • book/src/foundations/ntt-crt.md
  • book/src/how/optimizations.md
  • specs/archive/2026-Q3/akita-field-refactor.md
  • specs/archive/2026-Q3/crt-ntt-*.md
  • specs/archive/2026-Q3/avx-simd-port.md

extension-opening

Extension-opening reduction prover paths

Code paths touched:

  • crates/akita-prover/src/protocol/extension_opening_reduction/dense.rs
  • crates/akita-prover/src/protocol/extension_opening_reduction/mod.rs
  • crates/akita-prover/src/protocol/extension_opening_reduction/sparse/mod.rs
  • crates/akita-prover/src/protocol/extension_opening_reduction/sparse/tables.rs
  • crates/akita-prover/src/protocol/extension_opening_reduction/sparse/tensor_factor.rs
  • crates/akita-prover/src/protocol/extension_opening_reduction/sparse/term.rs
  • crates/akita-prover/src/protocol/extension_opening_reduction/sparse/tests.rs
  • crates/akita-prover/src/protocol/extension_opening_reduction/sparse/witness.rs
  • crates/akita-prover/src/protocol/extension_opening_reduction/tables.rs
  • crates/akita-prover/src/protocol/extension_opening_reduction/term.rs
  • crates/akita-types/src/extension_opening_reduction.rs

Consider updating:

  • book/src/foundations/extension-opening-reduction.md
  • book/src/how/proving/extension-opening-reduction.md
  • book/src/how/proving/fold-path.md
  • specs/archive/2026-Q3/eor-*.md
  • specs/archive/2026-Q3/extension-field-*.md
  • specs/archive/2026-Q3/protocol-field-geometry-cutover.md

pcs-umbrella

Public PCS API, examples, integration tests

Code paths touched:

  • crates/akita-pcs/Cargo.toml
  • crates/akita-pcs/benches/extension_opening_reduction.rs

Consider updating:

  • book/src/usage/**
  • README.md

Per-PR checklist: spec Status / acceptance criteria; book owning page; AGENTS.md if contracts changed; archive spec after fold.

@github-actions

github-actions Bot commented Aug 12, 2026

Copy link
Copy Markdown

PCS Profile Benchmark

13 of 13 profiles passed.

Times are medians of 3 measured runs after 1 discarded warmup run. Peak RSS is the largest measured value.

Each sample verifies the same proof first with the configured multi-threaded pool and then with one thread. Both timings reuse the same verifier setup.

Merge-base comparisons are available for 13 of 13 profiles. For matching profiles, the head and merge-base binaries ran interleaved on the same runner.

Benchmark shards

CI shard Profiles
1-fp32-base Fp32 dense nv30, direct setup check
Fp32 one-hot nv34, direct setup check
2-fp64-base Fp64 dense nv29, direct setup check
Fp64 one-hot nv35, direct setup check
3-fp128-base Fp128 dense nv28, direct setup check
Fp128 one-hot nv36, direct setup check
Fp128 one-hot nv36, recursive setup check
4-multi-group-direct Fp128 multi-group, direct setup check
5-multi-group-recursive Fp128 multi-group, recursive setup check
6-multi-group-recursive-w8r2 Fp128 multi-group W8R2, recursive setup check
7-distributed Fp128 one-hot nv32 W2R2, direct setup check
Fp128 one-hot nv32 W4R2, direct setup check
Fp128 one-hot nv32 W8R2, direct setup check

Public opening statements

Public opening statement Profiles
Over Fp32, one committed 30 variable multilinear polynomial with 2^30 coefficients is opened at one 30 coordinate point. Fp32 dense nv30, direct setup check
Over Fp32, one committed 34 variable multilinear polynomial with 2^34 coefficients is opened at one 34 coordinate point. Fp32 one-hot nv34, direct setup check
Over Fp64, one committed 29 variable multilinear polynomial with 2^29 coefficients is opened at one 29 coordinate point. Fp64 dense nv29, direct setup check
Over Fp64, one committed 35 variable multilinear polynomial with 2^35 coefficients is opened at one 35 coordinate point. Fp64 one-hot nv35, direct setup check
Over Fp128, one committed 28 variable multilinear polynomial with 2^28 coefficients is opened at one 28 coordinate point. Fp128 dense nv28, direct setup check
Over Fp128, one committed 36 variable multilinear polynomial with 2^36 coefficients is opened at one 36 coordinate point. Fp128 one-hot nv36, direct setup check
Fp128 one-hot nv36, recursive setup check
Over Fp128, 4 polynomials in 3 groups: one 16 variable polynomial at its own point, one 16 variable polynomial at its own point, and 2 34 variable polynomials at one shared point. Fp128 multi-group, direct setup check
Fp128 multi-group, recursive setup check
Over Fp128, 4 polynomials in 3 groups: one 16 variable polynomial at its own point, one 16 variable polynomial at its own point, and 2 32 variable polynomials at one shared point. Fp128 multi-group W8R2, recursive setup check
Over Fp128, one committed 32 variable multilinear polynomial with 2^32 coefficients is opened at one 32 coordinate point. Fp128 one-hot nv32 W2R2, direct setup check
Fp128 one-hot nv32 W4R2, direct setup check
Fp128 one-hot nv32 W8R2, direct setup check

One-hot profiles generate deterministic witnesses with one 1 in every consecutive chunk of 256 coefficients. This witness shape is not a separate public claim.

Direct evaluates the public setup contribution during Stage 2. Recursive carries the same check through a Stage 3 setup-product sumcheck. Both modes execute the complete fold schedule and terminal verification.

The chunked profiles W2R2, W4R2, W8R2 divide the witness relation into the stated number of exact chunks for the first two fold levels.

Generated profiles may select different A, B, and D ring dimensions at different fold levels. The short profile names omit those dimensions.

Each sample generates deterministic witnesses and opening points, prepares setup, commits, proves, serializes the proof, checks its size, prepares verifier setup, and verifies the claimed openings. It does not test malformed proofs.

Phase time

Profile Setup Commit Prove Verify, multi-threaded Verify, single-threaded
Fp32 dense nv30, direct setup check 0.063 s
+0.8%
3.116 s
-0.1%
3.834 s
-0.8%
21.1 ms
+1.9%
59.2 ms
-0.3%
Fp32 one-hot nv34, direct setup check 0.067 s
+0.2%
2.492 s
-0.7%
3.731 s
-1.9%
24.3 ms
-2.1%
81.2 ms
+0.2%
Fp64 dense nv29, direct setup check 0.110 s
+2.4%
4.491 s
-0.3%
2.450 s
+0.2%
15.2 ms
-1.3%
55.8 ms
-0.3%
Fp64 one-hot nv35, direct setup check 0.201 s
-0.1%
3.484 s
+1.0%
3.225 s
+2.3%
19.1 ms
-0.5%
100.6 ms
+0.2%
Fp128 dense nv28, direct setup check 0.108 s
-0.1%
8.277 s
-0.4%
2.821 s
-0.5%
18.5 ms
-0.8%
70.1 ms
+0.6%
Fp128 one-hot nv36, direct setup check 0.272 s
-0.2%
7.962 s
-0.5%
4.617 s
+1.4%
27.1 ms
+0.9%
160.3 ms
+0.8%
Fp128 one-hot nv36, recursive setup check 0.881 s
-1.5%
7.945 s
-0.9%
4.993 s
+2.3%
20.3 ms
+0.5%
53.2 ms
+0.8%
Fp128 multi-group, direct setup check 0.259 s
-1.4%
8.471 s
+0.6%
2.871 s
+1.0%
23.3 ms
-3.1%
143.0 ms
+0.2%
Fp128 multi-group, recursive setup check 1.132 s
+1.4%
7.967 s
-2.8%
3.389 s
-0.4%
20.0 ms
-0.2%
37.6 ms
-0.3%
Fp128 multi-group W8R2, recursive setup check 0.466 s
+3.2%
2.168 s
+2.0%
4.370 s
-0.3%
26.1 ms
+1.2%
68.0 ms
+0.1%
Fp128 one-hot nv32 W2R2, direct setup check 0.073 s
-0.6%
0.714 s
+1.4%
1.753 s
+0.7%
19.1 ms
+2.4%
58.3 ms
+0.3%
Fp128 one-hot nv32 W4R2, direct setup check 0.082 s
-0.1%
0.721 s
+3.0%
2.398 s
+1.6%
21.5 ms
-5.3%
77.1 ms
-0.1%
Fp128 one-hot nv32 W8R2, direct setup check 0.075 s
+1.8%
0.714 s
+0.9%
3.277 s
+1.3%
24.1 ms
-1.6%
80.7 ms
+0.0%

Proof size and protocol shape

Profile Fold A/B/D schedule Total proof Fold payload Terminal response Fold levels Grinding retries
Fp32 dense nv30, direct setup check 2048/256/256 → 256/256/256 → 128/128/128 68,762 bytes
+0.0%
30,220 bytes
+0.0%
38,542 bytes
+0.0%
z 20,622 · e 2,560 · t 15,360
7
+0.0%
None
Fp32 one-hot nv34, direct setup check 2048/256/256 → 256/256/256 → 128/128/128 69,193 bytes
+0.0%
26,828 bytes
+0.0%
42,365 bytes
+0.0%
z 20,861 · e 3,072 · t 18,432
7
+0.0%
None
Fp64 dense nv29, direct setup check 1024/256/64 → 128/64/64 → 64/64/64 70,755 bytes
+0.0%
24,876 bytes
+0.0%
45,879 bytes
+0.0%
z 20,791 · e 3,584 · t 21,504
7
+0.0%
None
Fp64 one-hot nv35, direct setup check 1024/256/64 → 512/256/64 → 64/64/64 71,174 bytes
+0.0%
25,276 bytes
+0.0%
45,898 bytes
+0.0%
z 20,810 · e 3,584 · t 21,504
7
+0.0%
None
Fp128 dense nv28, direct setup check 512/64/64 → 64/64/64 72,951 bytes
+0.0%
19,916 bytes
+0.0%
53,035 bytes
+0.0%
z 20,267 · e 8,192 · t 24,576
7
+0.0%
None
Fp128 one-hot nv36, direct setup check 512/64/64 → 256/64/64 → 64/64/64 73,996 bytes
+0.0%
20,892 bytes
+0.0%
53,104 bytes
+0.0%
z 20,336 · e 8,192 · t 24,576
7
+0.0%
None
Fp128 one-hot nv36, recursive setup check 512/64/64 → 256/64/64 → 64/64/64 76,414 bytes
+0.0%
23,308 bytes
+0.0%
53,106 bytes
+0.0%
z 20,338 · e 8,192 · t 24,576
7
+0.0%
None
Fp128 multi-group, direct setup check 512/64/64 → 64/64/64 73,502 bytes
+0.0%
20,444 bytes
+0.0%
53,058 bytes
+0.0%
z 20,290 · e 8,192 · t 24,576
7
+0.0%
None
Fp128 multi-group, recursive setup check 512/128/128 → 64/64/64 91,149 bytes
+0.0%
33,152 bytes
+0.0%
57,997 bytes
+0.0%
z 22,157 · e 7,168 · t 28,672
8
+0.0%
L7: 4 / 4 / 4
Fp128 multi-group W8R2, recursive setup check 256/64/64 → 64/64/64 79,497 bytes
+0.0%
26,432 bytes
+0.0%
53,065 bytes
+0.0%
z 20,297 · e 8,192 · t 24,576
8
+0.0%
None
Fp128 one-hot nv32 W2R2, direct setup check 512/64/64 → 64/64/64 73,443 bytes
+0.0%
20,332 bytes
+0.0%
53,111 bytes
+0.0%
z 20,343 · e 8,192 · t 24,576
7
+0.0%
None
Fp128 one-hot nv32 W4R2, direct setup check 512/64/64 → 256/64/64 → 64/64/64 73,966 bytes
+0.0%
20,892 bytes
+0.0%
53,074 bytes
+0.0%
z 20,306 · e 8,192 · t 24,576
7
+0.0%
None
Fp128 one-hot nv32 W8R2, direct setup check 512/64/64 → 64/64/64 74,689 bytes
+0.0%
21,596 bytes
+0.0%
53,093 bytes
+0.0%
z 20,325 · e 8,192 · t 24,576
7
+0.0%
None

Grinding retries are rejected attempts at each fold, listed in measured-run order. Zero means the first sampled nonce was accepted.

Memory and setup size

Profile Setup vector Prepared NTT cache Verifier NTT cache Peak RSS
Fp32 dense nv30, direct setup check 22.0 MiB
+0.0%
206.0 MiB
+0.0%
0.9 MiB
+0.0%
5061.8 MiB
-0.1%
Fp32 one-hot nv34, direct setup check 32.0 MiB
+0.0%
234.0 MiB
+0.0%
0.9 MiB
+0.0%
1503.2 MiB
+0.3%
Fp64 dense nv29, direct setup check 44.0 MiB
+0.0%
478.0 MiB
+0.0%
1.3 MiB
+0.0%
5338.3 MiB
+0.2%
Fp64 one-hot nv35, direct setup check 128.0 MiB
+0.0%
960.0 MiB
+0.0%
1.3 MiB
+0.0%
2688.2 MiB
-0.3%
Fp128 dense nv28, direct setup check 86.0 MiB
+0.0%
424.0 MiB
+0.0%
1.0 MiB
+0.0%
5223.5 MiB
+0.1%
Fp128 one-hot nv36, direct setup check 256.0 MiB
+0.0%
1150.0 MiB
+0.0%
1.0 MiB
+0.0%
3818.1 MiB
-0.2%
Fp128 one-hot nv36, recursive setup check 256.0 MiB
+0.0%
1174.5 MiB
+0.0%
1.0 MiB
+0.0%
3818.8 MiB
-1.0%
Fp128 multi-group, direct setup check 256.0 MiB
+0.0%
1071.9 MiB
+0.0%
1.0 MiB
+0.0%
2911.4 MiB
+0.1%
Fp128 multi-group, recursive setup check 256.0 MiB
+0.0%
1192.4 MiB
+0.0%
1.4 MiB
+0.0%
3024.9 MiB
+0.2%
Fp128 multi-group W8R2, recursive setup check 128.0 MiB
+0.0%
542.9 MiB
+0.0%
1.0 MiB
+0.0%
2619.7 MiB
+0.2%
Fp128 one-hot nv32 W2R2, direct setup check 64.0 MiB
+0.0%
267.5 MiB
+0.0%
1.0 MiB
+0.0%
939.3 MiB
-0.4%
Fp128 one-hot nv32 W4R2, direct setup check 64.0 MiB
+0.0%
301.8 MiB
+0.0%
1.0 MiB
+0.0%
1232.3 MiB
+0.2%
Fp128 one-hot nv32 W8R2, direct setup check 64.0 MiB
+0.0%
267.5 MiB
+0.0%
1.0 MiB
+0.0%
1544.9 MiB
-0.4%

Deltas are shown only for profiles with a matching merge-base case. Negative is smaller or faster.

The uploaded report.md benchmark artifact contains the detailed fold schedule, proof-size breakdown, terminal response segments, and Golomb diagnostics.

@quangvdao
quangvdao marked this pull request as ready for review August 12, 2026 01:29
@github-actions

github-actions Bot commented Aug 12, 2026

Copy link
Copy Markdown

CI test timing

  • Report generated: 2026-08-22T18:20:28Z.
  • Source: fc0fd10 on codex/prover-opt-01-suffix-eor.
  • Workflow run: 32589107128.
  • Main baseline: 610f770.
  • Previous run: 97bdb0d.

Run summary

Wall s Main wall s Main Δ Ratio Tests Skipped Failed Status
433.0 344.0 +25.9% 1.26x 1697 0 0 ok

Wall time spans 2 parallel nextest slice shards.

Slowest tests

Rank Duration s Test
1 19.6 akita-pcs::akita_fp128_e2e::fp128_dense
2 19.4 akita-planner::schedule_params::adaptive_search_tests::uniform_suffix_dp_matches_unpruned_exact_cutover_search
3 10.1 akita-pcs::scheme::tests::onehot::multi_group_root_allows_precommitted_arity_above_final_group
4 9.9 akita-pcs::scheme::tests::onehot::selective_l2::selective_l2_proof_rejects_transcript_mutations
5 5.1 akita-planner::schedule_params::adaptive_search_tests::adaptive_nv36_minimizes_first_direct_setup_before_proof_bytes
6 4.6 akita-planner::schedule_params::adaptive_search_tests::production_suffix_selects_l2_with_the_typed_response_model
7 4.6 akita-planner::schedule_params::adaptive_search_tests::adaptive_frontier_matches_unpruned_traversal_and_hand_priced_role_optima
8 4.5 akita-pcs::akita_small_field_e2e::selective_l2::fp32_nv20_shipped_terminal_route_roundtrip_and_rejections
9 4.5 akita-pcs::akita_small_field_e2e::fp32_dense
10 4.3 akita-prover::kernels::linear::tests::chunking::q128_many_blocks_digits_chunk_instead_of_unsafe_block_parallel
11 4.3 akita-pcs::akita_small_field_e2e::fp64_dense_pre
12 4.2 akita-pcs::akita_small_field_e2e::selective_l2::fp32_ext4_multiblock_l2_pcs_roundtrip_and_stage2_rejections
13 4.2 akita-pcs::setup::adaptive_dense::large_setup_batch_passes
14 4.2 akita-pcs::akita_fp128_e2e::fp128_onehot_pre
15 4.2 akita-planner::planner::totality_tests::contractive_winner_remains_selected
16 4.2 akita-pcs::akita_small_field_e2e::fp32_onehot_pre
17 4.1 akita-pcs::scheme::tests::single::verify_rejects_malformed_v_dimension_without_panicking
18 4.0 akita-pcs::scheme::tests::single::folded_payload_commitments_and_digits_stay_base_field
19 3.8 akita-planner::schedule_params::adaptive_search_tests::mixed_domain_search_beats_or_ties_uniform_d64
20 3.7 akita-pcs::akita_fp128_e2e::heterogeneous::heterogeneous_compute_backends

Regressions vs main

No per-test regressions above the threshold.

New slow tests

No new tests ≥30s vs main baseline.

@quangvdao
quangvdao force-pushed the codex/prover-opt-01-suffix-eor branch from cf985c5 to 18a1250 Compare August 20, 2026 23:26
@quangvdao quangvdao changed the title perf(prover)!: stream suffix EOR inputs refactor(eor)!: simplify recursive suffix reduction Aug 20, 2026
@github-actions github-actions Bot added spec PR contains a spec implementation PR contains implementation of a spec and removed no-spec PR has no spec file labels Aug 21, 2026
@quangvdao quangvdao changed the title refactor(eor)!: simplify recursive suffix reduction refactor(eor)!: consolidate dense reduction Aug 21, 2026
Make dense recursive suffix tables the only tensor-packed EOR representation. Remove sparse and lazy implementations, compute hooks, tests, and the synthetic benchmark while preserving the live protocol.
Pack balanced suffix digits and contract tensor columns directly from the compact witness instead of allocating a full base-field table. Reuse row iterators for dense inputs and remove temporary coordinate allocations from tensor transposition and projection.

Keep the complete suffix tensor API in its own focused module.

BREAKING CHANGE: ExtField implementors must provide allocation-free coefficient construction and access. TensorColumnSource implementors must provide a row iterator.
Remove stale sparse and suffix-only wording after the EOR rebuild. Drop the unused tensor batch bound and align the packed sum-check spec with the surviving dense implementation.
@quangvdao
quangvdao force-pushed the codex/prover-opt-01-suffix-eor branch from 871f04d to e96f50d Compare August 21, 2026 17:11
@quangvdao quangvdao changed the title refactor(eor)!: consolidate dense reduction refactor(eor)!: unify setup prefix and suffix reduction Aug 21, 2026
where
Self: 'a;

/// Yield the `width` coefficients at flat tail index `tail`.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This public trait now relies on implementors returning exactly width items, but the fold code zips the iterator into fixed-width accumulators without checking exact length. The current implementors are fine, but a short row would silently leave trailing slots unchanged and a long row would silently drop extras. If this stays public, can we document the exact-length invariant as hard and add a debug assertion/helper to catch bad implementors in tests?

return Err(AkitaError::InvalidInput(
"extension-opening reduction input claim mismatch".to_string(),
));
#[cfg(debug_assertions)]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Moving this term-derived input-claim check behind debug assertions weakens the production boundary. I understand the full scan is expensive, but in release a mismatch between transcript-bound partials and generated dense terms will now run through proving and only fail later as a final-oracle mismatch. Can we add either a diagnostic/profile-mode consistency check or a regression that deliberately corrupts term construction and proves release-style behavior still fails clearly?

}
}

let mut folded_factor = match factor {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This claims to share the dense group factor, but after the first challenge each term converts the shared factor into its own owned folded table. For a group with multiple polynomials, every term then folds the same transparent factor independently, so factor work/storage still scales with term count. Can we move the factor state to a group-level EOR object, or otherwise fold the common factor once per round and feed it to the per-witness accumulations?

{
use rayon::prelude::*;

// Coarse subtree expansion wins at the terminal suffix sizes.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This comment does not quite match the branch below. For small tables with multiple Rayon threads we still build out and run a parallel final map; only the equality-table construction stays serial. Either route small tables through the fused serial mapped path, or rewrite the comment so the intended small-table behavior is clear.

harness = false

[[bench]]
name = "extension_opening_reduction"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This PR is performance-motivated but removes the dedicated EOR benchmark target entirely. Deleting sparse coverage makes sense with the sparse path gone, but we still need a dense replacement that covers owned factors, multi-term shared factors, cylindrical padding, and fp32/fp64 extension-degree cases; otherwise regressions in the new dense/shared-factor path will be hard to see.

&CpuBackend::DEFAULT, None, view, &logical_point
)
.unwrap();
let expected_partials =

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This test is useful for setup-prefix tensor-source wiring, but it still compares against the same dense helper stack used by the implementation. The new risky path is the full mixed setup-prefix + recursive-suffix EOR: setup-prefix projection, suffix projection, shared factor construction, cylindrical padding, common rho, and final-factor replay. Can we add a protocol-level dense-oracle regression that checks input claim, per-group final factor, per-group final claim, and the batched final claim independently?

@quangvdao

Copy link
Copy Markdown
Author

@RadNi I worked through the six review comments and pushed the validated revision to my fork. I no longer have LayerZero repository access, so could you please mirror this commit onto the LayerZero PR branch?

Fork commit: quangvdao@dd0a9fb
Fork branch: https://github.com/quangvdao/akita/tree/codex/prover-opt-01-suffix-eor
LayerZero branch: codex/prover-opt-01-suffix-eor
Expected current LayerZero SHA: f35a8a98b9cc4addbdd8d4a17fab354db6ccee16
Desired tested SHA: dd0a9fbdb6dfecd2b363a7ad82ffbc8a65366a2b

This is a verified fast-forward from the current PR head. The revision:

  • makes tensor rows exact-size, documents the hard width invariant, and adds centralized debug checks plus malformed-row tests;
  • keeps the input-claim consistency check explicitly debug-only with debug_assert_eq!;
  • moves factor ownership to the EOR group so a shared factor folds once per round while retaining the fused first-witness path;
  • routes small multi-threaded evals_mapped calls through the fused serial path;
  • adds one deterministic mixed setup-prefix/recursive-suffix dense-oracle protocol test, including cylindrical padding and independent partial/final-claim checks (about 0.03s focused runtime);
  • intentionally leaves the dedicated EOR benchmark removed. We are not adding a replacement microbenchmark because the performance objective is eliminating structural scaling problems, while regression tracking remains concentrated in the existing end-to-end profile CI.

Validation passed: the full repository preflight, all four prescribed release Clippy configurations, and the complete akita-prover --tests suite (358 passed, 2 ignored), plus the focused EOR, malformed-row, and parallel eval-map regressions.

Once it is mirrored, I will verify the LayerZero head and follow up on the individual review threads.

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

Labels

implementation PR contains implementation of a spec spec PR contains a spec

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants