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
17pub struct Sr1csAdapter<F: Field> {
19 _phantom: core::marker::PhantomData<F>,
20}
21
22impl<F: Field> Sr1csAdapter<F> {
23 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 #[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 #[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 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_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 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}