Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A gadget to compute the second term of the sum in section 5, step 5 #56

2 changes: 1 addition & 1 deletion src/folding/circuits/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use ark_r1cs_std::fields::{fp::FpVar, FieldVar};
use ark_relations::r1cs::SynthesisError;
use std::marker::PhantomData;

/// EqEval is a gadget for computing $\tilde{eq}(a, b) = \Pi_{i=1}^{l}(a_i \cdot b_i + (1 - a_i)(1 - b_i))$
/// EqEval is a gadget for computing $\tilde{eq}(a, b) = \prod_{i=1}^{l}(a_i \cdot b_i + (1 - a_i)(1 - b_i))$
/// :warning: This is not the ark_r1cs_std::eq::EqGadget
pub struct EqEvalGadget<F: PrimeField> {
_f: PhantomData<F>,
Expand Down
77 changes: 73 additions & 4 deletions src/folding/hypernova/circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ pub struct SumMulsGammaPowsEqSigmaGadget<F: PrimeField> {
}

impl<F: PrimeField> SumMulsGammaPowsEqSigmaGadget<F> {
/// Computes the sum $\Sigma_{j}^{j + n} \gamma^{j} \cdot eq_eval \cdot \sigma_{j}$, where $n$ is the length of the `sigmas` vector
/// Computes the sum $\sum_{j}^{j + n} \gamma^{j} \cdot eq_eval \cdot \sigma_{j}$, where $n$ is the length of the `sigmas` vector
/// It corresponds to the first term of the sum that $\mathcal{V}$ has to compute at section 5, step 5 of "A multi-folding scheme for CCS".
///
/// # Arguments
/// - `sigmas`: vector of $\sigma_j$ values
/// - `eq_eval`: the value of $\tilde{eq}(x_j, x^{\prime})$
/// - `gamma`: a `GammaVar`, which supports a `pow` method, representing $\gamma$
/// - `gamma`: value $\gamma$
arnaucube marked this conversation as resolved.
Show resolved Hide resolved
/// - `j`: the power at which we start to compute $\gamma^{j}$. This is needed in the contexxt of multifolding.
///
/// # Notes
Expand All @@ -42,15 +42,44 @@ impl<F: PrimeField> SumMulsGammaPowsEqSigmaGadget<F> {
}
}

/// Gadget to compute $\sum_{i=1}^{q} c_i * \prod_{j \in S_i} theta_j$.
pub struct SumCiMulProdThetajGadget<F: PrimeField> {
_f: PhantomData<F>,
}

impl<F: PrimeField> SumCiMulProdThetajGadget<F> {
/// Computes $\sum_{i=1}^{q} c_i * \prod_{j \in S_i} theta_j$
///
/// # Arguments
/// - `c_i`: vector of $c_i$ values
/// - `thetas`: vector of pre-processed $\thetas[j]$ values corresponding to a particular `ccs.S[i]`
///
/// # Notes
/// This is a part of the second term of the sum that $\mathcal{V}$ has to compute at section 5, step 5 of "A multi-folding scheme for CCS".
/// The first term is computed by `SumMulsGammaPowsEqSigmaGadget::sum_muls_gamma_pows_eq_sigma`.
/// This is a doct product between a vector of c_i values and a vector of pre-processed $\theta_j$ values, where $j$ is a value from $S_i$.
/// Hence, this requires some pre-processing of the $\theta_j$ values, before running this gadget.
pub fn sum_ci_mul_prod_thetaj(c_i: Vec<FpVar<F>>, thetas: Vec<Vec<FpVar<F>>>) -> FpVar<F> {
let mut result = FpVar::<F>::zero();
for (i, c_i) in c_i.iter().enumerate() {
let prod = &thetas[i].iter().fold(FpVar::one(), |acc, e| acc * e);
result += c_i * prod;
}
result
}
}

#[cfg(test)]
mod tests {
use super::SumMulsGammaPowsEqSigmaGadget;
use super::{SumCiMulProdThetajGadget, SumMulsGammaPowsEqSigmaGadget};
use crate::{
ccs::{
tests::{get_test_ccs, get_test_z},
CCS,
},
folding::hypernova::utils::{compute_sigmas_and_thetas, sum_muls_gamma_pows_eq_sigma},
folding::hypernova::utils::{
compute_sigmas_and_thetas, sum_ci_mul_prod_thetaj, sum_muls_gamma_pows_eq_sigma,
},
pedersen::Pedersen,
utils::virtual_polynomial::eq_eval,
};
Expand Down Expand Up @@ -102,4 +131,44 @@ mod tests {
assert_eq!(expected, computed.value().unwrap());
}
}

#[test]
pub fn test_sum_ci_mul_prod_thetaj_gadget() {
let mut rng = test_rng();
let ccs: CCS<Projective> = get_test_ccs();
let z1 = get_test_z(3);
let z2 = get_test_z(4);

let r_x_prime: Vec<Fr> = (0..ccs.s).map(|_| Fr::rand(&mut rng)).collect();

// Initialize a multifolding object
let pedersen_params = Pedersen::new_params(&mut rng, ccs.n - ccs.l - 1);
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);

let mut e_lcccs = Vec::new();
for r_x in &vec![lcccs_instance.r_x] {
e_lcccs.push(eq_eval(r_x, &r_x_prime).unwrap());
}

// Initialize cs
let cs = ConstraintSystem::<Fr>::new_ref();
let vec_thetas = sigmas_thetas.1;
for (_, thetas) in vec_thetas.iter().enumerate() {
// sum c_i * prod theta_j
let expected = sum_ci_mul_prod_thetaj(&ccs, thetas); // from `compute_c_from_sigmas_and_thetas`
let mut prepared_thetas = Vec::new();
for i in 0..ccs.q {
let prepared: Vec<Fr> = ccs.S[i].iter().map(|j| thetas[*j]).collect();
prepared_thetas
.push(Vec::<FpVar<Fr>>::new_witness(cs.clone(), || Ok(prepared)).unwrap());
}
let computed = SumCiMulProdThetajGadget::sum_ci_mul_prod_thetaj(
Vec::<FpVar<Fr>>::new_witness(cs.clone(), || Ok(ccs.c.clone())).unwrap(),
prepared_thetas,
);
assert_eq!(expected, computed.value().unwrap());
}
}
}
27 changes: 18 additions & 9 deletions src/folding/hypernova/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ pub fn compute_sigmas_and_thetas<C: CurveGroup>(
SigmasThetas(sigmas, thetas)
}

