Skip to main content

ark_relations/sr1cs/
mod.rs

1use ark_ff::{Field, One, Zero};
2use ark_std::{cfg_iter, vec::Vec};
3
4use crate::{
5    gr1cs::{
6        predicate::{self, polynomial_constraint::SR1CS_PREDICATE_LABEL},
7        ConstraintSystem, ConstraintSystemRef, LinearCombination, OptimizationGoal, SynthesisError,
8        SynthesisMode, Variable, R1CS_PREDICATE_LABEL,
9    },
10    lc, lc_diff,
11};
12use ark_std::{collections::BTreeMap, ops::AddAssign};
13
14#[cfg(feature = "parallel")]
15use rayon::prelude::*;
16
17/// Helper struct to enable easy conversion from R1CS to SR1CS
18pub struct Sr1csAdapter<F: Field> {
19    _phantom: core::marker::PhantomData<F>,
20}
21
22impl<F: Field> Sr1csAdapter<F> {
23    /// Computes the inner product of `terms` with `assignment`.
24    pub fn evaluate_constraint<'a, LHS, RHS, R>(
25        terms: &'a [(LHS, usize)],
26        assignment: &'a [RHS],
27    ) -> R
28    where
29        LHS: One + Send + Sync + PartialEq,
30        RHS: Send + Sync + core::ops::Mul<&'a LHS, Output = RHS> + Copy,
31        R: Zero + Send + Sync + AddAssign<RHS> + core::iter::Sum,
32    {
33        // Need to wrap in a closure when using Rayon
34        #[cfg(feature = "parallel")]
35        let zero = || R::zero();
36        #[cfg(not(feature = "parallel"))]
37        let zero = R::zero();
38
39        let res = cfg_iter!(terms).fold(zero, |mut sum, (coeff, index)| {
40            let val = &assignment[*index];
41
42            if coeff.is_one() {
43                sum += *val;
44            } else {
45                sum += val.mul(coeff);
46            }
47
48            sum
49        });
50
51        // Need to explicitly call `.sum()` when using Rayon
52        #[cfg(feature = "parallel")]
53        return res.sum();
54        #[cfg(not(feature = "parallel"))]
55        return res;
56    }
57
58    fn add_to_variable_maps(
59        constraint: &[(F, usize)],
60        public_variables: &mut BTreeMap<usize, Variable>,
61        witness_variables: &mut BTreeMap<usize, Variable>,
62        num_public: usize,
63        new_cs: &ConstraintSystemRef<F>,
64    ) -> LinearCombination<F> {
65        let lc = constraint
66            .iter()
67            .map(|(coeff, index)| {
68                let var = if *index == 0 {
69                    Variable::One
70                } else if *index < num_public {
71                    *public_variables
72                        .entry(*index)
73                        .or_insert_with(|| new_cs.new_witness_variable(|| Ok(F::ONE)).unwrap())
74                } else {
75                    *witness_variables
76                        .entry(*index)
77                        .or_insert_with(|| new_cs.new_witness_variable(|| Ok(F::ONE)).unwrap())
78                };
79                (*coeff, var)
80            })
81            .collect();
82        LinearCombination(lc)
83    }
84
85    fn add_to_variable_maps_witness(
86        constraint: &[(F, usize)],
87        public_variables: &mut BTreeMap<usize, Variable>,
88        witness_variables: &mut BTreeMap<usize, Variable>,
89        num_public: usize,
90        r1cs_assignment: &[F],
91        new_cs: &ConstraintSystemRef<F>,
92    ) -> (LinearCombination<F>, F) {
93        let (lc, vals): (Vec<_>, Vec<_>) = constraint
94            .iter()
95            .map(|(coeff, index)| {
96                let (var, val) = if *index == 0 {
97                    (Variable::One, F::ONE)
98                } else if *index < num_public {
99                    let val = r1cs_assignment[*index];
100                    let var = *public_variables
101                        .entry(*index)
102                        .or_insert_with(|| new_cs.new_witness_variable(|| Ok(val)).unwrap());
103                    (var, val)
104                } else {
105                    let val = r1cs_assignment[*index];
106                    let var = *witness_variables
107                        .entry(*index)
108                        .or_insert_with(|| new_cs.new_witness_variable(|| Ok(val)).unwrap());
109                    (var, val)
110                };
111                ((*coeff, var), *coeff * val)
112            })
113            .unzip();
114        let val = vals.into_iter().sum::<F>();
115        (LinearCombination(lc), val)
116    }
117
118    /// Converts an R1CS constraint system to an SR1CS constraint system.
119    ///
120    /// # Panics
121    ///
122    /// Panics if the constraint system does not have the R1CS predicate registered or
123    /// if there is more than one predicate registered.
124    pub fn r1cs_to_sr1cs(
125        cs: &ConstraintSystemRef<F>,
126    ) -> Result<ConstraintSystemRef<F>, SynthesisError> {
127        assert_eq!(cs.num_predicates(), 1);
128        let matrices = &cs.to_matrices().unwrap()[R1CS_PREDICATE_LABEL];
129        let mut public_variables = BTreeMap::new();
130        let mut witness_variables = BTreeMap::new();
131        let num_public = cs.num_instance_variables();
132
133        let new_cs = ConstraintSystem::new_ref();
134        // new_cs.set_optimization_goal(OptimizationGoal::Constraints);
135        new_cs.set_mode(SynthesisMode::Setup);
136        new_cs.remove_predicate(R1CS_PREDICATE_LABEL);
137        let _ = new_cs.register_predicate(
138            SR1CS_PREDICATE_LABEL,
139            predicate::PredicateConstraintSystem::new_sr1cs_predicate().unwrap(),
140        );
141        for ((a_i, b_i), c_i) in matrices[0].iter().zip(&matrices[1]).zip(&matrices[2]) {
142            let a_i = Self::add_to_variable_maps(
143                a_i,
144                &mut public_variables,
145                &mut witness_variables,
146                num_public,
147                &new_cs,
148            );
149            let b_i = Self::add_to_variable_maps(
150                b_i,
151                &mut public_variables,
152                &mut witness_variables,
153                num_public,
154                &new_cs,
155            );
156            let mut c_i = Self::add_to_variable_maps(
157                c_i,
158                &mut public_variables,
159                &mut witness_variables,
160                num_public,
161                &new_cs,
162            );
163            let square_variable = new_cs.new_witness_variable(|| Ok(F::ONE))?;
164
165            let left_1 = || a_i.clone() + &b_i;
166            c_i.0.iter_mut().for_each(|(c, _)| {
167                c.double_in_place().double_in_place();
168            });
169            let right_1 = || c_i + square_variable;
170            new_cs.enforce_sr1cs_constraint(left_1, right_1)?;
171
172            let left_2 = || a_i - b_i;
173            let right_2 = || lc![square_variable];
174            new_cs.enforce_sr1cs_constraint(left_2, right_2)?;
175        }
176
177        for old_var in public_variables.values() {
178            let new_var = new_cs.new_input_variable(|| Ok(F::ONE))?;
179            let lc = || lc_diff![*old_var, new_var];
180            new_cs.enforce_sr1cs_constraint(lc, || lc![])?;
181        }
182        Ok(new_cs)
183    }
184
185    /// Converts an R1CS constraint system to an SR1CS constraint system,
186    /// while also converting the R1CS assignment to an equivalent SR1CS assignment.
187    ///
188    /// # Panics
189    ///
190    /// Panics is the constraint system does not have the R1CS predicate registered.
191    pub fn r1cs_to_sr1cs_with_assignment(
192        cs: &mut ConstraintSystem<F>,
193    ) -> Result<ConstraintSystemRef<F>, SynthesisError> {
194        let matrices = &cs.to_matrices().unwrap()[R1CS_PREDICATE_LABEL];
195        let mut public_variables = BTreeMap::new();
196        let mut witness_variables = BTreeMap::new();
197        let num_public = cs.num_instance_variables();
198
199        let mut r1cs_assignment = cs.assignments.instance_assignment.clone();
200        r1cs_assignment.extend_from_slice(&cs.assignments.witness_assignment);
201
202        let new_cs = ConstraintSystem::new_ref();
203        new_cs.remove_predicate(R1CS_PREDICATE_LABEL);
204        let _ = new_cs.register_predicate(
205            SR1CS_PREDICATE_LABEL,
206            predicate::PredicateConstraintSystem::new_sr1cs_predicate().unwrap(),
207        );
208        new_cs.set_optimization_goal(OptimizationGoal::Constraints);
209        cs.set_mode(SynthesisMode::Prove {
210            construct_matrices: true,
211            generate_lc_assignments: true,
212        });
213        for ((a_i, b_i), c_i) in matrices[0].iter().zip(&matrices[1]).zip(&matrices[2]) {
214            let (a_i, a_val) = Self::add_to_variable_maps_witness(
215                a_i,
216                &mut public_variables,
217                &mut witness_variables,
218                num_public,
219                &r1cs_assignment,
220                &new_cs,
221            );
222            let (b_i, b_val) = Self::add_to_variable_maps_witness(
223                b_i,
224                &mut public_variables,
225                &mut witness_variables,
226                num_public,
227                &r1cs_assignment,
228                &new_cs,
229            );
230            let (mut c_i, _c_val) = Self::add_to_variable_maps_witness(
231                c_i,
232                &mut public_variables,
233                &mut witness_variables,
234                num_public,
235                &r1cs_assignment,
236                &new_cs,
237            );
238            let square_variable = new_cs.new_witness_variable(|| Ok((a_val - b_val).square()))?;
239
240            let left_1 = || a_i.clone() + &b_i;
241            c_i.0.iter_mut().for_each(|(c, _)| {
242                c.double_in_place().double_in_place();
243            });
244            let right_1 = || c_i + square_variable;
245            new_cs.enforce_sr1cs_constraint(left_1, right_1)?;
246
247            let left_2 = || a_i - &b_i;
248            let right_2 = || lc![square_variable];
249            new_cs.enforce_sr1cs_constraint(left_2, right_2)?;
250        }
251
252        let mut public_variable_polys = Vec::new();
253        for old_var in public_variables.values() {
254            let value = new_cs
255                .assigned_value(*old_var)
256                .ok_or(SynthesisError::AssignmentMissing)?;
257            let constraint_number = new_cs.num_constraints();
258            let new_var = new_cs.new_input_variable(|| Ok(value))?;
259            let lc = || lc_diff![*old_var, new_var];
260            new_cs.enforce_sr1cs_constraint(lc, || lc![])?;
261            public_variable_polys.push(constraint_number);
262        }
263        new_cs.finalize();
264        Ok(new_cs)
265    }
266}
267
268#[cfg(test)]
269mod tests {
270    use crate::gr1cs::ConstraintSynthesizer;
271    use ark_ff::PrimeField;
272    use ark_test_curves::bls12_381::Fr;
273
274    use super::*;
275
276    struct DummyCircuit<F: PrimeField> {
277        pub a: Option<F>,
278        pub b: Option<F>,
279        pub num_variables: usize,
280        pub num_constraints: usize,
281    }
282
283    impl<F: PrimeField> Clone for DummyCircuit<F> {
284        fn clone(&self) -> Self {
285            DummyCircuit {
286                a: self.a,
287                b: self.b,
288                num_variables: self.num_variables,
289                num_constraints: self.num_constraints,
290            }
291        }
292    }
293
294    impl<F: PrimeField> Copy for DummyCircuit<F> {}
295    impl<F: PrimeField> ConstraintSynthesizer<F> for DummyCircuit<F> {
296        fn generate_constraints(self, cs: ConstraintSystemRef<F>) -> Result<(), SynthesisError> {
297            let a = cs.new_witness_variable(|| self.a.ok_or(SynthesisError::AssignmentMissing))?;
298            let b = cs.new_witness_variable(|| self.b.ok_or(SynthesisError::AssignmentMissing))?;
299            let c = cs.new_input_variable(|| {
300                let a = self.a.ok_or(SynthesisError::AssignmentMissing)?;
301                let b = self.b.ok_or(SynthesisError::AssignmentMissing)?;
302
303                Ok(a * b)
304            })?;
305
306            for _ in 0..(self.num_variables - 3) {
307                let _ =
308                    cs.new_witness_variable(|| self.a.ok_or(SynthesisError::AssignmentMissing))?;
309            }
310
311            for _ in 0..self.num_constraints - 1 {
312                cs.enforce_r1cs_constraint(|| lc![a], || lc![b], || lc![c])?;
313            }
314
315            cs.enforce_r1cs_constraint(|| lc![], || lc![], || lc![])?;
316
317            Ok(())
318        }
319    }
320    #[test]
321    fn r1cs_to_sr1cs() {
322        let cs = ConstraintSystem::<Fr>::new_ref();
323        let circuit = DummyCircuit {
324            a: Some(3u64.into()),
325            b: Some(5u64.into()),
326            num_variables: 128,
327            num_constraints: 128,
328        };
329        circuit.generate_constraints(cs.clone()).unwrap();
330    }
331}