Skip to content

feat: structural zero wire in R1CS layout (z = [w, 1, 0, x])#492

Open
sai-deng wants to merge 1 commit intomicrosoft:mainfrom
sai-deng:feat/structural-zero-wire
Open

feat: structural zero wire in R1CS layout (z = [w, 1, 0, x])#492
sai-deng wants to merge 1 commit intomicrosoft:mainfrom
sai-deng:feat/structural-zero-wire

Conversation

@sai-deng
Copy link
Copy Markdown
Contributor

@sai-deng sai-deng commented Apr 3, 2026

Change the R1CS vector layout from z = [w, 1, x] to z = [w, 1, 0, x],
making zero a structural part of the vector like the existing one-wire.
The ONE wire stays at its historical position num_vars (unchanged).

  • CS::one() returns Input(0) (unchanged), CS::zero() returns Input(1) (new)
  • All CS defaults initialize with 2 built-in inputs (ONE, ZERO)
  • Frontend bridge skips 2 built-in inputs instead of 1
  • R1CS core: z-vector length is num_vars + 2 + num_io, column validation adjusted
  • Spartan verifier MLE evaluates (u, 0, X) instead of (u, X)
  • Add AllocatedNum::zero() wrapping CS::zero() (zero constraints)
  • Remove alloc_zero() (was 1 aux + 1 constraint per call, 8 call sites)
  • Update all hand-built test R1CS, constraint counts, PP digests

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR updates the project’s R1CS witness vector layout to include a structural zero wire, shifting from z = [w, 1, x] to z = [w, 0, 1, x]. It threads the resulting index/length shift through the frontend constraint systems, R1CS core operations, Spartan SNARK verification logic, and updates affected tests/expected digests accordingly.

Changes:

  • Introduces CS::zero() as Input(0) and shifts CS::one() to Input(1), with CS implementations initializing built-in inputs (ZERO, ONE).
  • Updates R1CS core logic to account for the extra structural wire (vector length +2, index shifts, padding/regularity checks).
  • Removes alloc_zero() and migrates call sites to AllocatedNum::zero() (zero-cost), plus updates Spartan/Neutron/Nova components and test vectors/digests.

Reviewed changes

Copilot reviewed 22 out of 22 changed files in this pull request and generated no comments.

Show a summary per file
File Description
src/spartan/snark.rs Adjusts witness concatenation and verifier-side public IO handling to include the new structural zero wire.
src/spartan/ppsnark.rs Same as above for the preprocessing SNARK path.
src/r1cs/mod.rs Updates core R1CS shape validation, witness length checks, satisfiability checks, padding, and random sampling for the new layout.
src/nova/nifs.rs Updates hand-built R1CS test shapes to match the shifted column layout.
src/nova/mod.rs Updates expected public-parameter digest test vectors impacted by the layout change.
src/nova/circuit/r1cs.rs Replaces alloc_zero usage with AllocatedNum::zero() in absorption/selection paths.
src/nova/circuit/mod.rs Replaces base-case zero allocation with the structural zero wire; updates expected constraint counts.
src/neutron/relation.rs Updates satisfiability witness concatenation to include the structural zero wire.
src/neutron/nifs.rs Updates folding computations and benchmark matrix sizing for the new z length.
src/neutron/mod.rs Updates expected public-parameter digest test vectors impacted by the layout change.
src/neutron/circuit/relation.rs Replaces alloc_zero with AllocatedNum::zero() for allocating T = 0.
src/neutron/circuit/mod.rs Replaces base-case zero allocation with structural zero; updates expected constraint counts.
src/gadgets/utils.rs Removes alloc_zero() helper (previously created an aux + constraint per call).
src/gadgets/ecc.rs Migrates off alloc_zero(); uses structural AllocatedNum::zero() in relevant code paths.
src/frontend/util_cs/witness_cs.rs Initializes witness CS inputs with (ZERO, ONE) and updates extend() to skip both built-ins.
src/frontend/util_cs/test_cs.rs Updates test CS default inputs/names to include (ZERO, ONE).
src/frontend/test_shape_cs.rs Updates default test shape CS to include named (ZERO, ONE) inputs.
src/frontend/shape_cs.rs Updates shape CS default built-in input count to 2.
src/frontend/r1cs.rs Frontend bridge now skips 2 built-in inputs and adjusts shape num_io accordingly; updates input index mapping comments.
src/frontend/gadgets/num.rs Adds AllocatedNum::zero() wrapper for the new structural zero wire.
src/frontend/gadgets/multieq.rs Ensures MultiEq forwards CS::zero() properly.
src/frontend/constraint_system.rs Adds default CS::zero() and shifts default CS::one() to Input(1); forwards in wrappers.
Comments suppressed due to low confidence (1)

src/frontend/gadgets/num.rs:56

  • Doc comment for AllocatedNum::one() says it uses the "input-0 wire", but CS::one() now maps to Input(1) (with Input(0) reserved for ZERO). Update the comment to avoid the stale index and prevent confusion about the new z = [w, 0, 1, x] layout.
  /// Returns an `AllocatedNum` wrapping the built-in `CS::one()` variable.
  /// Costs zero constraints since it uses the input-0 wire directly.
  pub fn one<CS: ConstraintSystem<Scalar>>() -> Self {
    AllocatedNum {
      value: Some(Scalar::ONE),
      variable: CS::one(),
    }

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

src/r1cs/mod.rs Outdated
.map(|_| E::Scalar::random(&mut OsRng))
.collect::<Vec<E::Scalar>>();

// The zero wire must be zero
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Is this necessary?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

E is computed from Z here, but commit_T and is_sat_relaxed hardcode 0 at this position. A non-zero value would make E inconsistent with the folded equation.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Reordered the layout from [W, 0, 1, X] to [W, 1, 0, X] so the ONE wire stays at its historical position num_vars. This avoids changing every place that extracts u = Z[num_vars] and keeps the Spartan MLE split untouched. The zero wire is now at num_vars + 1 instead. Force-pushed with the updated commit.

@sai-deng sai-deng force-pushed the feat/structural-zero-wire branch from 94a981e to 2878fa6 Compare April 6, 2026 05:22
@sai-deng sai-deng changed the title feat: structural zero wire in R1CS layout (z = [w, 0, 1, x]) feat: structural zero wire in R1CS layout (z = [w, 1, 0, x]) Apr 6, 2026
@sai-deng sai-deng force-pushed the feat/structural-zero-wire branch from 2878fa6 to 0bd685a Compare April 6, 2026 05:38
Add a built-in zero wire at Input(1) alongside the existing one wire
at Input(0). The R1CS z-vector layout changes from [W, u, X] to
[W, u, 0, X], keeping the ONE wire at its historical position num_vars.

This gives every circuit a free CS::zero() / AllocatedNum::zero()
without allocating any auxiliary variables or enforcement constraints.
Each alloc_zero() call previously cost 1 aux variable + 1 constraint;
this change saves 8 constraints across the Nova augmented circuit.

Key changes:
- ConstraintSystem trait: add zero() returning Input(1)
- AllocatedNum::zero() wraps CS::zero(), zero cost
- Remove alloc_zero() and all 8 call sites
- R1CS z-vectors: [W, u, 0, X] with Z[num_vars+1] = 0
- Spartan MLE: public IO is (u, 0, X)
- Frontend bridge: skip first 2 inputs (ONE, ZERO) for X
- Update test R1CS column indices for I0/I1 (shifted by +1)
- Update constraint counts and PP digests
@sai-deng sai-deng force-pushed the feat/structural-zero-wire branch from 0bd685a to 62e3780 Compare April 6, 2026 05:45
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.

3 participants