Skip to content

Commit

Permalink
Optimize the HyperNova compute_g, compute_Ls and to_lcccs methods
Browse files Browse the repository at this point in the history
- Optimize the HyperNova `compute_g`, `compute_Ls` and `to_lcccs` methods
- in some tests, increase the size of test matrices to a more real-world size.

| method                | matrix size   | old version seconds | new version seconds |
| --------------------- | ------------- | ------------------- | ------------------- |
| compute_g             | 2^8 x 2^8     | 16.48               | 0.16                |
| compute_g             | 2^9 x 2^9     | 122.62              | 0.51                |
| compute_Ls            | 2^8 x 2^8     | 9.73                | 0.11                |
| compute_Ls            | 2^9 x 2^9     | 67.16               | 0.38                |
| to_lcccs              | 2^8 x 2^8     | 4.56                | 0.21                |
| to_lcccs              | 2^9 x 2^9     | 67.65               | 0.84                |

- Note: 2^16 x 2^16 is the actual size (upperbound) of the circuit,
  which is not represented in the table since it was needing too much
  ram to even be computed.
  • Loading branch information
arnaucube committed Jun 4, 2024
1 parent 59b8bdb commit 41f9d23
Show file tree
Hide file tree
Showing 12 changed files with 261 additions and 140 deletions.
4 changes: 2 additions & 2 deletions folding-schemes/src/ccs/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use ark_ff::PrimeField;
use ark_std::log2;

use crate::utils::vec::*;
use crate::utils::vec::{hadamard, mat_vec_mul, vec_add, vec_scalar_mul, SparseMatrix};
use crate::Error;

pub mod r1cs;
Expand Down Expand Up @@ -48,7 +48,7 @@ impl<F: PrimeField> CCS<F> {
// complete the hadamard chain
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)?)?;
hadamard_result = hadamard(&hadamard_result, &mat_vec_mul(M_j, z)?)?;
}

// multiply by the coefficient of this step
Expand Down
26 changes: 18 additions & 8 deletions folding-schemes/src/ccs/r1cs.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use ark_ff::PrimeField;
use ark_relations::r1cs::ConstraintSystem;
use ark_std::rand::Rng;

use crate::utils::vec::*;
use crate::utils::vec::{hadamard, mat_vec_mul, vec_add, vec_scalar_mul, SparseMatrix};
use crate::Error;
use ark_relations::r1cs::ConstraintSystem;

