Skip to content

Commit 163bf7f

Browse files
committed
Circuit compute_c reduce constraints (#97)
* migrate from CurveGroup to PrimeField in hypernova & ccs when curve whas not needed to simplify the code * refactor test of compute_c circuit to use multiple lcccs&cccs instances * refactor hypernova's compute_c circuit to reduce from `110635` to `553` constraints * apply review nits
1 parent d5c1e5f commit 163bf7f

File tree

11 files changed

+210
-383
lines changed

11 files changed

+210
-383
lines changed

folding-schemes/src/ccs/mod.rs

Lines changed: 19 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
use ark_ec::CurveGroup;
1+
use ark_ff::PrimeField;
22
use ark_std::log2;
3-
use ark_std::{One, Zero};
4-
use std::ops::Neg;
53

64
use crate::utils::vec::*;
75
use crate::Error;
@@ -12,7 +10,7 @@ use r1cs::R1CS;
1210
/// CCS represents the Customizable Constraint Systems structure defined in
1311
/// the [CCS paper](https://eprint.iacr.org/2023/552)
1412
#[derive(Debug, Clone, Eq, PartialEq)]
15-
pub struct CCS<C: CurveGroup> {
13+
pub struct CCS<F: PrimeField> {
1614
/// m: number of rows in M_i (such that M_i \in F^{m, n})
1715
pub m: usize,
1816
/// n = |z|, number of cols in M_i
@@ -31,25 +29,24 @@ pub struct CCS<C: CurveGroup> {
3129
pub s_prime: usize,
3230

3331
/// vector of matrices
34-
pub M: Vec<SparseMatrix<C::ScalarField>>,
32+
pub M: Vec<SparseMatrix<F>>,
3533
/// vector of multisets
3634
pub S: Vec<Vec<usize>>,
3735
/// vector of coefficients
38-
pub c: Vec<C::ScalarField>,
36+
pub c: Vec<F>,
3937
}
4038

41-
impl<C: CurveGroup> CCS<C> {
39+
impl<F: PrimeField> CCS<F> {
4240
/// check that a CCS structure is satisfied by a z vector. Only for testing.
43-
pub fn check_relation(&self, z: &[C::ScalarField]) -> Result<(), Error> {
44-
let mut result = vec![C::ScalarField::zero(); self.m];
41+
pub fn check_relation(&self, z: &[F]) -> Result<(), Error> {
42+
let mut result = vec![F::zero(); self.m];
4543

4644
for i in 0..self.q {
4745
// extract the needed M_j matrices out of S_i
48-
let vec_M_j: Vec<&SparseMatrix<C::ScalarField>> =
49-
self.S[i].iter().map(|j| &self.M[*j]).collect();
46+
let vec_M_j: Vec<&SparseMatrix<F>> = self.S[i].iter().map(|j| &self.M[*j]).collect();
5047

5148
// complete the hadamard chain
52-
let mut hadamard_result = vec![C::ScalarField::one(); self.m];
49+
let mut hadamard_result = vec![F::one(); self.m];
5350
for M_j in vec_M_j.into_iter() {
5451
hadamard_result = hadamard(&hadamard_result, &mat_vec_mul_sparse(M_j, z)?)?;
5552
}
@@ -72,8 +69,8 @@ impl<C: CurveGroup> CCS<C> {
7269
}
7370
}
7471

75-
impl<C: CurveGroup> CCS<C> {
76-
pub fn from_r1cs(r1cs: R1CS<C::ScalarField>) -> Self {
72+
impl<F: PrimeField> CCS<F> {
73+
pub fn from_r1cs(r1cs: R1CS<F>) -> Self {
7774
let m = r1cs.A.n_rows;
7875
let n = r1cs.A.n_cols;
7976
CCS {
@@ -87,13 +84,13 @@ impl<C: CurveGroup> CCS<C> {
8784
d: 2,
8885

8986
S: vec![vec![0, 1], vec![2]],
90-
c: vec![C::ScalarField::one(), C::ScalarField::one().neg()],
87+
c: vec![F::one(), F::one().neg()],
9188
M: vec![r1cs.A, r1cs.B, r1cs.C],
9289
}
9390
}
9491

95-
pub fn to_r1cs(self) -> R1CS<C::ScalarField> {
96-
R1CS::<C::ScalarField> {
92+
pub fn to_r1cs(self) -> R1CS<F> {
93+
R1CS::<F> {
9794
l: self.l,
9895
A: self.M[0].clone(),
9996
B: self.M[1].clone(),
@@ -107,11 +104,11 @@ pub mod tests {
107104
use super::*;
108105
use crate::ccs::r1cs::tests::{get_test_r1cs, get_test_z as r1cs_get_test_z};
109106
use ark_ff::PrimeField;
110-
use ark_pallas::Projective;
107+
use ark_pallas::Fr;
111108

112-
pub fn get_test_ccs<C: CurveGroup>() -> CCS<C> {
113-
let r1cs = get_test_r1cs::<C::ScalarField>();
114-
CCS::<C>::from_r1cs(r1cs)
109+
pub fn get_test_ccs<F: PrimeField>() -> CCS<F> {
110+
let r1cs = get_test_r1cs::<F>();
111+
CCS::<F>::from_r1cs(r1cs)
115112
}
116113
pub fn get_test_z<F: PrimeField>(input: usize) -> Vec<F> {
117114
r1cs_get_test_z(input)
@@ -120,7 +117,7 @@ pub mod tests {
120117
/// Test that a basic CCS relation can be satisfied
121118
#[test]
122119
fn test_ccs_relation() {
123-
let ccs = get_test_ccs::<Projective>();
120+
let ccs = get_test_ccs::<Fr>();
124121
let z = get_test_z(3);
125122

126123
ccs.check_relation(&z).unwrap();

folding-schemes/src/folding/circuits/nonnative/affine.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use ark_ec::{AffineRepr, CurveGroup};
2+
use ark_ff::PrimeField;
23
use ark_r1cs_std::{
34
alloc::{AllocVar, AllocationMode},
45
fields::fp::FpVar,
@@ -16,7 +17,7 @@ use super::uint::{nonnative_field_to_field_elements, NonNativeUintVar};
1617
#[derive(Debug, Clone)]
1718
pub struct NonNativeAffineVar<C: CurveGroup>
1819
where
19-
<C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField,
20+
<C as CurveGroup>::BaseField: PrimeField,
2021
{
2122
pub x: NonNativeUintVar<C::ScalarField>,
2223
pub y: NonNativeUintVar<C::ScalarField>,
@@ -25,7 +26,7 @@ where
2526
impl<C> AllocVar<C, C::ScalarField> for NonNativeAffineVar<C>
2627
where
2728
C: CurveGroup,
28-
<C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField,
29+
<C as CurveGroup>::BaseField: PrimeField,
2930
{
3031
fn new_variable<T: Borrow<C>>(
3132
cs: impl Into<Namespace<C::ScalarField>>,
@@ -49,7 +50,7 @@ where
4950

5051
impl<C: CurveGroup> ToConstraintFieldGadget<C::ScalarField> for NonNativeAffineVar<C>
5152
where
52-
<C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField,
53+
<C as CurveGroup>::BaseField: PrimeField,
5354
{
5455
// Used for converting `NonNativeAffineVar` to a vector of `FpVar` with minimum length in
5556
// the circuit.
@@ -66,7 +67,7 @@ pub fn nonnative_affine_to_field_elements<C: CurveGroup>(
6667
p: C,
6768
) -> Result<(Vec<C::ScalarField>, Vec<C::ScalarField>), SynthesisError>
6869
where
69-
<C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField,
70+
<C as CurveGroup>::BaseField: PrimeField,
7071
{
7172
let affine = p.into_affine();
7273
if affine.is_zero() {
@@ -83,7 +84,7 @@ where
8384

8485
impl<C: CurveGroup> NonNativeAffineVar<C>
8586
where
86-
<C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField,
87+
<C as CurveGroup>::BaseField: PrimeField,
8788
{
8889
// A wrapper of `point_to_nonnative_limbs_custom_opt` with constraints-focused optimization
8990
// type (which is the default optimization type for arkworks' Groth16).

folding-schemes/src/folding/circuits/sum_check.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
use crate::utils::espresso::sum_check::SumCheck;
22
use crate::utils::virtual_polynomial::VPAuxInfo;
33
use crate::{
4-
transcript::{
5-
poseidon::{PoseidonTranscript, PoseidonTranscriptVar},
6-
TranscriptVar,
7-
},
4+
transcript::{poseidon::PoseidonTranscript, TranscriptVar},
85
utils::sum_check::{structs::IOPProof, IOPSumCheck},
96
};
107
use ark_crypto_primitives::sponge::Absorb;
@@ -150,7 +147,7 @@ impl<C: CurveGroup> SumCheckVerifierGadget<C> {
150147
pub fn verify(
151148
iop_proof_var: &IOPProofVar<C>,
152149
poly_aux_info_var: &VPAuxInfoVar<C::ScalarField>,
153-
transcript_var: &mut PoseidonTranscriptVar<C::ScalarField>,
150+
transcript_var: &mut impl TranscriptVar<C::ScalarField>,
154151
) -> Result<(Vec<FpVar<C::ScalarField>>, Vec<FpVar<C::ScalarField>>), SynthesisError> {
155152
let mut e_vars = vec![iop_proof_var.claim.clone()];
156153
let mut r_vars: Vec<FpVar<C::ScalarField>> = Vec::new();

folding-schemes/src/folding/circuits/utils.rs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ pub struct EqEvalGadget<F: PrimeField> {
1212
impl<F: PrimeField> EqEvalGadget<F> {
1313
/// Gadget to evaluate eq polynomial.
1414
/// Follows the implementation of `eq_eval` found in this crate.
15-
pub fn eq_eval(x: Vec<FpVar<F>>, y: Vec<FpVar<F>>) -> Result<FpVar<F>, SynthesisError> {
15+
pub fn eq_eval(x: &[FpVar<F>], y: &[FpVar<F>]) -> Result<FpVar<F>, SynthesisError> {
1616
if x.len() != y.len() {
1717
return Err(SynthesisError::Unsatisfiable);
1818
}
@@ -30,16 +30,15 @@ impl<F: PrimeField> EqEvalGadget<F> {
3030

3131
#[cfg(test)]
3232
mod tests {
33-
34-
use crate::utils::virtual_polynomial::eq_eval;
35-
36-
use super::EqEvalGadget;
3733
use ark_ff::Field;
3834
use ark_pallas::Fr;
3935
use ark_r1cs_std::{alloc::AllocVar, fields::fp::FpVar, R1CSVar};
4036
use ark_relations::r1cs::ConstraintSystem;
4137
use ark_std::{test_rng, UniformRand};
4238

39+
use super::EqEvalGadget;
40+
use crate::utils::virtual_polynomial::eq_eval;
41+
4342
#[test]
4443
pub fn test_eq_eval_gadget() {
4544
let mut rng = test_rng();
@@ -57,19 +56,19 @@ mod tests {
5756
.map(|y| FpVar::<Fr>::new_witness(cs.clone(), || Ok(y)).unwrap())
5857
.collect();
5958
let expected_eq_eval = eq_eval::<Fr>(&x_vec, &y_vec).unwrap();
60-
let gadget_eq_eval: FpVar<Fr> = EqEvalGadget::<Fr>::eq_eval(x, y).unwrap();
59+
let gadget_eq_eval: FpVar<Fr> = EqEvalGadget::<Fr>::eq_eval(&x, &y).unwrap();
6160
assert_eq!(expected_eq_eval, gadget_eq_eval.value().unwrap());
6261
}
6362

6463
let x: Vec<FpVar<Fr>> = vec![];
6564
let y: Vec<FpVar<Fr>> = vec![];
66-
let gadget_eq_eval = EqEvalGadget::<Fr>::eq_eval(x, y);
65+
let gadget_eq_eval = EqEvalGadget::<Fr>::eq_eval(&x, &y);
6766
assert!(gadget_eq_eval.is_err());
6867

6968
let x: Vec<FpVar<Fr>> = vec![];
7069
let y: Vec<FpVar<Fr>> =
7170
vec![FpVar::<Fr>::new_witness(cs.clone(), || Ok(&Fr::ONE)).unwrap()];
72-
let gadget_eq_eval = EqEvalGadget::<Fr>::eq_eval(x, y);
71+
let gadget_eq_eval = EqEvalGadget::<Fr>::eq_eval(&x, &y);
7372
assert!(gadget_eq_eval.is_err());
7473
}
7574
}

folding-schemes/src/folding/hypernova/cccs.rs

Lines changed: 19 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use ark_std::Zero;
55
use std::ops::Add;
66
use std::sync::Arc;
77

8-
use ark_std::{rand::Rng, UniformRand};
8+
use ark_std::rand::Rng;
99

1010
use super::utils::compute_sum_Mz;
1111
use crate::ccs::CCS;
@@ -35,13 +35,17 @@ pub struct CCCS<C: CurveGroup> {
3535
pub x: Vec<C::ScalarField>,
3636
}
3737

38-
impl<C: CurveGroup> CCS<C> {
39-
pub fn to_cccs<R: Rng>(
38+
impl<F: PrimeField> CCS<F> {
39+
pub fn to_cccs<R: Rng, C: CurveGroup>(
4040
&self,
4141
rng: &mut R,
4242
pedersen_params: &PedersenParams<C>,
4343
z: &[C::ScalarField],
44-
) -> Result<(CCCS<C>, Witness<C::ScalarField>), Error> {
44+
) -> Result<(CCCS<C>, Witness<C::ScalarField>), Error>
45+
where
46+
// enforce that CCS's F is the C::ScalarField
47+
C: CurveGroup<ScalarField = F>,
48+
{
4549
let w: Vec<C::ScalarField> = z[(1 + self.l)..].to_vec();
4650
let r_w = C::ScalarField::rand(rng);
4751
let C = Pedersen::<C, true>::commit(pedersen_params, &w, &r_w)?;
@@ -57,13 +61,12 @@ impl<C: CurveGroup> CCS<C> {
5761

5862
/// Computes q(x) = \sum^q c_i * \prod_{j \in S_i} ( \sum_{y \in {0,1}^s'} M_j(x, y) * z(y) )
5963
/// polynomial over x
60-
pub fn compute_q(&self, z: &Vec<C::ScalarField>) -> VirtualPolynomial<C::ScalarField> {
64+
pub fn compute_q(&self, z: &Vec<F>) -> VirtualPolynomial<F> {
6165
let z_mle = vec_to_mle(self.s_prime, z);
62-
let mut q = VirtualPolynomial::<C::ScalarField>::new(self.s);
66+
let mut q = VirtualPolynomial::<F>::new(self.s);
6367

6468
for i in 0..self.q {
65-
let mut prod: VirtualPolynomial<C::ScalarField> =
66-
VirtualPolynomial::<C::ScalarField>::new(self.s);
69+
let mut prod: VirtualPolynomial<F> = VirtualPolynomial::<F>::new(self.s);
6770
for j in self.S[i].clone() {
6871
let M_j = matrix_to_mle(self.M[j].clone());
6972

@@ -74,11 +77,9 @@ impl<C: CurveGroup> CCS<C> {
7477
// If this is the first time we are adding something to this virtual polynomial, we need to
7578
// explicitly add the products using add_mle_list()
7679
// XXX is this true? improve API
77-
prod.add_mle_list([Arc::new(sum_Mz)], C::ScalarField::one())
78-
.unwrap();
80+
prod.add_mle_list([Arc::new(sum_Mz)], F::one()).unwrap();
7981
} else {
80-
prod.mul_by_mle(Arc::new(sum_Mz), C::ScalarField::one())
81-
.unwrap();
82+
prod.mul_by_mle(Arc::new(sum_Mz), F::one()).unwrap();
8283
}
8384
}
8485
// Multiply by the product by the coefficient c_i
@@ -92,11 +93,7 @@ impl<C: CurveGroup> CCS<C> {
9293
/// Computes Q(x) = eq(beta, x) * q(x)
9394
/// = eq(beta, x) * \sum^q c_i * \prod_{j \in S_i} ( \sum_{y \in {0,1}^s'} M_j(x, y) * z(y) )
9495
/// polynomial over x
95-
pub fn compute_Q(
96-
&self,
97-
z: &Vec<C::ScalarField>,
98-
beta: &[C::ScalarField],
99-
) -> VirtualPolynomial<C::ScalarField> {
96+
pub fn compute_Q(&self, z: &Vec<F>, beta: &[F]) -> VirtualPolynomial<F> {
10097
let q = self.compute_q(z);
10198
q.build_f_hat(beta).unwrap()
10299
}
@@ -107,7 +104,7 @@ impl<C: CurveGroup> CCCS<C> {
107104
pub fn check_relation(
108105
&self,
109106
pedersen_params: &PedersenParams<C>,
110-
ccs: &CCS<C>,
107+
ccs: &CCS<C::ScalarField>,
111108
w: &Witness<C::ScalarField>,
112109
) -> Result<(), Error> {
113110
// check that C is the commitment of w. Notice that this is not verifying a Pedersen
@@ -139,15 +136,15 @@ pub mod tests {
139136
use ark_std::test_rng;
140137
use ark_std::UniformRand;
141138

142-
use ark_pallas::{Fr, Projective};
139+
use ark_pallas::Fr;
143140

144141
/// Do some sanity checks on q(x). It's a multivariable polynomial and it should evaluate to zero inside the
145142
/// hypercube, but to not-zero outside the hypercube.
146143
#[test]
147144
fn test_compute_q() {
148145
let mut rng = test_rng();
149146

150-
let ccs = get_test_ccs::<Projective>();
147+
let ccs = get_test_ccs::<Fr>();
151148
let z = get_test_z(3);
152149

153150
let q = ccs.compute_q(&z);
@@ -167,7 +164,7 @@ pub mod tests {
167164
fn test_compute_Q() {
168165
let mut rng = test_rng();
169166

170-
let ccs: CCS<Projective> = get_test_ccs();
167+
let ccs: CCS<Fr> = get_test_ccs();
171168
let z = get_test_z(3);
172169
ccs.check_relation(&z).unwrap();
173170

@@ -201,7 +198,7 @@ pub mod tests {
201198
fn test_Q_against_q() {
202199
let mut rng = test_rng();
203200

204-
let ccs: CCS<Projective> = get_test_ccs();
201+
let ccs: CCS<Fr> = get_test_ccs();
205202
let z = get_test_z(3);
206203
ccs.check_relation(&z).unwrap();
207204

0 commit comments

Comments
 (0)