Skip to main content

sonobe_primitives/arithmetizations/r1cs/
circuits.rs

1//! This module implements in-circuit R1CS variables and relation check gadgets.
2
3use ark_ff::{PrimeField, Zero};
4use ark_r1cs_std::alloc::{AllocVar, AllocationMode};
5use ark_relations::gr1cs::{Namespace, SynthesisError};
6use ark_std::{One, borrow::Borrow, ops::Mul};
7
8use super::R1CS;
9use crate::{
10    algebra::ops::{
11        eq::EquivalenceGadget,
12        matrix::{MatrixGadget, SparseMatrixVar},
13        vector::VectorGadget,
14    },
15    arithmetizations::ArithRelationGadget,
16    circuits::Assignments,
17};
18
19/// [`R1CSMatricesVar`] is the in-circuit variable of a given R1CS structure.
20///
21/// Only the matrices are represented, while the remaining R1CS parameters are
22/// constants to the circuit.
23///
24/// The naming is chosen to distinguish from arkworks' `(G)R1CSVar`.
25#[allow(non_snake_case)]
26#[derive(Debug, Clone)]
27pub struct R1CSMatricesVar<FVar> {
28    A: SparseMatrixVar<FVar>,
29    B: SparseMatrixVar<FVar>,
30    C: SparseMatrixVar<FVar>,
31}
32
33impl<F: PrimeField, ConstraintF: PrimeField, FVar: AllocVar<F, ConstraintF>>
34    AllocVar<R1CS<F>, ConstraintF> for R1CSMatricesVar<FVar>
35{
36    fn new_variable<T: Borrow<R1CS<F>>>(
37        cs: impl Into<Namespace<ConstraintF>>,
38        f: impl FnOnce() -> Result<T, SynthesisError>,
39        mode: AllocationMode,
40    ) -> Result<Self, SynthesisError> {
41        f().and_then(|val| {
42            let cs = cs.into();
43
44            let val = val.borrow();
45
46            Ok(Self {
47                A: SparseMatrixVar::<FVar>::new_variable(
48                    cs.clone(),
49                    || Ok(&val.matrices[0]),
50                    mode,
51                )?,
52                B: SparseMatrixVar::<FVar>::new_variable(
53                    cs.clone(),
54                    || Ok(&val.matrices[1]),
55                    mode,
56                )?,
57                C: SparseMatrixVar::<FVar>::new_variable(
58                    cs.clone(),
59                    || Ok(&val.matrices[2]),
60                    mode,
61                )?,
62            })
63        })
64    }
65}
66
67impl<FVar> R1CSMatricesVar<FVar>
68where
69    SparseMatrixVar<FVar>: MatrixGadget<FVar>,
70    [FVar]: VectorGadget<FVar>,
71    for<'a> &'a FVar: Mul<&'a FVar, Output = FVar>,
72{
73    /// [`R1CSMatricesVar::evaluate_r1cs`] is the in-circuit version of
74    /// [`R1CS::evaluate_r1cs`] that evaluates the R1CS variable at a given
75    /// vector of assignments `z`.
76    #[allow(non_snake_case)]
77    pub fn evaluate_r1cs(
78        &self,
79        z: Assignments<FVar, impl AsRef<[FVar]>>,
80    ) -> Result<Vec<FVar>, SynthesisError> {
81        // Multiply Cz by z[0] (u) here, allowing this method to be reused for
82        // both relaxed and plain R1CS.
83        let Az = self.A.mul_vector(&z)?;
84        let Bz = self.B.mul_vector(&z)?;
85        let Cz = self.C.mul_vector(&z)?;
86        let uCz = Cz.scale(&z[0])?;
87        let AzBz = Az.hadamard(&Bz)?;
88        AzBz.sub(&uCz)
89    }
90}
91
92impl<FVar, WVar: AsRef<[FVar]>, UVar: AsRef<[FVar]>> ArithRelationGadget<WVar, UVar>
93    for R1CSMatricesVar<FVar>
94where
95    SparseMatrixVar<FVar>: MatrixGadget<FVar>,
96    [FVar]: VectorGadget<FVar> + EquivalenceGadget<[FVar]>,
97    // TODO (@winderica): this will not work for our incoming decider
98    FVar: Clone + Zero + One,
99    for<'a> &'a FVar: Mul<&'a FVar, Output = FVar>,
100{
101    type Evaluation = Vec<FVar>;
102
103    fn eval_relation(&self, w: &WVar, u: &UVar) -> Result<Self::Evaluation, SynthesisError> {
104        self.evaluate_r1cs((FVar::one(), u.as_ref(), w.as_ref()).into())
105    }
106
107    fn check_evaluation(_w: &WVar, _u: &UVar, e: Self::Evaluation) -> Result<(), SynthesisError> {
108        e.enforce_equivalent(&vec![FVar::zero(); e.len()])
109    }
110}
111
112#[cfg(test)]
113mod tests {
114    use ark_bn254::Fr;
115    use ark_ff::{One, UniformRand, Zero};
116    use ark_std::{error::Error, rand::thread_rng};
117
118    use super::*;
119    use crate::{
120        circuits::utils::{constraints_for_test, satisfying_assignments_for_test},
121        relations::Relation,
122    };
123
124    #[test]
125    fn test_eval() -> Result<(), Box<dyn Error>> {
126        let mut rng = thread_rng();
127        let r1cs = constraints_for_test::<Fr>();
128
129        assert!(
130            r1cs.evaluate_r1cs(satisfying_assignments_for_test(Fr::rand(&mut rng)))?
131                .into_iter()
132                .all(|e| e.is_zero())
133        );
134        assert!(
135            !r1cs
136                .evaluate_r1cs(Assignments::from((
137                    Fr::one(),
138                    vec![Fr::rand(&mut rng)],
139                    vec![
140                        Fr::rand(&mut rng),
141                        Fr::rand(&mut rng),
142                        Fr::rand(&mut rng),
143                        Fr::rand(&mut rng),
144                    ],
145                )))?
146                .into_iter()
147                .all(|e| e.is_zero())
148        );
149
150        Ok(())
151    }
152
153    #[test]
154    fn test_check() -> Result<(), Box<dyn Error>> {
155        let mut rng = thread_rng();
156        let r1cs = constraints_for_test::<Fr>();
157
158        let assignments = satisfying_assignments_for_test(Fr::rand(&mut rng));
159
160        assert!(
161            r1cs.check_relation(&assignments.private, &assignments.public)
162                .is_ok()
163        );
164        assert!(
165            r1cs.check_relation(
166                &[
167                    Fr::rand(&mut rng),
168                    Fr::rand(&mut rng),
169                    Fr::rand(&mut rng),
170                    Fr::rand(&mut rng),
171                ],
172                &[Fr::rand(&mut rng)]
173            )
174            .is_err()
175        );
176
177        Ok(())
178    }
179}