#[derive(Debug, Clone, Eq, PartialEq)]
pub struct R1CS<F: PrimeField> {
Expand All @@ -13,16 +14,25 @@ pub struct R1CS<F: PrimeField> {
}

impl<F: PrimeField> R1CS<F> {
pub fn rand<R: Rng>(rng: &mut R, n_rows: usize, n_cols: usize) -> Self {
Self {
l: 1,
A: SparseMatrix::rand(rng, n_rows, n_cols),
B: SparseMatrix::rand(rng, n_rows, n_cols),
C: SparseMatrix::rand(rng, n_rows, n_cols),
}
}

/// returns a tuple containing (w, x) (witness and public inputs respectively)
pub fn split_z(&self, z: &[F]) -> (Vec<F>, Vec<F>) {
(z[self.l + 1..].to_vec(), z[1..self.l + 1].to_vec())
}

/// check that a R1CS structure is satisfied by a z vector. Only for testing.
pub fn check_relation(&self, z: &[F]) -> Result<(), Error> {
let Az = mat_vec_mul_sparse(&self.A, z)?;
let Bz = mat_vec_mul_sparse(&self.B, z)?;
let Cz = mat_vec_mul_sparse(&self.C, z)?;
let Az = mat_vec_mul(&self.A, z)?;
let Bz = mat_vec_mul(&self.B, z)?;
let Cz = mat_vec_mul(&self.C, z)?;
let AzBz = hadamard(&Az, &Bz)?;
if AzBz != Cz {
return Err(Error::NotSatisfied);
Expand Down Expand Up @@ -58,9 +68,9 @@ pub struct RelaxedR1CS<F: PrimeField> {
impl<F: PrimeField> RelaxedR1CS<F> {
/// check that a RelaxedR1CS structure is satisfied by a z vector. Only for testing.
pub fn check_relation(&self, z: &[F]) -> Result<(), Error> {
let Az = mat_vec_mul_sparse(&self.A, z)?;
let Bz = mat_vec_mul_sparse(&self.B, z)?;
let Cz = mat_vec_mul_sparse(&self.C, z)?;
let Az = mat_vec_mul(&self.A, z)?;
let Bz = mat_vec_mul(&self.B, z)?;
let Cz = mat_vec_mul(&self.C, z)?;
let uCz = vec_scalar_mul(&Cz, &self.u);
let uCzE = vec_add(&uCz, &self.E)?;
let AzBz = hadamard(&Az, &Bz)?;
Expand Down
8 changes: 4 additions & 4 deletions folding-schemes/src/folding/hypernova/cccs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ use crate::commitment::{
CommitmentScheme,
};
use crate::utils::hypercube::BooleanHypercube;
use crate::utils::mle::matrix_to_mle;
use crate::utils::mle::vec_to_mle;
use crate::utils::mle::matrix_to_dense_mle;
use crate::utils::mle::vec_to_dense_mle;
use crate::utils::virtual_polynomial::VirtualPolynomial;
use crate::Error;

Expand Down Expand Up @@ -62,13 +62,13 @@ impl<F: PrimeField> CCS<F> {
/// 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: &[F]) -> VirtualPolynomial<F> {
let z_mle = vec_to_mle(self.s_prime, z);
let z_mle = vec_to_dense_mle(self.s_prime, z);
let mut q = VirtualPolynomial::<F>::new(self.s);

for i in 0..self.q {
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());
let M_j = matrix_to_dense_mle(self.M[j].clone());

let sum_Mz = compute_sum_Mz(M_j, &z_mle, self.s_prime);

Expand Down
104 changes: 62 additions & 42 deletions folding-schemes/src/folding/hypernova/lcccs.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
use ark_ec::CurveGroup;
use ark_ff::PrimeField;
use ark_poly::DenseMultilinearExtension;
use ark_std::One;
use std::sync::Arc;
use ark_poly::MultilinearExtension;

use ark_std::rand::Rng;

use super::cccs::Witness;
use super::utils::{compute_all_sum_Mz_evals, compute_sum_Mz};
use super::utils::compute_all_sum_Mz_evals;
use crate::ccs::CCS;
use crate::commitment::{
pedersen::{Params as PedersenParams, Pedersen},
CommitmentScheme,
};
use crate::utils::mle::{matrix_to_mle, vec_to_mle};
use crate::utils::virtual_polynomial::VirtualPolynomial;
use crate::utils::mle::dense_vec_to_dense_mle;
use crate::utils::vec::mat_vec_mul;
use crate::Error;

/// Linearized Committed CCS instance
Expand All @@ -35,9 +34,6 @@ pub struct LCCCS<C: CurveGroup> {
impl<F: PrimeField> CCS<F> {
/// Compute v_j values of the linearized committed CCS form
/// Given `r`, compute: \sum_{y \in {0,1}^s'} M_j(r, y) * z(y)
fn compute_v_j(&self, z: &[F], r: &[F]) -> Vec<F> {
compute_all_sum_Mz_evals(&self.M, z, r, self.s_prime)
}
pub fn to_lcccs<R: Rng, C: CurveGroup>(
&self,
Expand All @@ -54,7 +50,22 @@ impl<F: PrimeField> CCS<F> {
let C = Pedersen::<C, true>::commit(pedersen_params, &w, &r_w)?;

let r_x: Vec<C::ScalarField> = (0..self.s).map(|_| C::ScalarField::rand(rng)).collect();
let v = self.compute_v_j(z, &r_x);

let mut Mzs = vec![];
for M_j in self.M.iter() {
Mzs.push(dense_vec_to_dense_mle(self.s, &mat_vec_mul(M_j, z)?));
}
let Mzs: Vec<DenseMultilinearExtension<F>> = self
.M
.iter()
.map(|M_j| Ok(dense_vec_to_dense_mle(self.s, &mat_vec_mul(M_j, z)?)))
.collect::<Result<_, Error>>()?;

// compute v_j
let v: Vec<F> = Mzs
.iter()
.map(|Mz| Mz.evaluate(&r_x).ok_or(Error::EvaluationFail))
.collect::<Result<_, Error>>()?;

Ok((
LCCCS::<C> {
Expand All @@ -70,29 +81,6 @@ impl<F: PrimeField> CCS<F> {
}

impl<C: CurveGroup> LCCCS<C> {
/// Compute all L_j(x) polynomials
pub fn compute_Ls(
&self,
ccs: &CCS<C::ScalarField>,
z: &[C::ScalarField],
) -> Vec<VirtualPolynomial<C::ScalarField>> {
let z_mle = vec_to_mle(ccs.s_prime, z);
// Convert all matrices to MLE
let M_x_y_mle: Vec<DenseMultilinearExtension<C::ScalarField>> =
ccs.M.clone().into_iter().map(matrix_to_mle).collect();

let mut vec_L_j_x = Vec::with_capacity(ccs.t);
for M_j in M_x_y_mle {
let sum_Mz = compute_sum_Mz(M_j, &z_mle, ccs.s_prime);
let sum_Mz_virtual =
VirtualPolynomial::new_from_mle(&Arc::new(sum_Mz.clone()), C::ScalarField::one());
let L_j_x = sum_Mz_virtual.build_f_hat(&self.r_x).unwrap();
vec_L_j_x.push(L_j_x);
}

vec_L_j_x
}

/// Perform the check of the LCCCS instance described at section 4.2
pub fn check_relation(
&self,
Expand All @@ -118,31 +106,64 @@ impl<C: CurveGroup> LCCCS<C> {

#[cfg(test)]
pub mod tests {
use super::*;
use ark_pallas::{Fr, Projective};
use ark_std::test_rng;
use ark_std::One;
use ark_std::UniformRand;
use ark_std::Zero;
use std::sync::Arc;

use crate::ccs::tests::{get_test_ccs, get_test_z};
use super::*;
use crate::ccs::{
r1cs::R1CS,
tests::{get_test_ccs, get_test_z},
};
use crate::utils::hypercube::BooleanHypercube;
use ark_std::test_rng;
use crate::utils::virtual_polynomial::{build_eq_x_r_vec, VirtualPolynomial};

use ark_pallas::{Fr, Projective};
// method for testing
pub fn compute_Ls<C: CurveGroup>(
ccs: &CCS<C::ScalarField>,
lcccs: &LCCCS<C>,
z: &[C::ScalarField],
) -> Vec<VirtualPolynomial<C::ScalarField>> {
let eq_rx = build_eq_x_r_vec(&lcccs.r_x).unwrap();
let eq_rx_mle = dense_vec_to_dense_mle(ccs.s, &eq_rx);

let mut Ls = Vec::with_capacity(ccs.t);
for M_j in ccs.M.iter() {
let mut L = VirtualPolynomial::<C::ScalarField>::new(ccs.s);
let mut Mz = vec![dense_vec_to_dense_mle(ccs.s, &mat_vec_mul(M_j, z).unwrap())];
Mz.push(eq_rx_mle.clone());
L.add_mle_list(
Mz.iter().map(|v| Arc::new(v.clone())),
C::ScalarField::one(),
)
.unwrap();
Ls.push(L);
}
Ls
}

#[test]
/// Test linearized CCCS v_j against the L_j(x)
fn test_lcccs_v_j() {
let mut rng = test_rng();

let ccs = get_test_ccs();
let z = get_test_z(3);
ccs.check_relation(&z.clone()).unwrap();
let n_rows = 2_u32.pow(5) as usize;
let n_cols = 2_u32.pow(5) as usize;
let r1cs = R1CS::rand(&mut rng, n_rows, n_cols);
let ccs = CCS::from_r1cs(r1cs);
let z: Vec<Fr> = (0..n_cols).map(|_| Fr::rand(&mut rng)).collect();

let (pedersen_params, _) =
Pedersen::<Projective>::setup(&mut rng, ccs.n - ccs.l - 1).unwrap();

let (lcccs, _) = ccs.to_lcccs(&mut rng, &pedersen_params, &z).unwrap();
// with our test vector coming from R1CS, v should have length 3
assert_eq!(lcccs.v.len(), 3);

let vec_L_j_x = lcccs.compute_Ls(&ccs, &z);
let vec_L_j_x = compute_Ls(&ccs, &lcccs, &z);
assert_eq!(vec_L_j_x.len(), lcccs.v.len());

for (v_i, L_j_x) in lcccs.v.into_iter().zip(vec_L_j_x) {
Expand Down Expand Up @@ -175,7 +196,7 @@ pub mod tests {
assert_eq!(lcccs.v.len(), 3);

// Bad compute L_j(x) with the bad z
let vec_L_j_x = lcccs.compute_Ls(&ccs, &bad_z);
let vec_L_j_x = compute_Ls(&ccs, &lcccs, &bad_z);
assert_eq!(vec_L_j_x.len(), lcccs.v.len());

// Make sure that the LCCCS is not satisfied given these L_j(x)
Expand All @@ -189,7 +210,6 @@ pub mod tests {
satisfied = false;
}
}

assert!(!satisfied);
}
}
2 changes: 1 addition & 1 deletion folding-schemes/src/folding/hypernova/nimfs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ where
let beta: Vec<C::ScalarField> = transcript.get_challenges(ccs.s);

// Compute g(x)
let g = compute_g(ccs, running_instances, &z_lcccs, &z_cccs, gamma, &beta);
let g = compute_g(ccs, running_instances, &z_lcccs, &z_cccs, gamma, &beta)?;

// Step 3: Run the sumcheck prover
let sumcheck_proof = IOPSumCheck::<C, T>::prove(&g, transcript)
Expand Down
Loading

0 comments on commit 41f9d23

Please sign in to comment.