Skip to content

Commit

Permalink
Merge branch 'main' into feat/basic-ccs
Browse files Browse the repository at this point in the history
  • Loading branch information
oskarth committed Jul 17, 2023
2 parents 05e06ce + 3437fae commit 16f260b
Show file tree
Hide file tree
Showing 8 changed files with 58 additions and 68 deletions.
20 changes: 8 additions & 12 deletions src/ccs/cccs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,9 @@ impl<G: Group> CCCSShape<G> {
}

// Using `fold` requires to not have results inside. So we unwrap for now but
// a better approach is needed (we can just keep the for loop otherwise.)
Ok((0..self.ccs.q).into_iter().fold(
VirtualPolynomial::<G::Scalar>::new(self.ccs.s),
|q, idx| {
// a better approach is needed (we ca just keep the for loop otherwise.)
Ok(
(0..self.ccs.q).fold(VirtualPolynomial::<G::Scalar>::new(self.ccs.s), |q, idx| {
let mut prod = VirtualPolynomial::<G::Scalar>::new(self.ccs.s);

for &j in &self.ccs.S[idx] {
Expand All @@ -88,8 +87,8 @@ impl<G: Group> CCCSShape<G> {
prod.scalar_mul(&self.ccs.c[idx]);
// Add it to the running sum
q.add(&prod)
},
))
}),
)
}

/// Computes Q(x) = \hat eq(beta, x) * q(x)
Expand Down Expand Up @@ -180,9 +179,9 @@ mod tests {
let q = cccs_shape.compute_q(&z).unwrap();

// Evaluate inside the hypercube
for x in BooleanHypercube::new(ccs_shape.s).into_iter() {
BooleanHypercube::new(ccs_shape.s).for_each(|x| {
assert_eq!(Fq::zero(), q.evaluate(&x).unwrap());
}
});

// Evaluate outside the hypercube
let beta: Vec<Fq> = (0..ccs_shape.s).map(|_| Fq::random(&mut rng)).collect();
Expand Down Expand Up @@ -224,7 +223,6 @@ mod tests {

// Now sum Q(x) evaluations in the hypercube and expect it to be 0
let r = BooleanHypercube::new(ccs_shape.s)
.into_iter()
.map(|x| Q.evaluate(&x).unwrap())
.fold(Fq::zero(), |acc, result| acc + result);
assert_eq!(r, Fq::zero());
Expand All @@ -234,7 +232,7 @@ mod tests {
/// Summing Q(x) over the hypercube is equivalent to evaluating G(x) at some point.
/// This test makes sure that G(x) agrees with q(x) inside the hypercube, but not outside
#[test]
fn test_Q_against_q() -> () {
fn test_Q_against_q() {
let mut rng = OsRng;

let z = CCSShape::<Ep>::get_test_z(3);
Expand All @@ -261,7 +259,6 @@ mod tests {

// Get G(d) by summing over Q_d(x) over the hypercube
let G_at_d = BooleanHypercube::new(ccs_shape.s)
.into_iter()
.map(|x| Q_at_d.evaluate(&x).unwrap())
.fold(Fq::zero(), |acc, result| acc + result);
assert_eq!(G_at_d, q.evaluate(&d).unwrap());
Expand All @@ -275,7 +272,6 @@ mod tests {

// Get G(d) by summing over Q_d(x) over the hypercube
let G_at_r = BooleanHypercube::new(ccs_shape.s)
.into_iter()
.map(|x| Q_at_r.evaluate(&x).unwrap())
.fold(Fq::zero(), |acc, result| acc + result);
assert_ne!(G_at_r, q.evaluate(&r).unwrap());
Expand Down
10 changes: 4 additions & 6 deletions src/ccs/lcccs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ impl<G: Group> LCCCS<G> {
// Sanity check
assert_eq!(z_mle.get_num_vars(), self.ccs.s_prime);

let sum_Mz = compute_sum_Mz::<G>(&M_j, &z_mle);
let sum_Mz = compute_sum_Mz::<G>(M_j, &z_mle);
let sum_Mz_virtual = VirtualPolynomial::new_from_mle(&Arc::new(sum_Mz), G::Scalar::ONE);
let L_j_x = sum_Mz_virtual.build_f_hat(&self.r_x).unwrap();
vec_L_j_x.push(L_j_x);
Expand Down Expand Up @@ -127,7 +127,7 @@ mod tests {

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

// Gen test vectors & artifacts
Expand All @@ -143,7 +143,6 @@ mod tests {

for (v_i, L_j_x) in lcccs.v.into_iter().zip(vec_L_j_x) {
let sum_L_j_x = BooleanHypercube::new(ccs.s)
.into_iter()
.map(|y| L_j_x.evaluate(&y).unwrap())
.fold(Fq::ZERO, |acc, result| acc + result);
assert_eq!(v_i, sum_L_j_x);
Expand All @@ -152,7 +151,7 @@ mod tests {

/// Given a bad z, check that the v_j should not match with the L_j(x)
#[test]
fn test_bad_v_j() -> () {
fn test_bad_v_j() {
let mut rng = OsRng;

// Gen test vectors & artifacts
Expand Down Expand Up @@ -180,14 +179,13 @@ mod tests {
let mut satisfied = true;
for (v_i, L_j_x) in lcccs.v.into_iter().zip(vec_L_j_x) {
let sum_L_j_x = BooleanHypercube::new(ccs.s)
.into_iter()
.map(|y| L_j_x.evaluate(&y).unwrap())
.fold(Fq::ZERO, |acc, result| acc + result);
if v_i != sum_L_j_x {
satisfied = false;
}
}

assert_eq!(satisfied, false);
assert!(!satisfied);
}
}
41 changes: 19 additions & 22 deletions src/ccs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,8 @@ impl<G: Group> CCSShape<G> {
// We require the number of public inputs/outputs to be even
assert_ne!(l % 2, 0, " number of public i/o has to be even");

let s = m.log_2() as usize;
let s_prime = n.log_2() as usize;
let s = m.log_2();
let s_prime = n.log_2();

CCSShape {
M: M.to_vec(),
Expand Down Expand Up @@ -143,7 +143,7 @@ impl<G: Group> CCSShape<G> {
C,
x: z[1..(1 + self.l)].to_vec(),
},
CCSWitness { w: w },
CCSWitness { w },
self.to_cccs_shape(),
)
}
Expand Down Expand Up @@ -224,22 +224,19 @@ impl<G: Group> CCSShape<G> {

let z = concat(vec![vec![G::Scalar::ONE], U.x.clone(), W.w.clone()]);

let r = (0..self.q)
.into_iter()
.fold(vec![G::Scalar::ZERO; self.m], |r, idx| {
let hadamard_output = self.S[idx]
.iter()
.fold(vec![G::Scalar::ZERO; self.m], |acc, j| {
let mvp = matrix_vector_product_sparse(&self.M[*j], &z);
hadamard_product(&acc, &mvp)
});

// Multiply by the coefficient of this step
let c_M_j_z: Vec<<G as Group>::Scalar> =
vector_elem_product(&hadamard_output, &self.c[idx]);

vector_add(&r, &c_M_j_z)
});
let r = (0..self.q).fold(vec![G::Scalar::ZERO; self.m], |r, idx| {
let hadamard_output = self.S[idx]
.iter()
.fold(vec![G::Scalar::ZERO; self.m], |acc, j| {
let mvp = matrix_vector_product_sparse(&self.M[*j], &z);
hadamard_product(&acc, &mvp)
});

// Multiply by the coefficient of this step
let c_M_j_z: Vec<<G as Group>::Scalar> = vector_elem_product(&hadamard_output, self.c[idx]);

vector_add(&r, &c_M_j_z)
});

// verify if comm_W is a commitment to W
let res_comm: bool = U.comm_w == CE::<G>::commit(ck, &W.w);
Expand Down Expand Up @@ -280,8 +277,8 @@ impl<G: Group> CCSShape<G> {
c: vec![G::Scalar::ONE, -G::Scalar::ONE],
m: r1cs.num_cons,
n: r1cs.num_vars,
s: r1cs.num_cons.log_2() as usize,
s_prime: r1cs.num_vars.log_2() as usize,
s: r1cs.num_cons.log_2(),
s_prime: r1cs.num_vars.log_2(),
}
}

Expand All @@ -303,7 +300,7 @@ impl<G: Group> CCSShape<G> {
}

#[cfg(test)]
pub(crate) fn gen_test_ccs(z: &Vec<G::Scalar>) -> (CCSShape<G>, CCSWitness<G>, CCSInstance<G>) {
pub(crate) fn gen_test_ccs(z: &[G::Scalar]) -> (CCSShape<G>, CCSWitness<G>, CCSInstance<G>) {
let one = G::Scalar::ONE;
let A = vec![
(0, 1, one),
Expand Down
18 changes: 9 additions & 9 deletions src/ccs/multifolding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -242,17 +242,17 @@ mod tests {

// evaluate g(x) over x \in {0,1}^s
let mut g_on_bhc = Fq::zero();
for x in BooleanHypercube::new(ccs.s).into_iter() {
for x in BooleanHypercube::new(ccs.s) {
g_on_bhc += g.evaluate(&x).unwrap();
}

// evaluate sum_{j \in [t]} (gamma^j * Lj(x)) over x \in {0,1}^s
let mut sum_Lj_on_bhc = Fq::zero();
let vec_L = lcccs_instance.compute_Ls(&z1);
for x in BooleanHypercube::new(ccs.s).into_iter() {
for j in 0..vec_L.len() {
for x in BooleanHypercube::new(ccs.s) {
for (j, coeff) in vec_L.iter().enumerate() {
let gamma_j = gamma.pow([j as u64]);
sum_Lj_on_bhc += vec_L[j].evaluate(&x).unwrap() * gamma_j;
sum_Lj_on_bhc += coeff.evaluate(&x).unwrap() * gamma_j;
}
}

Expand All @@ -269,7 +269,7 @@ mod tests {
}

#[test]
fn test_compute_sigmas_and_thetas() -> () {
fn test_compute_sigmas_and_thetas() {
let z1 = CCSShape::<Ep>::get_test_z(3);
let z2 = CCSShape::<Ep>::get_test_z(4);

Expand Down Expand Up @@ -300,16 +300,16 @@ mod tests {
{
// evaluate g(x) over x \in {0,1}^s
let mut g_on_bhc = Fq::zero();
for x in BooleanHypercube::new(ccs.s).into_iter() {
for x in BooleanHypercube::new(ccs.s) {
g_on_bhc += g.evaluate(&x).unwrap();
}
// evaluate sum_{j \in [t]} (gamma^j * Lj(x)) over x \in {0,1}^s
let mut sum_Lj_on_bhc = Fq::zero();
let vec_L = lcccs_instance.compute_Ls(&z1);
for x in BooleanHypercube::new(ccs.s).into_iter() {
for j in 0..vec_L.len() {
for x in BooleanHypercube::new(ccs.s) {
for (j, coeff) in vec_L.iter().enumerate() {
let gamma_j = gamma.pow([j as u64]);
sum_Lj_on_bhc += vec_L[j].evaluate(&x).unwrap() * gamma_j;
sum_Lj_on_bhc += coeff.evaluate(&x).unwrap() * gamma_j;
}
}

Expand Down
6 changes: 3 additions & 3 deletions src/ccs/util/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ pub fn compute_sum_Mz<G: Group>(

let bhc = BooleanHypercube::<G::Scalar>::new(z.get_num_vars());
for y in bhc.into_iter() {
let M_y = fix_variables(&M_mle, &y);
let M_y = fix_variables(M_mle, &y);

// reverse y to match spartan/polynomial evaluate
let y_rev: Vec<G::Scalar> = y.into_iter().rev().collect();
Expand Down Expand Up @@ -175,15 +175,15 @@ mod tests {
let mut last_vars_fixed = A_mle.clone();
// this is equivalent to Espresso/hyperplonk's 'fix_last_variables' mehthod
for bit in y.clone().iter().rev() {
last_vars_fixed.bound_poly_var_top(&bit)
last_vars_fixed.bound_poly_var_top(bit)
}

assert_eq!(last_vars_fixed.Z, row_i);
}
}

#[test]
fn test_compute_sum_Mz_over_boolean_hypercube() -> () {
fn test_compute_sum_Mz_over_boolean_hypercube() {
let z = CCSShape::<Ep>::get_test_z(3);
let (ccs, _, _) = CCSShape::<Ep>::gen_test_ccs(&z);

Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
)]
#![allow(non_snake_case)]
#![allow(clippy::type_complexity)]
#![allow(clippy::upper_case_acronyms)]
#![forbid(unsafe_code)]

// private modules
Expand Down
6 changes: 3 additions & 3 deletions src/spartan/polynomial.rs
Original file line number Diff line number Diff line change
Expand Up @@ -271,11 +271,11 @@ mod tests {
assert_eq!(y, Fp::one());

let eval_list = eq_poly.evals();
for i in 0..(2_usize).pow(3) {
for (i, &coeff) in eval_list.iter().enumerate().take((2_usize).pow(3)) {
if i == 5 {
assert_eq!(eval_list[i], Fp::one());
assert_eq!(coeff, Fp::one());
} else {
assert_eq!(eval_list[i], Fp::zero());
assert_eq!(coeff, Fp::zero());
}
}
}
Expand Down
24 changes: 11 additions & 13 deletions src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,10 +141,10 @@ pub fn vector_add<F: PrimeField>(a: &Vec<F>, b: &Vec<F>) -> Vec<F> {
res
}

pub fn vector_elem_product<F: PrimeField>(a: &Vec<F>, e: &F) -> Vec<F> {
pub fn vector_elem_product<F: PrimeField>(a: &Vec<F>, e: F) -> Vec<F> {
let mut res = Vec::with_capacity(a.len());
for i in 0..a.len() {
res.push(a[i] * e);
for &item in a {
res.push(item * e);
}

res
Expand All @@ -161,10 +161,10 @@ pub fn matrix_vector_product<F: PrimeField>(matrix: &Vec<Vec<F>>, vector: &Vec<F
"matrix rows != vector length"
);
let mut res = Vec::with_capacity(matrix.len());
for i in 0..matrix.len() {
for row in matrix {
let mut sum = F::ZERO;
for j in 0..matrix[i].len() {
sum += matrix[i][j] * vector[j];
for j in 0..row.len() {
sum += row[j] * vector[j];
}
res.push(sum);
}
Expand All @@ -177,7 +177,7 @@ pub fn matrix_vector_product<F: PrimeField>(matrix: &Vec<Vec<F>>, vector: &Vec<F
// XXX: This could be implemented via Mul trait in the lib. We should consider as it will reduce imports.
pub fn matrix_vector_product_sparse<F: PrimeField>(
matrix: &SparseMatrix<F>,
vector: &Vec<F>,
vector: &[F],
) -> Vec<F> {
let mut res = vec![F::ZERO; matrix.n_rows()];
for &(row, col, value) in matrix.coeffs.iter() {
Expand Down Expand Up @@ -258,12 +258,10 @@ pub fn random_zero_mle_list<F: PrimeField, R: RngCore>(
}
}

let list = multiplicands
multiplicands
.into_iter()
.map(|x| Arc::new(MultilinearPolynomial::new(x)))
.collect();

list
.collect()
}

pub(crate) fn log2(x: usize) -> u32 {
Expand Down Expand Up @@ -301,7 +299,7 @@ mod tests {
fn test_vector_elem_product_with<F: PrimeField>() {
let a = to_F_vec::<F>(vec![1, 2, 3]);
let e = F::from(2);
let res = vector_elem_product(&a, &e);
let res = vector_elem_product(&a, e);
assert_eq!(res, to_F_vec::<F>(vec![2, 4, 6]));
}

Expand Down Expand Up @@ -414,7 +412,7 @@ mod tests {

// check that the A_mle evaluated over the boolean hypercube equals the matrix A_i_j values
let bhc = BooleanHypercube::<F>::new(A_mle.get_num_vars());
let mut A_padded = A.clone();
let mut A_padded = A;
A_padded.pad();
for term in A_padded.coeffs.iter() {
let (i, j, coeff) = term;
Expand Down

0 comments on commit 16f260b

Please sign in to comment.