Skip to content

Commit

Permalink
remove clones (microsoft#139)
Browse files Browse the repository at this point in the history
  • Loading branch information
winston-h-zhang authored and huitseeker committed Dec 5, 2023
1 parent c3f79bd commit 534f952
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 14 deletions.
39 changes: 27 additions & 12 deletions src/r1cs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,29 @@ impl<E: Engine> R1CSShape<E> {
Ok((Az, Bz, Cz))
}

pub(crate) fn multiply_witness(
&self,
W: &[E::Scalar],
u: &E::Scalar,
X: &[E::Scalar],
) -> Result<(Vec<E::Scalar>, Vec<E::Scalar>, Vec<E::Scalar>), NovaError> {
if X.len() != self.num_io || W.len() != self.num_vars {
return Err(NovaError::InvalidWitnessLength);
}

let (Az, (Bz, Cz)) = rayon::join(
|| self.A.multiply_witness(W, u, X),
|| {
rayon::join(
|| self.B.multiply_witness(W, u, X),
|| self.C.multiply_witness(W, u, X),
)
},
);

Ok((Az, Bz, Cz))
}

/// Checks if the Relaxed R1CS instance is satisfiable given a witness and its shape
pub fn is_sat_relaxed(
&self,
Expand All @@ -195,8 +218,7 @@ impl<E: Engine> R1CSShape<E> {

// verify if Az * Bz = u*Cz + E
let res_eq = {
let z = [W.W.clone(), vec![U.u], U.X.clone()].concat();
let (Az, Bz, Cz) = self.multiply_vec(&z)?;
let (Az, Bz, Cz) = self.multiply_witness(&W.W, &U.u, &U.X)?;
assert_eq!(Az.len(), self.num_cons);
assert_eq!(Bz.len(), self.num_cons);
assert_eq!(Cz.len(), self.num_cons);
Expand Down Expand Up @@ -230,8 +252,7 @@ impl<E: Engine> R1CSShape<E> {

// verify if Az * Bz = u*Cz
let res_eq = {
let z = [W.W.clone(), vec![E::Scalar::ONE], U.X.clone()].concat();
let (Az, Bz, Cz) = self.multiply_vec(&z)?;
let (Az, Bz, Cz) = self.multiply_witness(&W.W, &E::Scalar::ONE, &U.X)?;
assert_eq!(Az.len(), self.num_cons);
assert_eq!(Bz.len(), self.num_cons);
assert_eq!(Cz.len(), self.num_cons);
Expand Down Expand Up @@ -259,15 +280,9 @@ impl<E: Engine> R1CSShape<E> {
U2: &R1CSInstance<E>,
W2: &R1CSWitness<E>,
) -> Result<(Vec<E::Scalar>, Commitment<E>), NovaError> {
let (AZ_1, BZ_1, CZ_1) = {
let Z1 = [W1.W.clone(), vec![U1.u], U1.X.clone()].concat();
self.multiply_vec(&Z1)?
};
let (AZ_1, BZ_1, CZ_1) = self.multiply_witness(&W1.W, &U1.u, &U1.X)?;

let (AZ_2, BZ_2, CZ_2) = {
let Z2 = [W2.W.clone(), vec![E::Scalar::ONE], U2.X.clone()].concat();
self.multiply_vec(&Z2)?
};
let (AZ_2, BZ_2, CZ_2) = self.multiply_witness(&W2.W, &E::Scalar::ONE, &U2.X)?;

let (AZ_1_circ_BZ_2, AZ_2_circ_BZ_1, u_1_cdot_CZ_2, u_2_cdot_CZ_1) = {
let AZ_1_circ_BZ_2 = (0..AZ_1.len())
Expand Down
37 changes: 35 additions & 2 deletions src/r1cs/sparse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use ff::PrimeField;
use rayon::prelude::*;
use serde::{Deserialize, Serialize};
use std::cmp::Ordering;

/// CSR format sparse matrix, We follow the names used by scipy.
/// Detailed explanation here: https://stackoverflow.com/questions/52299420/scipy-csr-matrix-understand-indptr
Expand Down Expand Up @@ -74,14 +75,14 @@ impl<F: PrimeField> SparseMatrix<F> {
.zip(&self.indices[ptrs[0]..ptrs[1]])
}

/// Multiply by a dense vector; uses rayon/gpu.
/// Multiply by a dense vector; uses rayon to parallelize.
pub fn multiply_vec(&self, vector: &[F]) -> Vec<F> {
assert_eq!(self.cols, vector.len(), "invalid shape");

self.multiply_vec_unchecked(vector)
}

/// Multiply by a dense vector; uses rayon/gpu.
/// Multiply by a dense vector; uses rayon to parallelize.
/// This does not check that the shape of the matrix/vector are compatible.
pub fn multiply_vec_unchecked(&self, vector: &[F]) -> Vec<F> {
self
Expand All @@ -96,6 +97,38 @@ impl<F: PrimeField> SparseMatrix<F> {
.collect()
}

/// Multiply by a witness representing a dense vector; uses rayon to parallelize.
pub fn multiply_witness(&self, W: &[F], u: &F, X: &[F]) -> Vec<F> {
assert_eq!(self.cols, W.len() + X.len() + 1, "invalid shape");

self.multiply_witness_unchecked(W, u, X)
}

/// Multiply by a witness representing a dense vector; uses rayon to parallelize.
/// This does not check that the shape of the matrix/vector are compatible.
pub fn multiply_witness_unchecked(&self, W: &[F], u: &F, X: &[F]) -> Vec<F> {
let num_vars = W.len();
// preallocate the result vector
let mut result = Vec::with_capacity(self.indptr.len() - 1);
self
.indptr
.par_windows(2)
.map(|ptrs| {
self
.get_row_unchecked(ptrs.try_into().unwrap())
.fold(F::ZERO, |acc, (val, col_idx)| {
let val = match col_idx.cmp(&num_vars) {
Ordering::Less => *val * W[*col_idx],
Ordering::Equal => *val * *u,
Ordering::Greater => *val * X[*col_idx - num_vars - 1],
};
acc + val
})
})
.collect_into_vec(&mut result);
result
}

/// number of non-zero entries
pub fn len(&self) -> usize {
*self.indptr.last().unwrap()
Expand Down

0 comments on commit 534f952

Please sign in to comment.