arnaucube
arnaucube/sonobePublic Self-hosted repository
sonobe/folding-schemes/src/utils/gadgets.rs
folding-schemes/src/utils/gadgets.rsRaw
  1use ark_ff::PrimeField;
  2use ark_r1cs_std::{
  3    alloc::{AllocVar, AllocationMode},
  4    eq::EqGadget,
  5    fields::{fp::FpVar, FieldVar},
  6    R1CSVar,
  7};
  8use ark_relations::r1cs::{Namespace, SynthesisError};
  9use core::borrow::Borrow;
 10
 11use crate::utils::vec::SparseMatrix;
 12
 13/// `EquivalenceGadget` enforces that two in-circuit variables are equivalent,
 14/// where the equivalence relation is parameterized by `M`:
 15/// - For `FpVar`, it is simply a equality relation, and `M` is unused.
 16/// - For `NonNativeUintVar`, we consider equivalence as a congruence relation,
 17///   in terms of modular arithmetic, so `M` specifies the modulus.
 18pub trait EquivalenceGadget<M> {
 19    fn enforce_equivalent(&self, other: &Self) -> Result<(), SynthesisError>;
 20}
 21impl<M, F: PrimeField> EquivalenceGadget<M> for FpVar<F> {
 22    fn enforce_equivalent(&self, other: &Self) -> Result<(), SynthesisError> {
 23        self.enforce_equal(other)
 24    }
 25}
 26impl<M, T: EquivalenceGadget<M>> EquivalenceGadget<M> for [T] {
 27    fn enforce_equivalent(&self, other: &Self) -> Result<(), SynthesisError> {
 28        self.iter()
 29            .zip(other)
 30            .try_for_each(|(a, b)| a.enforce_equivalent(b))
 31    }
 32}
 33
 34pub trait MatrixGadget<FV> {
 35    fn mul_vector(&self, v: &[FV]) -> Result<Vec<FV>, SynthesisError>;
 36}
 37
 38pub trait VectorGadget<FV> {
 39    fn add(&self, other: &Self) -> Result<Vec<FV>, SynthesisError>;
 40
 41    fn mul_scalar(&self, other: &FV) -> Result<Vec<FV>, SynthesisError>;
 42
 43    fn hadamard(&self, other: &Self) -> Result<Vec<FV>, SynthesisError>;
 44}
 45
 46impl<F: PrimeField> VectorGadget<FpVar<F>> for [FpVar<F>] {
 47    fn add(&self, other: &Self) -> Result<Vec<FpVar<F>>, SynthesisError> {
 48        if self.len() != other.len() {
 49            return Err(SynthesisError::Unsatisfiable);
 50        }
 51        Ok(self.iter().zip(other.iter()).map(|(a, b)| a + b).collect())
 52    }
 53
 54    fn mul_scalar(&self, c: &FpVar<F>) -> Result<Vec<FpVar<F>>, SynthesisError> {
 55        Ok(self.iter().map(|a| a * c).collect())
 56    }
 57
 58    fn hadamard(&self, other: &Self) -> Result<Vec<FpVar<F>>, SynthesisError> {
 59        if self.len() != other.len() {
 60            return Err(SynthesisError::Unsatisfiable);
 61        }
 62        Ok(self.iter().zip(other.iter()).map(|(a, b)| a * b).collect())
 63    }
 64}
 65
 66#[derive(Debug, Clone)]
 67pub struct SparseMatrixVar<FV> {
 68    pub n_rows: usize,
 69    pub n_cols: usize,
 70    // same format as the native SparseMatrix (which follows ark_relations::r1cs::Matrix format
 71    pub coeffs: Vec<Vec<(FV, usize)>>,
 72}
 73
 74impl<F, CF, FV> AllocVar<SparseMatrix<F>, CF> for SparseMatrixVar<FV>
 75where
 76    F: PrimeField,
 77    CF: PrimeField,
 78    FV: AllocVar<F, CF>,
 79{
 80    fn new_variable<T: Borrow<SparseMatrix<F>>>(
 81        cs: impl Into<Namespace<CF>>,
 82        f: impl FnOnce() -> Result<T, SynthesisError>,
 83        mode: AllocationMode,
 84    ) -> Result<Self, SynthesisError> {
 85        f().and_then(|val| {
 86            let cs = cs.into();
 87
 88            let mut coeffs: Vec<Vec<(FV, usize)>> = Vec::new();
 89            for row in val.borrow().coeffs.iter() {
 90                let mut rowVar: Vec<(FV, usize)> = Vec::new();
 91                for &(value, col_i) in row.iter() {
 92                    let coeffVar = FV::new_variable(cs.clone(), || Ok(value), mode)?;
 93                    rowVar.push((coeffVar, col_i));
 94                }
 95                coeffs.push(rowVar);
 96            }
 97
 98            Ok(Self {
 99                n_rows: val.borrow().n_rows,
100                n_cols: val.borrow().n_cols,
101                coeffs,
102            })
103        })
104    }
105}
106
107impl<F: PrimeField> MatrixGadget<FpVar<F>> for SparseMatrixVar<FpVar<F>> {
108    fn mul_vector(&self, v: &[FpVar<F>]) -> Result<Vec<FpVar<F>>, SynthesisError> {
109        Ok(self
110            .coeffs
111            .iter()
112            .map(|row| {
113                let products = row
114                    .iter()
115                    .map(|(value, col_i)| value * &v[*col_i])
116                    .collect::<Vec<_>>();
117                if products.is_constant() {
118                    FpVar::constant(products.value().unwrap_or_default().into_iter().sum())
119                } else {
120                    products.iter().sum()
121                }
122            })
123            .collect())
124    }
125}
126
127/// Interprets the given vector v as the evaluations of a dense multilinear extension of n_vars,
128/// and evaluates it at the given point. This method mimics the behavior of
129/// `utils/mle.rs#dense_vec_to_dense_mle` + `DenseMultilinearExtension::evaluate` but in R1CS
130/// constraints, since dense multilinear extensions are not supported in ark_r1cs_std.
131pub fn eval_mle<F: PrimeField>(
132    // n_vars indicates the number of variables in the MLE
133    n_vars: usize,
134    // v is the vector of the evaluations of the dense multilinear extension (MLE)
135    v: Vec<FpVar<F>>,
136    // point is the point at which we want to evaluate the MLE
137    point: Vec<FpVar<F>>,
138) -> FpVar<F> {
139    // pad to 2^n_vars
140    let mut poly: Vec<FpVar<F>> = [
141        v.to_owned(),
142        std::iter::repeat(FpVar::zero())
143            .take((1 << n_vars) - v.len())
144            .collect(),
145    ]
146    .concat();
147
148    for i in 1..n_vars + 1 {
149        let r = point[i - 1].clone();
150        for b in 0..(1 << (n_vars - 1)) {
151            let left = poly[b << 1].clone();
152            let right = poly[(b << 1) + 1].clone();
153            poly[b] = left.clone() + r.clone() * (right - left);
154        }
155    }
156    poly[0].clone()
157}