use ark_ff::PrimeField; use ark_r1cs_std::{ alloc::{AllocVar, AllocationMode}, fields::{fp::FpVar, FieldVar}, R1CSVar, }; use ark_relations::r1cs::{Namespace, SynthesisError}; use core::{borrow::Borrow, marker::PhantomData}; use crate::utils::vec::SparseMatrix; pub trait MatrixGadget { fn mul_vector(&self, v: &[FV]) -> Result, SynthesisError>; } pub trait VectorGadget { fn add(&self, other: &Self) -> Result, SynthesisError>; fn mul_scalar(&self, other: &FV) -> Result, SynthesisError>; fn hadamard(&self, other: &Self) -> Result, SynthesisError>; } impl VectorGadget> for [FpVar] { fn add(&self, other: &Self) -> Result>, SynthesisError> { if self.len() != other.len() { return Err(SynthesisError::Unsatisfiable); } Ok(self.iter().zip(other.iter()).map(|(a, b)| a + b).collect()) } fn mul_scalar(&self, c: &FpVar) -> Result>, SynthesisError> { Ok(self.iter().map(|a| a * c).collect()) } fn hadamard(&self, other: &Self) -> Result>, SynthesisError> { if self.len() != other.len() { return Err(SynthesisError::Unsatisfiable); } Ok(self.iter().zip(other.iter()).map(|(a, b)| a * b).collect()) } } #[derive(Debug, Clone)] pub struct SparseMatrixVar> { _f: PhantomData, _cf: PhantomData, _fv: PhantomData, pub n_rows: usize, pub n_cols: usize, // same format as the native SparseMatrix (which follows ark_relations::r1cs::Matrix format pub coeffs: Vec>, } impl AllocVar, CF> for SparseMatrixVar where F: PrimeField, CF: PrimeField, FV: AllocVar, { fn new_variable>>( cs: impl Into>, f: impl FnOnce() -> Result, mode: AllocationMode, ) -> Result { f().and_then(|val| { let cs = cs.into(); let mut coeffs: Vec> = Vec::new(); for row in val.borrow().coeffs.iter() { let mut rowVar: Vec<(FV, usize)> = Vec::new(); for &(value, col_i) in row.iter() { let coeffVar = FV::new_variable(cs.clone(), || Ok(value), mode)?; rowVar.push((coeffVar, col_i)); } coeffs.push(rowVar); } Ok(Self { _f: PhantomData, _cf: PhantomData, _fv: PhantomData, n_rows: val.borrow().n_rows, n_cols: val.borrow().n_cols, coeffs, }) }) } } impl MatrixGadget> for SparseMatrixVar> { fn mul_vector(&self, v: &[FpVar]) -> Result>, SynthesisError> { Ok(self .coeffs .iter() .map(|row| { let products = row .iter() .map(|(value, col_i)| value * &v[*col_i]) .collect::>(); if products.is_constant() { FpVar::constant(products.value().unwrap_or_default().into_iter().sum()) } else { products.iter().sum() } }) .collect()) } }