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 Nov 27, 2023
1 parent e985fc4 commit 5a49202
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 15 deletions.
43 changes: 30 additions & 13 deletions src/r1cs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,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 @@ -200,8 +223,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 @@ -242,8 +264,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 @@ -276,15 +297,11 @@ 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) = tracing::trace_span!("AZ_1, BZ_1, CZ_1").in_scope(|| {
let Z1 = [W1.W.clone(), vec![U1.u], U1.X.clone()].concat();
self.multiply_vec(&Z1)
})?;

let (AZ_2, BZ_2, CZ_2) = tracing::trace_span!("AZ_2, BZ_2, CZ_2").in_scope(|| {
let Z2 = [W2.W.clone(), vec![E::Scalar::ONE], U2.X.clone()].concat();
self.multiply_vec(&Z2)
})?;
let (AZ_1, BZ_1, CZ_1) = tracing::trace_span!("AZ_1, BZ_1, CZ_1")
.in_scope(|| self.multiply_witness(&W1.W, &U1.u, &U1.X))?;

let (AZ_2, BZ_2, CZ_2) = tracing::trace_span!("AZ_2, BZ_2, CZ_2")
.in_scope(|| 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) =
tracing::trace_span!("cross terms").in_scope(|| {
Expand Down
43 changes: 41 additions & 2 deletions src/r1cs/sparse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
//! Specifically, we implement sparse matrix / dense vector multiplication
//! to compute the `A z`, `B z`, and `C z` in Nova.

use std::cmp::Ordering;

use abomonation::Abomonation;
use abomonation_derive::Abomonation;
use ff::PrimeField;
Expand Down Expand Up @@ -91,14 +93,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.
#[tracing::instrument(
skip_all,
Expand All @@ -118,6 +120,43 @@ 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.
#[tracing::instrument(
skip_all,
level = "trace",
name = "SparseMatrix::multiply_vec_unchecked"
)]
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 5a49202

Please sign in to comment.