Skip to content

Commit

Permalink
Circuit compute_c reduce constraints (#97)
Browse files Browse the repository at this point in the history
* 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

* fix clippy lints after rust-toolchain v1.76.0
  • Loading branch information
arnaucube committed May 20, 2024
1 parent d5c1e5f commit 48947e8
Show file tree
Hide file tree
Showing 15 changed files with 221 additions and 394 deletions.
41 changes: 19 additions & 22 deletions folding-schemes/src/ccs/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
use ark_ec::CurveGroup;
use ark_ff::PrimeField;
use ark_std::log2;
use ark_std::{One, Zero};
use std::ops::Neg;

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

/// vector of matrices
pub M: Vec<SparseMatrix<C::ScalarField>>,
pub M: Vec<SparseMatrix<F>>,
/// vector of multisets
pub S: Vec<Vec<usize>>,
/// vector of coefficients
pub c: Vec<C::ScalarField>,
pub c: Vec<F>,
}

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

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

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

impl<C: CurveGroup> CCS<C> {
pub fn from_r1cs(r1cs: R1CS<C::ScalarField>) -> Self {
impl<F: PrimeField> CCS<F> {
pub fn from_r1cs(r1cs: R1CS<F>) -> Self {
let m = r1cs.A.n_rows;
let n = r1cs.A.n_cols;
CCS {
Expand All @@ -87,13 +84,13 @@ impl<C: CurveGroup> CCS<C> {
d: 2,

S: vec![vec![0, 1], vec![2]],
c: vec![C::ScalarField::one(), C::ScalarField::one().neg()],
c: vec![F::one(), F::one().neg()],
M: vec![r1cs.A, r1cs.B, r1cs.C],
}
}

pub fn to_r1cs(self) -> R1CS<C::ScalarField> {
R1CS::<C::ScalarField> {
pub fn to_r1cs(self) -> R1CS<F> {
R1CS::<F> {
l: self.l,
A: self.M[0].clone(),
B: self.M[1].clone(),
Expand All @@ -107,11 +104,11 @@ pub mod tests {
use super::*;
use crate::ccs::r1cs::tests::{get_test_r1cs, get_test_z as r1cs_get_test_z};
use ark_ff::PrimeField;
use ark_pallas::Projective;
use ark_pallas::Fr;

pub fn get_test_ccs<C: CurveGroup>() -> CCS<C> {
let r1cs = get_test_r1cs::<C::ScalarField>();
CCS::<C>::from_r1cs(r1cs)
pub fn get_test_ccs<F: PrimeField>() -> CCS<F> {
let r1cs = get_test_r1cs::<F>();
CCS::<F>::from_r1cs(r1cs)
}
pub fn get_test_z<F: PrimeField>(input: usize) -> Vec<F> {
r1cs_get_test_z(input)
Expand All @@ -120,7 +117,7 @@ pub mod tests {
/// Test that a basic CCS relation can be satisfied
#[test]
fn test_ccs_relation() {
let ccs = get_test_ccs::<Projective>();
let ccs = get_test_ccs::<Fr>();
let z = get_test_z(3);

ccs.check_relation(&z).unwrap();
Expand Down
2 changes: 1 addition & 1 deletion folding-schemes/src/commitment/ipa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -493,7 +493,7 @@ where
/// there are some constraints saved.
#[allow(clippy::too_many_arguments)]
pub fn verify<const K: usize>(
g: &Vec<GC>, // params.generators
g: &[GC], // params.generators
h: &GC, // params.h
x: &NonNativeFieldVar<C::ScalarField, CF<C>>, // evaluation point, challenge
v: &NonNativeFieldVar<C::ScalarField, CF<C>>, // value at evaluation point
Expand Down
11 changes: 6 additions & 5 deletions folding-schemes/src/folding/circuits/nonnative/affine.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use ark_ec::{AffineRepr, CurveGroup};
use ark_ff::PrimeField;
use ark_r1cs_std::{
alloc::{AllocVar, AllocationMode},
fields::fp::FpVar,
Expand All @@ -16,7 +17,7 @@ use super::uint::{nonnative_field_to_field_elements, NonNativeUintVar};
#[derive(Debug, Clone)]
pub struct NonNativeAffineVar<C: CurveGroup>
where
<C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField,
<C as CurveGroup>::BaseField: PrimeField,
{
pub x: NonNativeUintVar<C::ScalarField>,
pub y: NonNativeUintVar<C::ScalarField>,
Expand All @@ -25,7 +26,7 @@ where
impl<C> AllocVar<C, C::ScalarField> for NonNativeAffineVar<C>
where
C: CurveGroup,
<C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField,
<C as CurveGroup>::BaseField: PrimeField,
{
fn new_variable<T: Borrow<C>>(
cs: impl Into<Namespace<C::ScalarField>>,
Expand All @@ -49,7 +50,7 @@ where

impl<C: CurveGroup> ToConstraintFieldGadget<C::ScalarField> for NonNativeAffineVar<C>
where
<C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField,
<C as CurveGroup>::BaseField: PrimeField,
{
// Used for converting `NonNativeAffineVar` to a vector of `FpVar` with minimum length in
// the circuit.
Expand All @@ -66,7 +67,7 @@ pub fn nonnative_affine_to_field_elements<C: CurveGroup>(
p: C,
) -> Result<(Vec<C::ScalarField>, Vec<C::ScalarField>), SynthesisError>
where
<C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField,
<C as CurveGroup>::BaseField: PrimeField,
{
let affine = p.into_affine();
if affine.is_zero() {
Expand All @@ -83,7 +84,7 @@ where

impl<C: CurveGroup> NonNativeAffineVar<C>
where
<C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField,
<C as CurveGroup>::BaseField: PrimeField,
{
// A wrapper of `point_to_nonnative_limbs_custom_opt` with constraints-focused optimization
// type (which is the default optimization type for arkworks' Groth16).
Expand Down
7 changes: 2 additions & 5 deletions folding-schemes/src/folding/circuits/sum_check.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
use crate::utils::espresso::sum_check::SumCheck;
use crate::utils::virtual_polynomial::VPAuxInfo;
use crate::{
transcript::{
poseidon::{PoseidonTranscript, PoseidonTranscriptVar},
TranscriptVar,
},
transcript::{poseidon::PoseidonTranscript, TranscriptVar},
utils::sum_check::{structs::IOPProof, IOPSumCheck},
};
use ark_crypto_primitives::sponge::Absorb;
Expand Down Expand Up @@ -150,7 +147,7 @@ impl<C: CurveGroup> SumCheckVerifierGadget<C> {
pub fn verify(
iop_proof_var: &IOPProofVar<C>,
poly_aux_info_var: &VPAuxInfoVar<C::ScalarField>,
transcript_var: &mut PoseidonTranscriptVar<C::ScalarField>,
transcript_var: &mut impl TranscriptVar<C::ScalarField>,
) -> Result<(Vec<FpVar<C::ScalarField>>, Vec<FpVar<C::ScalarField>>), SynthesisError> {
let mut e_vars = vec![iop_proof_var.claim.clone()];
let mut r_vars: Vec<FpVar<C::ScalarField>> = Vec::new();
Expand Down
15 changes: 7 additions & 8 deletions folding-schemes/src/folding/circuits/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ pub struct EqEvalGadget<F: PrimeField> {
impl<F: PrimeField> EqEvalGadget<F> {
/// Gadget to evaluate eq polynomial.
/// Follows the implementation of `eq_eval` found in this crate.
pub fn eq_eval(x: Vec<FpVar<F>>, y: Vec<FpVar<F>>) -> Result<FpVar<F>, SynthesisError> {
pub fn eq_eval(x: &[FpVar<F>], y: &[FpVar<F>]) -> Result<FpVar<F>, SynthesisError> {
if x.len() != y.len() {
return Err(SynthesisError::Unsatisfiable);
}
Expand All @@ -30,16 +30,15 @@ impl<F: PrimeField> EqEvalGadget<F> {

#[cfg(test)]
mod tests {

use crate::utils::virtual_polynomial::eq_eval;

use super::EqEvalGadget;
use ark_ff::Field;
use ark_pallas::Fr;
use ark_r1cs_std::{alloc::AllocVar, fields::fp::FpVar, R1CSVar};
use ark_relations::r1cs::ConstraintSystem;
use ark_std::{test_rng, UniformRand};

use super::EqEvalGadget;
use crate::utils::virtual_polynomial::eq_eval;

#[test]
pub fn test_eq_eval_gadget() {
let mut rng = test_rng();
Expand All @@ -57,19 +56,19 @@ mod tests {
.map(|y| FpVar::<Fr>::new_witness(cs.clone(), || Ok(y)).unwrap())
.collect();
let expected_eq_eval = eq_eval::<Fr>(&x_vec, &y_vec).unwrap();
let gadget_eq_eval: FpVar<Fr> = EqEvalGadget::<Fr>::eq_eval(x, y).unwrap();
let gadget_eq_eval: FpVar<Fr> = EqEvalGadget::<Fr>::eq_eval(&x, &y).unwrap();
assert_eq!(expected_eq_eval, gadget_eq_eval.value().unwrap());
}

let x: Vec<FpVar<Fr>> = vec![];
let y: Vec<FpVar<Fr>> = vec![];
let gadget_eq_eval = EqEvalGadget::<Fr>::eq_eval(x, y);
let gadget_eq_eval = EqEvalGadget::<Fr>::eq_eval(&x, &y);
assert!(gadget_eq_eval.is_err());

let x: Vec<FpVar<Fr>> = vec![];
let y: Vec<FpVar<Fr>> =
vec![FpVar::<Fr>::new_witness(cs.clone(), || Ok(&Fr::ONE)).unwrap()];
let gadget_eq_eval = EqEvalGadget::<Fr>::eq_eval(x, y);
let gadget_eq_eval = EqEvalGadget::<Fr>::eq_eval(&x, &y);
assert!(gadget_eq_eval.is_err());
}
}
41 changes: 19 additions & 22 deletions folding-schemes/src/folding/hypernova/cccs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use ark_std::Zero;
use std::ops::Add;
use std::sync::Arc;

use ark_std::{rand::Rng, UniformRand};
use ark_std::rand::Rng;

use super::utils::compute_sum_Mz;
use crate::ccs::CCS;
Expand Down Expand Up @@ -35,13 +35,17 @@ pub struct CCCS<C: CurveGroup> {
pub x: Vec<C::ScalarField>,
}

impl<C: CurveGroup> CCS<C> {
pub fn to_cccs<R: Rng>(
impl<F: PrimeField> CCS<F> {
pub fn to_cccs<R: Rng, C: CurveGroup>(
&self,
rng: &mut R,
pedersen_params: &PedersenParams<C>,
z: &[C::ScalarField],
) -> Result<(CCCS<C>, Witness<C::ScalarField>), Error> {
) -> Result<(CCCS<C>, Witness<C::ScalarField>), Error>
where
// enforce that CCS's F is the C::ScalarField
C: CurveGroup<ScalarField = F>,
{
let w: Vec<C::ScalarField> = z[(1 + self.l)..].to_vec();
let r_w = C::ScalarField::rand(rng);
let C = Pedersen::<C, true>::commit(pedersen_params, &w, &r_w)?;
Expand All @@ -57,13 +61,12 @@ impl<C: CurveGroup> CCS<C> {

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

for i in 0..self.q {
let mut prod: VirtualPolynomial<C::ScalarField> =
VirtualPolynomial::<C::ScalarField>::new(self.s);
let mut prod: VirtualPolynomial<F> = VirtualPolynomial::<F>::new(self.s);
for j in self.S[i].clone() {
let M_j = matrix_to_mle(self.M[j].clone());

Expand All @@ -74,11 +77,9 @@ impl<C: CurveGroup> CCS<C> {
// If this is the first time we are adding something to this virtual polynomial, we need to
// explicitly add the products using add_mle_list()
// XXX is this true? improve API
prod.add_mle_list([Arc::new(sum_Mz)], C::ScalarField::one())
.unwrap();
prod.add_mle_list([Arc::new(sum_Mz)], F::one()).unwrap();
} else {
prod.mul_by_mle(Arc::new(sum_Mz), C::ScalarField::one())
.unwrap();
prod.mul_by_mle(Arc::new(sum_Mz), F::one()).unwrap();
}
}
// Multiply by the product by the coefficient c_i
Expand All @@ -92,11 +93,7 @@ impl<C: CurveGroup> CCS<C> {
/// Computes Q(x) = eq(beta, x) * q(x)
/// = eq(beta, x) * \sum^q c_i * \prod_{j \in S_i} ( \sum_{y \in {0,1}^s'} M_j(x, y) * z(y) )
/// polynomial over x
pub fn compute_Q(
&self,
z: &Vec<C::ScalarField>,
beta: &[C::ScalarField],
) -> VirtualPolynomial<C::ScalarField> {
pub fn compute_Q(&self, z: &[F], beta: &[F]) -> VirtualPolynomial<F> {
let q = self.compute_q(z);
q.build_f_hat(beta).unwrap()
}
Expand All @@ -107,7 +104,7 @@ impl<C: CurveGroup> CCCS<C> {
pub fn check_relation(
&self,
pedersen_params: &PedersenParams<C>,
ccs: &CCS<C>,
ccs: &CCS<C::ScalarField>,
w: &Witness<C::ScalarField>,
) -> Result<(), Error> {
// check that C is the commitment of w. Notice that this is not verifying a Pedersen
Expand Down Expand Up @@ -139,15 +136,15 @@ pub mod tests {
use ark_std::test_rng;
use ark_std::UniformRand;

use ark_pallas::{Fr, Projective};
use ark_pallas::Fr;

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

let ccs = get_test_ccs::<Projective>();
let ccs = get_test_ccs::<Fr>();
let z = get_test_z(3);

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

let ccs: CCS<Projective> = get_test_ccs();
let ccs: CCS<Fr> = get_test_ccs();
let z = get_test_z(3);
ccs.check_relation(&z).unwrap();

Expand Down Expand Up @@ -201,7 +198,7 @@ pub mod tests {
fn test_Q_against_q() {
let mut rng = test_rng();

let ccs: CCS<Projective> = get_test_ccs();
let ccs: CCS<Fr> = get_test_ccs();
let z = get_test_z(3);
ccs.check_relation(&z).unwrap();

Expand Down
Loading

0 comments on commit 48947e8

Please sign in to comment.