use ark_ff::PrimeField; pub use ark_relations::r1cs::Matrix as R1CSMatrix; use ark_std::cfg_iter; use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator}; use crate::Error; #[derive(Clone, Debug, Eq, PartialEq)] pub struct SparseMatrix { pub n_rows: usize, pub n_cols: usize, /// coeffs = R1CSMatrix = Vec>, which contains each row and the F is the value /// of the coefficient and the usize indicates the column position pub coeffs: R1CSMatrix, } impl SparseMatrix { pub fn to_dense(&self) -> Vec> { let mut r: Vec> = vec![vec![F::zero(); self.n_cols]; self.n_rows]; for (row_i, row) in self.coeffs.iter().enumerate() { for &(value, col_i) in row.iter() { r[row_i][col_i] = value; } } r } } pub fn dense_matrix_to_sparse(m: Vec>) -> SparseMatrix { let mut r = SparseMatrix:: { n_rows: m.len(), n_cols: m[0].len(), coeffs: Vec::new(), }; for m_row in m.iter() { let mut row: Vec<(F, usize)> = Vec::new(); for (col_i, value) in m_row.iter().enumerate() { if !value.is_zero() { row.push((*value, col_i)); } } r.coeffs.push(row); } r } pub fn vec_add(a: &[F], b: &[F]) -> Result, Error> { if a.len() != b.len() { return Err(Error::NotSameLength( "a.len()".to_string(), a.len(), "b.len()".to_string(), b.len(), )); } Ok(a.iter().zip(b.iter()).map(|(x, y)| *x + y).collect()) } pub fn vec_sub(a: &[F], b: &[F]) -> Result, Error> { if a.len() != b.len() { return Err(Error::NotSameLength( "a.len()".to_string(), a.len(), "b.len()".to_string(), b.len(), )); } Ok(a.iter().zip(b.iter()).map(|(x, y)| *x - y).collect()) } pub fn vec_scalar_mul(vec: &[F], c: &F) -> Vec { vec.iter().map(|a| *a * c).collect() } pub fn is_zero_vec(vec: &[F]) -> bool { vec.iter().all(|a| a.is_zero()) } pub fn mat_vec_mul(M: &Vec>, z: &[F]) -> Result, Error> { if M.is_empty() { return Err(Error::Empty); } if M[0].len() != z.len() { return Err(Error::NotSameLength( "M[0].len()".to_string(), M[0].len(), "z.len()".to_string(), z.len(), )); } let mut r: Vec = vec![F::zero(); M.len()]; for (i, M_i) in M.iter().enumerate() { for (j, M_ij) in M_i.iter().enumerate() { r[i] += *M_ij * z[j]; } } Ok(r) } pub fn mat_vec_mul_sparse(M: &SparseMatrix, z: &[F]) -> Result, Error> { if M.n_cols != z.len() { return Err(Error::NotSameLength( "M.n_cols".to_string(), M.n_cols, "z.len()".to_string(), z.len(), )); } let mut res = vec![F::zero(); M.n_rows]; for (row_i, row) in M.coeffs.iter().enumerate() { for &(value, col_i) in row.iter() { res[row_i] += value * z[col_i]; } } Ok(res) } pub fn hadamard(a: &[F], b: &[F]) -> Result, Error> { if a.len() != b.len() { return Err(Error::NotSameLength( "a.len()".to_string(), a.len(), "b.len()".to_string(), b.len(), )); } Ok(cfg_iter!(a).zip(b).map(|(a, b)| *a * b).collect()) } #[cfg(test)] pub mod tests { use super::*; use ark_pallas::Fr; pub fn to_F_matrix(M: Vec>) -> SparseMatrix { dense_matrix_to_sparse(to_F_dense_matrix(M)) } pub fn to_F_dense_matrix(M: Vec>) -> Vec> { M.iter() .map(|m| m.iter().map(|r| F::from(*r as u64)).collect()) .collect() } pub fn to_F_vec(z: Vec) -> Vec { z.iter().map(|c| F::from(*c as u64)).collect() } #[test] fn test_dense_sparse_conversions() { let A = to_F_dense_matrix::(vec![ vec![0, 1, 0, 0, 0, 0], vec![0, 0, 0, 1, 0, 0], vec![0, 1, 0, 0, 1, 0], vec![5, 0, 0, 0, 0, 1], ]); let A_sparse = dense_matrix_to_sparse(A.clone()); assert_eq!(A_sparse.to_dense(), A); } // test mat_vec_mul & mat_vec_mul_sparse #[test] fn test_mat_vec_mul() { let A = to_F_matrix::(vec![ vec![0, 1, 0, 0, 0, 0], vec![0, 0, 0, 1, 0, 0], vec![0, 1, 0, 0, 1, 0], vec![5, 0, 0, 0, 0, 1], ]) .to_dense(); let z = to_F_vec(vec![1, 3, 35, 9, 27, 30]); assert_eq!(mat_vec_mul(&A, &z).unwrap(), to_F_vec(vec![3, 9, 30, 35])); assert_eq!( mat_vec_mul_sparse(&dense_matrix_to_sparse(A), &z).unwrap(), to_F_vec(vec![3, 9, 30, 35]) ); let A = to_F_matrix::(vec![vec![2, 3, 4, 5], vec![4, 8, 12, 14], vec![9, 8, 7, 6]]); let v = to_F_vec(vec![19, 55, 50, 3]); assert_eq!( mat_vec_mul(&A.to_dense(), &v).unwrap(), to_F_vec(vec![418, 1158, 979]) ); assert_eq!( mat_vec_mul_sparse(&A, &v).unwrap(), to_F_vec(vec![418, 1158, 979]) ); } #[test] fn test_hadamard_product() { let a = to_F_vec::(vec![1, 2, 3, 4, 5, 6]); let b = to_F_vec(vec![7, 8, 9, 10, 11, 12]); assert_eq!( hadamard(&a, &b).unwrap(), to_F_vec(vec![7, 16, 27, 40, 55, 72]) ); } #[test] fn test_vec_add() { let a: Vec = to_F_vec::(vec![1, 2, 3, 4, 5, 6]); let b: Vec = to_F_vec(vec![7, 8, 9, 10, 11, 12]); assert_eq!( vec_add(&a, &b).unwrap(), to_F_vec(vec![8, 10, 12, 14, 16, 18]) ); } }