From 6089294309d13fa57735bc4c0098393d82b9ff97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Garillot?= <4142+huitseeker@users.noreply.github.com> Date: Sun, 25 Feb 2024 02:12:54 -0800 Subject: [PATCH] Batch inversion & normalization (#345) * fix: use ff batch inversion * fix: use batch normalization in HyperKZG --- src/provider/hyperkzg.rs | 19 ++++++++++--------- src/provider/ipa_pc.rs | 27 ++------------------------- src/provider/util/mod.rs | 22 ++++++++++++++++++++++ src/spartan/sumcheck/engine.rs | 32 +++----------------------------- 4 files changed, 37 insertions(+), 63 deletions(-) diff --git a/src/provider/hyperkzg.rs b/src/provider/hyperkzg.rs index 15daf7f4f..02a6e4457 100644 --- a/src/provider/hyperkzg.rs +++ b/src/provider/hyperkzg.rs @@ -24,7 +24,7 @@ use crate::{ }; use core::marker::PhantomData; use ff::{Field, PrimeFieldBits}; -use group::{Curve, Group as _}; +use group::{prime::PrimeCurveAffine as _, Curve, Group as _}; use itertools::Itertools as _; use pairing::{Engine, MillerLoopResult, MultiMillerLoop}; use rayon::prelude::*; @@ -241,14 +241,15 @@ where // We do not need to commit to the first polynomial as it is already committed. // Compute commitments in parallel - let comms: Vec = (1..polys.len()) - .into_par_iter() - .map(|i| { - >::commit(ck, &polys[i]) - .comm - .to_affine() - }) - .collect(); + let comms = { + let mut comms = vec![E::G1Affine::identity(); polys.len() - 1]; + let comms_proj: Vec = (1..polys.len()) + .into_par_iter() + .map(|i| >::commit(ck, &polys[i]).comm) + .collect(); + Curve::batch_normalize(&comms_proj, &mut comms); + comms + }; // Phase 2 // We do not need to add x to the transcript, because in our context x was obtained from the transcript. diff --git a/src/provider/ipa_pc.rs b/src/provider/ipa_pc.rs index f0a9df871..bc54fd2e6 100644 --- a/src/provider/ipa_pc.rs +++ b/src/provider/ipa_pc.rs @@ -2,7 +2,7 @@ use crate::{ digest::SimpleDigestible, errors::{NovaError, PCSError}, - provider::{pedersen::CommitmentKeyExtTrait, traits::DlogGroup}, + provider::{pedersen::CommitmentKeyExtTrait, traits::DlogGroup, util::field::batch_invert}, spartan::polys::eq::EqPolynomial, traits::{ commitment::{CommitmentEngineTrait, CommitmentTrait}, @@ -304,29 +304,6 @@ where let P = U.comm_a_vec + CE::::commit(&ck_c, &[U.c]); - let batch_invert = |v: &[E::Scalar]| -> Result, NovaError> { - let mut products = vec![E::Scalar::ZERO; v.len()]; - let mut acc = E::Scalar::ONE; - - for i in 0..v.len() { - products[i] = acc; - acc *= v[i]; - } - - // return error if acc is zero - acc = Option::from(acc.invert()).ok_or(NovaError::InternalError)?; - - // compute the inverse once for all entries - let mut inv = vec![E::Scalar::ZERO; v.len()]; - for i in (0..v.len()).rev() { - let tmp = acc * v[i]; - inv[i] = products[i] * acc; - acc = tmp; - } - - Ok(inv) - }; - // compute a vector of public coins using self.L_vec and self.R_vec let r = (0..self.L_vec.len()) .map(|i| { @@ -341,7 +318,7 @@ where .into_par_iter() .map(|i| r[i] * r[i]) .collect(); - let r_inverse = batch_invert(&r)?; + let r_inverse = batch_invert(r.clone())?; let r_inverse_square: Vec = (0..self.L_vec.len()) .into_par_iter() .map(|i| r_inverse[i] * r_inverse[i]) diff --git a/src/provider/util/mod.rs b/src/provider/util/mod.rs index d097ffe4f..0c9cfd5e9 100644 --- a/src/provider/util/mod.rs +++ b/src/provider/util/mod.rs @@ -11,6 +11,28 @@ pub mod msm { } } +pub mod field { + use crate::errors::NovaError; + use ff::{BatchInverter, Field}; + + #[inline] + pub fn batch_invert(mut v: Vec) -> Result, NovaError> { + // we only allocate the scratch space if every element of v is nonzero + let mut scratch_space = v + .iter() + .map(|x| { + if !x.is_zero_vartime() { + Ok(*x) + } else { + Err(NovaError::InternalError) + } + }) + .collect::, _>>()?; + let _ = BatchInverter::invert_with_external_scratch(&mut v, &mut scratch_space[..]); + Ok(v) + } +} + pub mod iterators { use ff::Field; use rayon::iter::{IndexedParallelIterator, IntoParallelIterator, ParallelIterator}; diff --git a/src/spartan/sumcheck/engine.rs b/src/spartan/sumcheck/engine.rs index 537bd6195..530bbe201 100644 --- a/src/spartan/sumcheck/engine.rs +++ b/src/spartan/sumcheck/engine.rs @@ -1,6 +1,7 @@ use ff::Field; use rayon::prelude::*; +use crate::provider::util::field::batch_invert; use crate::spartan::math::Math; use crate::spartan::polys::{ eq::EqPolynomial, masked_eq::MaskedEqPolynomial, multilinear::MultilinearPolynomial, @@ -191,33 +192,6 @@ impl MemorySumcheckInstance { || hash_func_vec(mem_col, addr_col, L_col), ); - let batch_invert = |v: &[E::Scalar]| -> Result, NovaError> { - let mut products = vec![E::Scalar::ZERO; v.len()]; - let mut acc = E::Scalar::ONE; - - for i in 0..v.len() { - products[i] = acc; - acc *= v[i]; - } - - // we can compute an inversion only if acc is non-zero - if acc == E::Scalar::ZERO { - return Err(NovaError::InternalError); - } - - // compute the inverse once for all entries - acc = acc.invert().unwrap(); - - let mut inv = vec![E::Scalar::ZERO; v.len()]; - for i in 0..v.len() { - let tmp = acc * v[v.len() - 1 - i]; - inv[v.len() - 1 - i] = products[v.len() - 1 - i] * acc; - acc = tmp; - } - - Ok(inv) - }; - // compute vectors TS[i]/(T[i] + r) and 1/(W[i] + r) let helper = |T: &[E::Scalar], W: &[E::Scalar], @@ -234,7 +208,7 @@ impl MemorySumcheckInstance { || { rayon::join( || { - let inv = batch_invert(&T.par_iter().map(|e| *e + *r).collect::>())?; + let inv = batch_invert(T.par_iter().map(|e| *e + *r).collect::>())?; // compute inv[i] * TS[i] in parallel Ok( @@ -242,7 +216,7 @@ impl MemorySumcheckInstance { .collect::>(), ) }, - || batch_invert(&W.par_iter().map(|e| *e + *r).collect::>()), + || batch_invert(W.par_iter().map(|e| *e + *r).collect::>()), ) }, || {