/// Computes the sum $\Sigma_{j = 0}^{n} \gamma^{\text{pow} + j} \cdot eq_eval \cdot \sigma_{j}$
/// Computes the sum $\sum_{j = 0}^{n} \gamma^{\text{pow} + j} \cdot eq_eval \cdot \sigma_{j}$
/// `pow` corresponds to `i * ccs.t` in `compute_c_from_sigmas_and_thetas`
pub fn sum_muls_gamma_pows_eq_sigma<F: PrimeField>(
gamma: F,
Expand All @@ -102,6 +102,22 @@ pub fn sum_muls_gamma_pows_eq_sigma<F: PrimeField>(
result
}

/// Computes $\sum_{i=1}^{q} c_i * \prod_{j \in S_i} theta_j$
pub fn sum_ci_mul_prod_thetaj<C: CurveGroup>(
ccs: &CCS<C>,
thetas: &[C::ScalarField],
) -> C::ScalarField {
let mut result = C::ScalarField::zero();
for i in 0..ccs.q {
let mut prod = C::ScalarField::one();
for j in ccs.S[i].clone() {
prod *= thetas[j];
}
result += ccs.c[i] * prod;
}
result
}

/// Compute the right-hand-side of step 5 of the multifolding scheme
pub fn compute_c_from_sigmas_and_thetas<C: CurveGroup>(
ccs: &CCS<C>,
Expand All @@ -127,14 +143,7 @@ pub fn compute_c_from_sigmas_and_thetas<C: CurveGroup>(
let e2 = eq_eval(beta, r_x_prime).unwrap();
for (k, thetas) in vec_thetas.iter().enumerate() {
// + gamma^{t+1} * e2 * sum c_i * prod theta_j
let mut lhs = C::ScalarField::zero();
for i in 0..ccs.q {
let mut prod = C::ScalarField::one();
for j in ccs.S[i].clone() {
prod *= thetas[j];
}
lhs += ccs.c[i] * prod;
}
let lhs = sum_ci_mul_prod_thetaj(ccs, thetas);
let gamma_t1 = gamma.pow([(mu * ccs.t + k) as u64]);
c += gamma_t1 * e2 * lhs;
}
Expand Down
Loading