From 80b494860fee5ddd5882659f4bff8b87d50ac7ae Mon Sep 17 00:00:00 2001 From: arnaucube Date: Mon, 3 Jun 2024 23:27:33 +0200 Subject: [PATCH] Optimize HyperNova's `compute_sigmas_thetas` and `compute_Q` | method | matrix size | old version seconds | new version seconds | | ------------- | ------------- | ------------------- | ------------------- | | compute_sigmas_thetas | 2^8 x 2^8 | 12.86 | 0.13 | | compute_sigmas_thetas | 2^9 x 2^9 | 100.01 | 0.51 | | compute_Q | 2^8 x 2^8 | 4.49 | 0.07 | | compute_Q | 2^9 x 2^9 | 70.77 | 0.55 | --- folding-schemes/src/folding/hypernova/cccs.rs | 28 ++++++++++++++-- .../src/folding/hypernova/circuits.rs | 4 +-- .../src/folding/hypernova/nimfs.rs | 6 ++-- .../src/folding/hypernova/utils.rs | 33 ++++++++++++++----- folding-schemes/src/utils/mle.rs | 6 ++-- 5 files changed, 58 insertions(+), 19 deletions(-) diff --git a/folding-schemes/src/folding/hypernova/cccs.rs b/folding-schemes/src/folding/hypernova/cccs.rs index dc100213..15cb3adf 100644 --- a/folding-schemes/src/folding/hypernova/cccs.rs +++ b/folding-schemes/src/folding/hypernova/cccs.rs @@ -14,9 +14,11 @@ use crate::commitment::{ CommitmentScheme, }; use crate::utils::hypercube::BooleanHypercube; +use crate::utils::mle::dense_vec_to_dense_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::utils::vec::mat_vec_mul; +use crate::utils::virtual_polynomial::{build_eq_x_r_vec, VirtualPolynomial}; use crate::Error; /// Witness for the LCCCS & CCCS, containing the w vector, and the r_w used as randomness in the Pedersen commitment. @@ -90,12 +92,32 @@ impl CCS { q } + pub fn compute_Q_OLD(&self, z: &[F], beta: &[F]) -> VirtualPolynomial { + let q = self.compute_q(z); + q.build_f_hat(beta).unwrap() + } + /// 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: &[F], beta: &[F]) -> VirtualPolynomial { - let q = self.compute_q(z); - q.build_f_hat(beta).unwrap() + let eq_beta = build_eq_x_r_vec(beta).unwrap(); + let eq_beta_mle = dense_vec_to_dense_mle(self.s, &eq_beta); + + let mut Q = VirtualPolynomial::::new(self.s); + for i in 0..self.q { + let mut Q_k = vec![]; + for &j in self.S[i].iter() { + Q_k.push(dense_vec_to_dense_mle( + self.s, + &mat_vec_mul(&self.M[j], z).unwrap(), + )); + } + Q_k.push(eq_beta_mle.clone()); + Q.add_mle_list(Q_k.iter().map(|v| Arc::new(v.clone())), self.c[i]) + .unwrap(); + } + Q } } diff --git a/folding-schemes/src/folding/hypernova/circuits.rs b/folding-schemes/src/folding/hypernova/circuits.rs index 7ebdeef6..807193be 100644 --- a/folding-schemes/src/folding/hypernova/circuits.rs +++ b/folding-schemes/src/folding/hypernova/circuits.rs @@ -361,7 +361,7 @@ mod tests { commitment::{pedersen::Pedersen, CommitmentScheme}, folding::hypernova::{ nimfs::NIMFS, - utils::{compute_c, compute_sigmas_and_thetas}, + utils::{compute_c, compute_sigmas_thetas}, }, transcript::{ poseidon::{poseidon_canonical_config, PoseidonTranscript, PoseidonTranscriptVar}, @@ -409,7 +409,7 @@ mod tests { cccs_instances.push(inst); } - let sigmas_thetas = compute_sigmas_and_thetas(&ccs, &z_lcccs, &z_cccs, &r_x_prime); + let sigmas_thetas = compute_sigmas_thetas(&ccs, &z_lcccs, &z_cccs, &r_x_prime).unwrap(); let expected_c = compute_c( &ccs, diff --git a/folding-schemes/src/folding/hypernova/nimfs.rs b/folding-schemes/src/folding/hypernova/nimfs.rs index 0e94a550..e34e865b 100644 --- a/folding-schemes/src/folding/hypernova/nimfs.rs +++ b/folding-schemes/src/folding/hypernova/nimfs.rs @@ -7,7 +7,7 @@ use ark_std::{One, Zero}; use super::cccs::{Witness, CCCS}; use super::lcccs::LCCCS; -use super::utils::{compute_c, compute_g, compute_sigmas_and_thetas}; +use super::utils::{compute_c, compute_g, compute_sigmas_thetas}; use crate::ccs::CCS; use crate::transcript::Transcript; use crate::utils::hypercube::BooleanHypercube; @@ -244,7 +244,7 @@ where let r_x_prime = sumcheck_proof.point.clone(); // Step 4: compute sigmas and thetas - let sigmas_thetas = compute_sigmas_and_thetas(ccs, &z_lcccs, &z_cccs, &r_x_prime); + let sigmas_thetas = compute_sigmas_thetas(ccs, &z_lcccs, &z_cccs, &r_x_prime)?; // Step 6: Get the folding challenge let rho_scalar = C::ScalarField::from_le_bytes_mod_order(b"rho"); @@ -395,7 +395,7 @@ pub mod tests { let r_x_prime: Vec = (0..ccs.s).map(|_| Fr::rand(&mut rng)).collect(); let sigmas_thetas = - compute_sigmas_and_thetas(&ccs, &[z1.clone()], &[z2.clone()], &r_x_prime); + compute_sigmas_thetas(&ccs, &[z1.clone()], &[z2.clone()], &r_x_prime).unwrap(); let (pedersen_params, _) = Pedersen::::setup(&mut rng, ccs.n - ccs.l - 1).unwrap(); diff --git a/folding-schemes/src/folding/hypernova/utils.rs b/folding-schemes/src/folding/hypernova/utils.rs index 8da47261..209723a3 100644 --- a/folding-schemes/src/folding/hypernova/utils.rs +++ b/folding-schemes/src/folding/hypernova/utils.rs @@ -66,25 +66,40 @@ pub fn compute_sum_Mz( /// Compute the arrays of sigma_i and theta_i from step 4 corresponding to the LCCCS and CCCS /// instances -pub fn compute_sigmas_and_thetas( +pub fn compute_sigmas_thetas( ccs: &CCS, z_lcccs: &[Vec], z_cccs: &[Vec], r_x_prime: &[F], -) -> SigmasThetas { +) -> Result, Error> { + // sigmas let mut sigmas: Vec> = Vec::new(); for z_lcccs_i in z_lcccs { - // sigmas - let sigma_i = compute_all_sum_Mz_evals(&ccs.M, z_lcccs_i, r_x_prime, ccs.s_prime); + let mut Mzs: Vec> = vec![]; + for M_j in ccs.M.iter() { + Mzs.push(dense_vec_to_dense_mle(ccs.s, &mat_vec_mul(M_j, z_lcccs_i)?)); + } + let sigma_i = Mzs + .iter() + .map(|Mz| Mz.evaluate(r_x_prime).ok_or(Error::EvaluationFail)) + .collect::>()?; sigmas.push(sigma_i); } + + // thetas let mut thetas: Vec> = Vec::new(); for z_cccs_i in z_cccs { - // thetas - let theta_i = compute_all_sum_Mz_evals(&ccs.M, z_cccs_i, r_x_prime, ccs.s_prime); + let mut Mzs: Vec> = vec![]; + for M_j in ccs.M.iter() { + Mzs.push(dense_vec_to_dense_mle(ccs.s, &mat_vec_mul(M_j, z_cccs_i)?)); + } + let theta_i = Mzs + .iter() + .map(|Mz| Mz.evaluate(r_x_prime).ok_or(Error::EvaluationFail)) + .collect::>()?; thetas.push(theta_i); } - SigmasThetas(sigmas, thetas) + Ok(SigmasThetas(sigmas, thetas)) } /// computes c from the step 5 in section 5 of HyperNova, adapted to multiple LCCCS & CCCS @@ -285,7 +300,7 @@ pub mod tests { } #[test] - fn test_compute_sigmas_and_thetas() { + fn test_compute_sigmas_thetas() { let ccs = get_test_ccs(); let z1 = get_test_z(3); let z2 = get_test_z(4); @@ -303,7 +318,7 @@ pub mod tests { let (lcccs_instance, _) = ccs.to_lcccs(&mut rng, &pedersen_params, &z1).unwrap(); let sigmas_thetas = - compute_sigmas_and_thetas(&ccs, &[z1.clone()], &[z2.clone()], &r_x_prime); + compute_sigmas_thetas(&ccs, &[z1.clone()], &[z2.clone()], &r_x_prime).unwrap(); let g = compute_g( &ccs, diff --git a/folding-schemes/src/utils/mle.rs b/folding-schemes/src/utils/mle.rs index a2623b5f..a044eca0 100644 --- a/folding-schemes/src/utils/mle.rs +++ b/folding-schemes/src/utils/mle.rs @@ -124,7 +124,8 @@ mod tests { ]); let A_mle = matrix_to_mle(A); - assert_eq!(A_mle.evaluations.len(), 16); // 4x4 matrix, thus 2bit x 2bit, thus 2^4=16 evals + assert_eq!(A_mle.evaluations.len(), 15); // 15 non-zero elements + assert_eq!(A_mle.num_vars, 4); // 4x4 matrix, thus 2bit x 2bit, thus 2^4=16 evals let A = to_F_matrix::(vec![ vec![2, 3, 4, 4, 1], @@ -134,7 +135,8 @@ mod tests { vec![420, 4, 2, 0, 5], ]); let A_mle = matrix_to_mle(A.clone()); - assert_eq!(A_mle.evaluations.len(), 64); // 5x5 matrix, thus 3bit x 3bit, thus 2^6=64 evals + assert_eq!(A_mle.evaluations.len(), 23); // 23 non-zero elements + assert_eq!(A_mle.num_vars, 6); // 5x5 matrix, thus 3bit x 3bit, thus 2^6=64 evals // check that the A_mle evaluated over the boolean hypercube equals the matrix A_i_j values let bhc = BooleanHypercube::new(A_mle.num_vars);