ark_relations/r1cs/constraint_system.rs
1#[cfg(feature = "std")]
2use crate::r1cs::ConstraintTrace;
3use crate::r1cs::{LcIndex, LinearCombination, Matrix, SynthesisError, Variable};
4use ark_ff::Field;
5use ark_std::{
6 any::{Any, TypeId},
7 boxed::Box,
8 cell::{Ref, RefCell, RefMut},
9 collections::BTreeMap,
10 format,
11 rc::Rc,
12 string::String,
13 vec,
14 vec::Vec,
15};
16
17/// Computations are expressed in terms of rank-1 constraint systems (R1CS).
18/// The `generate_constraints` method is called to generate constraints for
19/// both CRS generation and for proving.
20// TODO: Think: should we replace this with just a closure?
21pub trait ConstraintSynthesizer<F: Field> {
22 /// Drives generation of new constraints inside `cs`.
23 fn generate_constraints(self, cs: ConstraintSystemRef<F>) -> crate::r1cs::Result<()>;
24}
25
26/// An Rank-One `ConstraintSystem`. Enforces constraints of the form
27/// `⟨a_i, z⟩ ⋅ ⟨b_i, z⟩ = ⟨c_i, z⟩`, where `a_i`, `b_i`, and `c_i` are linear
28/// combinations over variables, and `z` is the concrete assignment to these
29/// variables.
30#[derive(Debug, Clone)]
31pub struct ConstraintSystem<F: Field> {
32 /// The mode in which the constraint system is operating. `self` can either
33 /// be in setup mode (i.e., `self.mode == SynthesisMode::Setup`) or in
34 /// proving mode (i.e., `self.mode == SynthesisMode::Prove`). If we are
35 /// in proving mode, then we have the additional option of whether or
36 /// not to construct the A, B, and C matrices of the constraint system
37 /// (see below).
38 pub mode: SynthesisMode,
39 /// The number of variables that are "public inputs" to the constraint
40 /// system.
41 pub num_instance_variables: usize,
42 /// The number of variables that are "private inputs" to the constraint
43 /// system.
44 pub num_witness_variables: usize,
45 /// The number of constraints in the constraint system.
46 pub num_constraints: usize,
47 /// The number of linear combinations
48 pub num_linear_combinations: usize,
49
50 /// The parameter we aim to minimize in this constraint system (either the
51 /// number of constraints or their total weight).
52 pub optimization_goal: OptimizationGoal,
53
54 /// Assignments to the public input variables. This is empty if `self.mode
55 /// == SynthesisMode::Setup`.
56 pub instance_assignment: Vec<F>,
57 /// Assignments to the private input variables. This is empty if `self.mode
58 /// == SynthesisMode::Setup`.
59 pub witness_assignment: Vec<F>,
60
61 /// Map for gadgets to cache computation results.
62 pub cache_map: Rc<RefCell<BTreeMap<TypeId, Box<dyn Any>>>>,
63
64 lc_map: BTreeMap<LcIndex, LinearCombination<F>>,
65
66 #[cfg(feature = "std")]
67 constraint_traces: Vec<Option<ConstraintTrace>>,
68
69 a_constraints: Vec<LcIndex>,
70 b_constraints: Vec<LcIndex>,
71 c_constraints: Vec<LcIndex>,
72
73 lc_assignment_cache: Rc<RefCell<BTreeMap<LcIndex, F>>>,
74}
75
76impl<F: Field> Default for ConstraintSystem<F> {
77 fn default() -> Self {
78 Self::new()
79 }
80}
81
82/// Defines the mode of operation of a `ConstraintSystem`.
83#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
84pub enum SynthesisMode {
85 /// Indicate to the `ConstraintSystem` that it should only generate
86 /// constraint matrices and not populate the variable assignments.
87 Setup,
88 /// Indicate to the `ConstraintSystem` that it populate the variable
89 /// assignments. If additionally `construct_matrices == true`, then generate
90 /// the matrices as in the `Setup` case.
91 Prove {
92 /// If `construct_matrices == true`, then generate
93 /// the matrices as in the `Setup` case.
94 construct_matrices: bool,
95 },
96}
97
98/// Defines the parameter to optimize for a `ConstraintSystem`.
99#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
100pub enum OptimizationGoal {
101 /// Make no attempt to optimize.
102 None,
103 /// Minimize the number of constraints.
104 Constraints,
105 /// Minimize the total weight of the constraints (the number of nonzero
106 /// entries across all constraints).
107 Weight,
108}
109
110impl<F: Field> ConstraintSystem<F> {
111 #[inline]
112 fn make_row(&self, l: &LinearCombination<F>) -> Vec<(F, usize)> {
113 let num_input = self.num_instance_variables;
114 l.0.iter()
115 .filter_map(|(coeff, var)| {
116 if coeff.is_zero() {
117 None
118 } else {
119 Some((
120 *coeff,
121 var.get_index_unchecked(num_input).expect("no symbolic LCs"),
122 ))
123 }
124 })
125 .collect()
126 }
127
128 /// Construct an empty `ConstraintSystem`.
129 pub fn new() -> Self {
130 Self {
131 num_instance_variables: 1,
132 num_witness_variables: 0,
133 num_constraints: 0,
134 num_linear_combinations: 0,
135 a_constraints: Vec::new(),
136 b_constraints: Vec::new(),
137 c_constraints: Vec::new(),
138 instance_assignment: vec![F::one()],
139 witness_assignment: Vec::new(),
140 cache_map: Rc::new(RefCell::new(BTreeMap::new())),
141 #[cfg(feature = "std")]
142 constraint_traces: Vec::new(),
143
144 lc_map: BTreeMap::new(),
145 lc_assignment_cache: Rc::new(RefCell::new(BTreeMap::new())),
146
147 mode: SynthesisMode::Prove {
148 construct_matrices: true,
149 },
150
151 optimization_goal: OptimizationGoal::Constraints,
152 }
153 }
154
155 /// Create a new `ConstraintSystemRef<F>`.
156 pub fn new_ref() -> ConstraintSystemRef<F> {
157 ConstraintSystemRef::new(Self::new())
158 }
159
160 /// Set `self.mode` to `mode`.
161 pub fn set_mode(&mut self, mode: SynthesisMode) {
162 self.mode = mode;
163 }
164
165 /// Check whether `self.mode == SynthesisMode::Setup`.
166 pub fn is_in_setup_mode(&self) -> bool {
167 self.mode == SynthesisMode::Setup
168 }
169
170 /// Check whether this constraint system aims to optimize weight,
171 /// number of constraints, or neither.
172 pub fn optimization_goal(&self) -> OptimizationGoal {
173 self.optimization_goal
174 }
175
176 /// Specify whether this constraint system should aim to optimize weight,
177 /// number of constraints, or neither.
178 pub fn set_optimization_goal(&mut self, goal: OptimizationGoal) {
179 // `set_optimization_goal` should only be executed before any constraint or value is created.
180 assert_eq!(self.num_instance_variables, 1);
181 assert_eq!(self.num_witness_variables, 0);
182 assert_eq!(self.num_constraints, 0);
183 assert_eq!(self.num_linear_combinations, 0);
184
185 self.optimization_goal = goal;
186 }
187
188 /// Check whether or not `self` will construct matrices.
189 pub fn should_construct_matrices(&self) -> bool {
190 match self.mode {
191 SynthesisMode::Setup => true,
192 SynthesisMode::Prove { construct_matrices } => construct_matrices,
193 }
194 }
195
196 /// Return a variable representing the constant "zero" inside the constraint
197 /// system.
198 #[inline]
199 pub fn zero() -> Variable {
200 Variable::Zero
201 }
202
203 /// Return a variable representing the constant "one" inside the constraint
204 /// system.
205 #[inline]
206 pub fn one() -> Variable {
207 Variable::One
208 }
209
210 /// Obtain a variable representing a new public instance input.
211 #[inline]
212 pub fn new_input_variable<Func>(&mut self, f: Func) -> crate::r1cs::Result<Variable>
213 where
214 Func: FnOnce() -> crate::r1cs::Result<F>,
215 {
216 let index = self.num_instance_variables;
217 self.num_instance_variables += 1;
218
219 if !self.is_in_setup_mode() {
220 self.instance_assignment.push(f()?);
221 }
222 Ok(Variable::Instance(index))
223 }
224
225 /// Obtain a variable representing a new private witness input.
226 #[inline]
227 pub fn new_witness_variable<Func>(&mut self, f: Func) -> crate::r1cs::Result<Variable>
228 where
229 Func: FnOnce() -> crate::r1cs::Result<F>,
230 {
231 let index = self.num_witness_variables;
232 self.num_witness_variables += 1;
233
234 if !self.is_in_setup_mode() {
235 self.witness_assignment.push(f()?);
236 }
237 Ok(Variable::Witness(index))
238 }
239
240 /// Obtain a variable representing a linear combination.
241 #[inline]
242 pub fn new_lc(&mut self, lc: LinearCombination<F>) -> crate::r1cs::Result<Variable> {
243 let index = LcIndex(self.num_linear_combinations);
244 let var = Variable::SymbolicLc(index);
245
246 self.lc_map.insert(index, lc);
247
248 self.num_linear_combinations += 1;
249 Ok(var)
250 }
251
252 /// Enforce a R1CS constraint with the name `name`.
253 #[inline]
254 pub fn enforce_constraint(
255 &mut self,
256 a: LinearCombination<F>,
257 b: LinearCombination<F>,
258 c: LinearCombination<F>,
259 ) -> crate::r1cs::Result<()> {
260 if self.should_construct_matrices() {
261 let a_index = self.new_lc(a)?.get_lc_index().unwrap();
262 let b_index = self.new_lc(b)?.get_lc_index().unwrap();
263 let c_index = self.new_lc(c)?.get_lc_index().unwrap();
264 self.a_constraints.push(a_index);
265 self.b_constraints.push(b_index);
266 self.c_constraints.push(c_index);
267 }
268 self.num_constraints += 1;
269 #[cfg(feature = "std")]
270 {
271 let trace = ConstraintTrace::capture();
272 self.constraint_traces.push(trace);
273 }
274 Ok(())
275 }
276
277 /// Count the number of times each LC is used within other LCs in the
278 /// constraint system
279 fn lc_num_times_used(&self, count_sinks: bool) -> Vec<usize> {
280 let mut num_times_used = vec![0; self.lc_map.len()];
281
282 // Iterate over every lc in constraint system
283 for (index, lc) in self.lc_map.iter() {
284 num_times_used[index.0] += count_sinks as usize;
285
286 // Increment the counter for each lc that this lc has a direct dependency on.
287 for &(_, var) in lc.iter() {
288 if var.is_lc() {
289 let lc_index = var.get_lc_index().expect("should be lc");
290 num_times_used[lc_index.0] += 1;
291 }
292 }
293 }
294 num_times_used
295 }
296
297 /// Transform the map of linear combinations.
298 /// Specifically, allow the creation of additional witness assignments.
299 ///
300 /// This method is used as a subroutine of `inline_all_lcs` and `outline_lcs`.
301 ///
302 /// The transformer function is given a references of this constraint system (&self),
303 /// number of times used, and a mutable reference of the linear combination to be transformed.
304 /// (&ConstraintSystem<F>, usize, &mut LinearCombination<F>)
305 ///
306 /// The transformer function returns the number of new witness variables needed
307 /// and a vector of new witness assignments (if not in the setup mode).
308 /// (usize, Option<Vec<F>>)
309 pub fn transform_lc_map(
310 &mut self,
311 transformer: &mut dyn FnMut(
312 &ConstraintSystem<F>,
313 usize,
314 &mut LinearCombination<F>,
315 ) -> (usize, Option<Vec<F>>),
316 ) {
317 // `transformed_lc_map` stores the transformed linear combinations.
318 let mut transformed_lc_map = BTreeMap::<_, LinearCombination<F>>::new();
319 let mut num_times_used = self.lc_num_times_used(false);
320
321 // This loop goes through all the LCs in the map, starting from
322 // the early ones. The transformer function is applied to the
323 // inlined LC, where new witness variables can be created.
324 for (&index, lc) in &self.lc_map {
325 let mut transformed_lc = LinearCombination::new();
326
327 // Inline the LC, unwrapping symbolic LCs that may constitute it,
328 // and updating them according to transformations in prior iterations.
329 for &(coeff, var) in lc.iter() {
330 if var.is_lc() {
331 let lc_index = var.get_lc_index().expect("should be lc");
332
333 // If `var` is a `SymbolicLc`, fetch the corresponding
334 // inlined LC, and substitute it in.
335 //
336 // We have the guarantee that `lc_index` must exist in
337 // `new_lc_map` since a LC can only depend on other
338 // LCs with lower indices, which we have transformed.
339 //
340 let lc = transformed_lc_map
341 .get(&lc_index)
342 .expect("should be inlined");
343 transformed_lc.extend((lc * coeff).0.into_iter());
344
345 // Delete linear combinations that are no longer used.
346 //
347 // Deletion is safe for both outlining and inlining:
348 // * Inlining: the LC is substituted directly into all use sites, and so once it
349 // is fully inlined, it is redundant.
350 //
351 // * Outlining: the LC is associated with a new variable `w`, and a new
352 // constraint of the form `lc_data * 1 = w`, where `lc_data` is the actual
353 // data in the linear combination. Furthermore, we replace its entry in
354 // `new_lc_map` with `(1, w)`. Once `w` is fully inlined, then we can delete
355 // the entry from `new_lc_map`
356 //
357 num_times_used[lc_index.0] -= 1;
358 if num_times_used[lc_index.0] == 0 {
359 // This lc is not used any more, so remove it.
360 transformed_lc_map.remove(&lc_index);
361 }
362 } else {
363 // Otherwise, it's a concrete variable and so we
364 // substitute it in directly.
365 transformed_lc.push((coeff, var));
366 }
367 }
368 transformed_lc.compactify();
369
370 // Call the transformer function.
371 let (num_new_witness_variables, new_witness_assignments) =
372 transformer(&self, num_times_used[index.0], &mut transformed_lc);
373
374 // Insert the transformed LC.
375 transformed_lc_map.insert(index, transformed_lc);
376
377 // Update the witness counter.
378 self.num_witness_variables += num_new_witness_variables;
379
380 // Supply additional witness assignments if not in the
381 // setup mode and if new witness variables are created.
382 if !self.is_in_setup_mode() && num_new_witness_variables > 0 {
383 assert!(new_witness_assignments.is_some());
384 if let Some(new_witness_assignments) = new_witness_assignments {
385 assert_eq!(new_witness_assignments.len(), num_new_witness_variables);
386 self.witness_assignment
387 .extend_from_slice(&new_witness_assignments);
388 }
389 }
390 }
391 // Replace the LC map.
392 self.lc_map = transformed_lc_map;
393 }
394
395 /// Naively inlines symbolic linear combinations into the linear
396 /// combinations that use them.
397 ///
398 /// Useful for standard pairing-based SNARKs where addition gates are cheap.
399 /// For example, in the SNARKs such as [\[Groth16\]](https://eprint.iacr.org/2016/260) and
400 /// [\[Groth-Maller17\]](https://eprint.iacr.org/2017/540), addition gates
401 /// do not contribute to the size of the multi-scalar multiplication, which
402 /// is the dominating cost.
403 pub fn inline_all_lcs(&mut self) {
404 // Only inline when a matrix representing R1CS is needed.
405 if !self.should_construct_matrices() {
406 return;
407 }
408
409 // A dummy closure is used, which means that
410 // - it does not modify the inlined LC.
411 // - it does not add new witness variables.
412 self.transform_lc_map(&mut |_, _, _| (0, None));
413 }
414
415 /// If a `SymbolicLc` is used in more than one location and has sufficient
416 /// length, this method makes a new variable for that `SymbolicLc`, adds
417 /// a constraint ensuring the equality of the variable and the linear
418 /// combination, and then uses that variable in every location the
419 /// `SymbolicLc` is used.
420 ///
421 /// Useful for SNARKs like [\[Marlin\]](https://eprint.iacr.org/2019/1047) or
422 /// [\[Fractal\]](https://eprint.iacr.org/2019/1076), where addition gates
423 /// are not cheap.
424 fn outline_lcs(&mut self) {
425 // Only inline when a matrix representing R1CS is needed.
426 if !self.should_construct_matrices() {
427 return;
428 }
429
430 // Store information about new witness variables created
431 // for outlining. New constraints will be added after the
432 // transformation of the LC map.
433 let mut new_witness_linear_combinations = Vec::new();
434 let mut new_witness_indices = Vec::new();
435
436 // It goes through all the LCs in the map, starting from
437 // the early ones, and decides whether or not to dedicate a witness
438 // variable for this LC.
439 //
440 // If true, the LC is replaced with 1 * this witness variable.
441 // Otherwise, the LC is inlined.
442 //
443 // Each iteration first updates the LC according to outlinings in prior
444 // iterations, and then sees if it should be outlined, and if so adds
445 // the outlining to the map.
446 //
447 self.transform_lc_map(&mut |cs, num_times_used, inlined_lc| {
448 let mut should_dedicate_a_witness_variable = false;
449 let mut new_witness_index = None;
450 let mut new_witness_assignment = Vec::new();
451
452 // Check if it is worthwhile to dedicate a witness variable.
453 let this_used_times = num_times_used + 1;
454 let this_len = inlined_lc.len();
455
456 // Cost with no outlining = `lc_len * number of usages`
457 // Cost with outlining is one constraint for `(lc_len) * 1 = {new variable}` and
458 // using that single new variable in each of the prior usages.
459 // This has total cost `number_of_usages + lc_len + 2`
460 if this_used_times * this_len > this_used_times + 2 + this_len {
461 should_dedicate_a_witness_variable = true;
462 }
463
464 // If it is worthwhile to dedicate a witness variable,
465 if should_dedicate_a_witness_variable {
466 // Add a new witness (the value of the linear combination).
467 // This part follows the same logic of `new_witness_variable`.
468 let witness_index = cs.num_witness_variables;
469 new_witness_index = Some(witness_index);
470
471 // Compute the witness assignment.
472 if !cs.is_in_setup_mode() {
473 let mut acc = F::zero();
474 for (coeff, var) in inlined_lc.iter() {
475 acc += *coeff * &cs.assigned_value(*var).unwrap();
476 }
477 new_witness_assignment.push(acc);
478 }
479
480 // Add a new constraint for this new witness.
481 new_witness_linear_combinations.push(inlined_lc.clone());
482 new_witness_indices.push(witness_index);
483
484 // Replace the linear combination with (1 * this new witness).
485 *inlined_lc = LinearCombination::from(Variable::Witness(witness_index));
486 }
487 // Otherwise, the LC remains unchanged.
488
489 // Return information about new witness variables.
490 if new_witness_index.is_some() {
491 (1, Some(new_witness_assignment))
492 } else {
493 (0, None)
494 }
495 });
496
497 // Add the constraints for the newly added witness variables.
498 for (new_witness_linear_combination, new_witness_variable) in
499 new_witness_linear_combinations
500 .iter()
501 .zip(new_witness_indices.iter())
502 {
503 // Add a new constraint
504 self.enforce_constraint(
505 new_witness_linear_combination.clone(),
506 LinearCombination::from(Self::one()),
507 LinearCombination::from(Variable::Witness(*new_witness_variable)),
508 )
509 .unwrap();
510 }
511 }
512
513 /// Finalize the constraint system (either by outlining or inlining,
514 /// if an optimization goal is set).
515 pub fn finalize(&mut self) {
516 match self.optimization_goal {
517 OptimizationGoal::None => self.inline_all_lcs(),
518 OptimizationGoal::Constraints => self.inline_all_lcs(),
519 OptimizationGoal::Weight => self.outline_lcs(),
520 };
521 }
522
523 /// This step must be called after constraint generation has completed, and
524 /// after all symbolic LCs have been inlined into the places that they
525 /// are used.
526 pub fn to_matrices(&self) -> Option<ConstraintMatrices<F>> {
527 if let SynthesisMode::Prove {
528 construct_matrices: false,
529 } = self.mode
530 {
531 None
532 } else {
533 let a: Vec<_> = self
534 .a_constraints
535 .iter()
536 .map(|index| self.make_row(self.lc_map.get(index).unwrap()))
537 .collect();
538 let b: Vec<_> = self
539 .b_constraints
540 .iter()
541 .map(|index| self.make_row(self.lc_map.get(index).unwrap()))
542 .collect();
543 let c: Vec<_> = self
544 .c_constraints
545 .iter()
546 .map(|index| self.make_row(self.lc_map.get(index).unwrap()))
547 .collect();
548
549 let a_num_non_zero: usize = a.iter().map(|lc| lc.len()).sum();
550 let b_num_non_zero: usize = b.iter().map(|lc| lc.len()).sum();
551 let c_num_non_zero: usize = c.iter().map(|lc| lc.len()).sum();
552 let matrices = ConstraintMatrices {
553 num_instance_variables: self.num_instance_variables,
554 num_witness_variables: self.num_witness_variables,
555 num_constraints: self.num_constraints,
556
557 a_num_non_zero,
558 b_num_non_zero,
559 c_num_non_zero,
560
561 a,
562 b,
563 c,
564 };
565 Some(matrices)
566 }
567 }
568
569 fn eval_lc(&self, lc: LcIndex) -> Option<F> {
570 let lc = self.lc_map.get(&lc)?;
571 let mut acc = F::zero();
572 for (coeff, var) in lc.iter() {
573 acc += *coeff * self.assigned_value(*var)?;
574 }
575 Some(acc)
576 }
577
578 /// If `self` is satisfied, outputs `Ok(true)`.
579 /// If `self` is unsatisfied, outputs `Ok(false)`.
580 /// If `self.is_in_setup_mode()`, outputs `Err(())`.
581 pub fn is_satisfied(&self) -> crate::r1cs::Result<bool> {
582 self.which_is_unsatisfied().map(|s| s.is_none())
583 }
584
585 /// If `self` is satisfied, outputs `Ok(None)`.
586 /// If `self` is unsatisfied, outputs `Some(i)`, where `i` is the index of
587 /// the first unsatisfied constraint. If `self.is_in_setup_mode()`, outputs
588 /// `Err(())`.
589 pub fn which_is_unsatisfied(&self) -> crate::r1cs::Result<Option<String>> {
590 if self.is_in_setup_mode() {
591 Err(SynthesisError::AssignmentMissing)
592 } else {
593 for i in 0..self.num_constraints {
594 let a = self
595 .eval_lc(self.a_constraints[i])
596 .ok_or(SynthesisError::AssignmentMissing)?;
597 let b = self
598 .eval_lc(self.b_constraints[i])
599 .ok_or(SynthesisError::AssignmentMissing)?;
600 let c = self
601 .eval_lc(self.c_constraints[i])
602 .ok_or(SynthesisError::AssignmentMissing)?;
603 if a * b != c {
604 let trace;
605 #[cfg(feature = "std")]
606 {
607 trace = self.constraint_traces[i].as_ref().map_or_else(
608 || {
609 eprintln!("Constraint trace requires enabling `ConstraintLayer`");
610 format!("{}", i)
611 },
612 |t| format!("{}", t),
613 );
614 }
615 #[cfg(not(feature = "std"))]
616 {
617 trace = format!("{}", i);
618 }
619 return Ok(Some(trace));
620 }
621 }
622 Ok(None)
623 }
624 }
625
626 /// Obtain the assignment corresponding to the `Variable` `v`.
627 pub fn assigned_value(&self, v: Variable) -> Option<F> {
628 match v {
629 Variable::One => Some(F::one()),
630 Variable::Zero => Some(F::zero()),
631 Variable::Witness(idx) => self.witness_assignment.get(idx).copied(),
632 Variable::Instance(idx) => self.instance_assignment.get(idx).copied(),
633 Variable::SymbolicLc(idx) => {
634 let value = self.lc_assignment_cache.borrow().get(&idx).copied();
635 if value.is_some() {
636 value
637 } else {
638 let value = self.eval_lc(idx)?;
639 self.lc_assignment_cache.borrow_mut().insert(idx, value);
640 Some(value)
641 }
642 },
643 }
644 }
645}
646/// The A, B and C matrices of a Rank-One `ConstraintSystem`.
647/// Also contains metadata on the structure of the constraint system
648/// and the matrices.
649#[derive(Debug, Clone, PartialEq, Eq)]
650pub struct ConstraintMatrices<F: Field> {
651 /// The number of variables that are "public instances" to the constraint
652 /// system.
653 pub num_instance_variables: usize,
654 /// The number of variables that are "private witnesses" to the constraint
655 /// system.
656 pub num_witness_variables: usize,
657 /// The number of constraints in the constraint system.
658 pub num_constraints: usize,
659 /// The number of non_zero entries in the A matrix.
660 pub a_num_non_zero: usize,
661 /// The number of non_zero entries in the B matrix.
662 pub b_num_non_zero: usize,
663 /// The number of non_zero entries in the C matrix.
664 pub c_num_non_zero: usize,
665
666 /// The A constraint matrix. This is empty when
667 /// `self.mode == SynthesisMode::Prove { construct_matrices = false }`.
668 pub a: Matrix<F>,
669 /// The B constraint matrix. This is empty when
670 /// `self.mode == SynthesisMode::Prove { construct_matrices = false }`.
671 pub b: Matrix<F>,
672 /// The C constraint matrix. This is empty when
673 /// `self.mode == SynthesisMode::Prove { construct_matrices = false }`.
674 pub c: Matrix<F>,
675}
676
677/// A shared reference to a constraint system that can be stored in high level
678/// variables.
679#[derive(Debug, Clone)]
680pub enum ConstraintSystemRef<F: Field> {
681 /// Represents the case where we *don't* need to allocate variables or
682 /// enforce constraints. Encountered when operating over constant
683 /// values.
684 None,
685 /// Represents the case where we *do* allocate variables or enforce
686 /// constraints.
687 CS(Rc<RefCell<ConstraintSystem<F>>>),
688}
689
690impl<F: Field> PartialEq for ConstraintSystemRef<F> {
691 fn eq(&self, other: &Self) -> bool {
692 match (self, other) {
693 (Self::None, Self::None) => true,
694 (..) => false,
695 }
696 }
697}
698
699impl<F: Field> Eq for ConstraintSystemRef<F> {}
700
701/// A namespaced `ConstraintSystemRef`.
702#[derive(Debug, Clone)]
703pub struct Namespace<F: Field> {
704 inner: ConstraintSystemRef<F>,
705 id: Option<tracing::Id>,
706}
707
708impl<F: Field> From<ConstraintSystemRef<F>> for Namespace<F> {
709 fn from(other: ConstraintSystemRef<F>) -> Self {
710 Self {
711 inner: other,
712 id: None,
713 }
714 }
715}
716
717impl<F: Field> Namespace<F> {
718 /// Construct a new `Namespace`.
719 pub fn new(inner: ConstraintSystemRef<F>, id: Option<tracing::Id>) -> Self {
720 Self { inner, id }
721 }
722
723 /// Obtain the inner `ConstraintSystemRef<F>`.
724 pub fn cs(&self) -> ConstraintSystemRef<F> {
725 self.inner.clone()
726 }
727
728 /// Manually leave the namespace.
729 pub fn leave_namespace(self) {
730 drop(self)
731 }
732}
733
734impl<F: Field> Drop for Namespace<F> {
735 fn drop(&mut self) {
736 if let Some(id) = self.id.as_ref() {
737 tracing::dispatcher::get_default(|dispatch| dispatch.exit(id))
738 }
739 let _ = self.inner;
740 }
741}
742
743impl<F: Field> ConstraintSystemRef<F> {
744 /// Returns `self` if `!self.is_none()`, otherwise returns `other`.
745 pub fn or(self, other: Self) -> Self {
746 match self {
747 ConstraintSystemRef::None => other,
748 _ => self,
749 }
750 }
751
752 /// Returns `true` is `self == ConstraintSystemRef::None`.
753 pub fn is_none(&self) -> bool {
754 matches!(self, ConstraintSystemRef::None)
755 }
756
757 /// Construct a `ConstraintSystemRef` from a `ConstraintSystem`.
758 #[inline]
759 pub fn new(inner: ConstraintSystem<F>) -> Self {
760 Self::CS(Rc::new(RefCell::new(inner)))
761 }
762
763 fn inner(&self) -> Option<&Rc<RefCell<ConstraintSystem<F>>>> {
764 match self {
765 Self::CS(a) => Some(a),
766 Self::None => None,
767 }
768 }
769
770 /// Consumes self to return the inner `ConstraintSystem<F>`. Returns
771 /// `None` if `Self::CS` is `None` or if any other references to
772 /// `Self::CS` exist.
773 pub fn into_inner(self) -> Option<ConstraintSystem<F>> {
774 match self {
775 Self::CS(a) => Rc::try_unwrap(a).ok().map(|s| s.into_inner()),
776 Self::None => None,
777 }
778 }
779
780 /// Obtain an immutable reference to the underlying `ConstraintSystem`.
781 ///
782 /// # Panics
783 /// This method panics if `self` is already mutably borrowed.
784 #[inline]
785 pub fn borrow(&self) -> Option<Ref<'_, ConstraintSystem<F>>> {
786 self.inner().map(|cs| cs.borrow())
787 }
788
789 /// Obtain a mutable reference to the underlying `ConstraintSystem`.
790 ///
791 /// # Panics
792 /// This method panics if `self` is already mutably borrowed.
793 #[inline]
794 pub fn borrow_mut(&self) -> Option<RefMut<'_, ConstraintSystem<F>>> {
795 self.inner().map(|cs| cs.borrow_mut())
796 }
797
798 /// Set `self.mode` to `mode`.
799 pub fn set_mode(&self, mode: SynthesisMode) {
800 self.inner().map_or((), |cs| cs.borrow_mut().set_mode(mode))
801 }
802
803 /// Check whether `self.mode == SynthesisMode::Setup`.
804 #[inline]
805 pub fn is_in_setup_mode(&self) -> bool {
806 self.inner()
807 .map_or(false, |cs| cs.borrow().is_in_setup_mode())
808 }
809
810 /// Returns the number of constraints.
811 #[inline]
812 pub fn num_constraints(&self) -> usize {
813 self.inner().map_or(0, |cs| cs.borrow().num_constraints)
814 }
815
816 /// Returns the number of instance variables.
817 #[inline]
818 pub fn num_instance_variables(&self) -> usize {
819 self.inner()
820 .map_or(0, |cs| cs.borrow().num_instance_variables)
821 }
822
823 /// Returns the number of witness variables.
824 #[inline]
825 pub fn num_witness_variables(&self) -> usize {
826 self.inner()
827 .map_or(0, |cs| cs.borrow().num_witness_variables)
828 }
829
830 /// Check whether this constraint system aims to optimize weight,
831 /// number of constraints, or neither.
832 #[inline]
833 pub fn optimization_goal(&self) -> OptimizationGoal {
834 self.inner().map_or(OptimizationGoal::Constraints, |cs| {
835 cs.borrow().optimization_goal()
836 })
837 }
838
839 /// Specify whether this constraint system should aim to optimize weight,
840 /// number of constraints, or neither.
841 #[inline]
842 pub fn set_optimization_goal(&self, goal: OptimizationGoal) {
843 self.inner()
844 .map_or((), |cs| cs.borrow_mut().set_optimization_goal(goal))
845 }
846
847 /// Check whether or not `self` will construct matrices.
848 #[inline]
849 pub fn should_construct_matrices(&self) -> bool {
850 self.inner()
851 .map_or(false, |cs| cs.borrow().should_construct_matrices())
852 }
853
854 /// Obtain a variable representing a new public instance input.
855 #[inline]
856 pub fn new_input_variable<Func>(&self, f: Func) -> crate::r1cs::Result<Variable>
857 where
858 Func: FnOnce() -> crate::r1cs::Result<F>,
859 {
860 self.inner()
861 .ok_or(SynthesisError::MissingCS)
862 .and_then(|cs| {
863 if !self.is_in_setup_mode() {
864 // This is needed to avoid double-borrows, because `f`
865 // might itself mutably borrow `cs` (eg: `f = || g.value()`).
866 let value = f();
867 cs.borrow_mut().new_input_variable(|| value)
868 } else {
869 cs.borrow_mut().new_input_variable(f)
870 }
871 })
872 }
873
874 /// Obtain a variable representing a new private witness input.
875 #[inline]
876 pub fn new_witness_variable<Func>(&self, f: Func) -> crate::r1cs::Result<Variable>
877 where
878 Func: FnOnce() -> crate::r1cs::Result<F>,
879 {
880 self.inner()
881 .ok_or(SynthesisError::MissingCS)
882 .and_then(|cs| {
883 if !self.is_in_setup_mode() {
884 // This is needed to avoid double-borrows, because `f`
885 // might itself mutably borrow `cs` (eg: `f = || g.value()`).
886 let value = f();
887 cs.borrow_mut().new_witness_variable(|| value)
888 } else {
889 cs.borrow_mut().new_witness_variable(f)
890 }
891 })
892 }
893
894 /// Obtain a variable representing a linear combination.
895 #[inline]
896 pub fn new_lc(&self, lc: LinearCombination<F>) -> crate::r1cs::Result<Variable> {
897 self.inner()
898 .ok_or(SynthesisError::MissingCS)
899 .and_then(|cs| cs.borrow_mut().new_lc(lc))
900 }
901
902 /// Enforce a R1CS constraint with the name `name`.
903 #[inline]
904 pub fn enforce_constraint(
905 &self,
906 a: LinearCombination<F>,
907 b: LinearCombination<F>,
908 c: LinearCombination<F>,
909 ) -> crate::r1cs::Result<()> {
910 self.inner()
911 .ok_or(SynthesisError::MissingCS)
912 .and_then(|cs| cs.borrow_mut().enforce_constraint(a, b, c))
913 }
914
915 /// Naively inlines symbolic linear combinations into the linear
916 /// combinations that use them.
917 ///
918 /// Useful for standard pairing-based SNARKs where addition gates are cheap.
919 /// For example, in the SNARKs such as [\[Groth16\]](https://eprint.iacr.org/2016/260) and
920 /// [\[Groth-Maller17\]](https://eprint.iacr.org/2017/540), addition gates
921 /// do not contribute to the size of the multi-scalar multiplication, which
922 /// is the dominating cost.
923 pub fn inline_all_lcs(&self) {
924 if let Some(cs) = self.inner() {
925 cs.borrow_mut().inline_all_lcs()
926 }
927 }
928
929 /// Finalize the constraint system (either by outlining or inlining,
930 /// if an optimization goal is set).
931 pub fn finalize(&self) {
932 if let Some(cs) = self.inner() {
933 cs.borrow_mut().finalize()
934 }
935 }
936
937 /// This step must be called after constraint generation has completed, and
938 /// after all symbolic LCs have been inlined into the places that they
939 /// are used.
940 #[inline]
941 pub fn to_matrices(&self) -> Option<ConstraintMatrices<F>> {
942 self.inner().and_then(|cs| cs.borrow().to_matrices())
943 }
944
945 /// If `self` is satisfied, outputs `Ok(true)`.
946 /// If `self` is unsatisfied, outputs `Ok(false)`.
947 /// If `self.is_in_setup_mode()` or if `self == None`, outputs `Err(())`.
948 pub fn is_satisfied(&self) -> crate::r1cs::Result<bool> {
949 self.inner()
950 .map_or(Err(SynthesisError::AssignmentMissing), |cs| {
951 cs.borrow().is_satisfied()
952 })
953 }
954
955 /// If `self` is satisfied, outputs `Ok(None)`.
956 /// If `self` is unsatisfied, outputs `Some(i)`, where `i` is the index of
957 /// the first unsatisfied constraint.
958 /// If `self.is_in_setup_mode()` or `self == None`, outputs `Err(())`.
959 pub fn which_is_unsatisfied(&self) -> crate::r1cs::Result<Option<String>> {
960 self.inner()
961 .map_or(Err(SynthesisError::AssignmentMissing), |cs| {
962 cs.borrow().which_is_unsatisfied()
963 })
964 }
965
966 /// Obtain the assignment corresponding to the `Variable` `v`.
967 pub fn assigned_value(&self, v: Variable) -> Option<F> {
968 self.inner().and_then(|cs| cs.borrow().assigned_value(v))
969 }
970
971 /// Get trace information about all constraints in the system
972 pub fn constraint_names(&self) -> Option<Vec<String>> {
973 #[cfg(feature = "std")]
974 {
975 self.inner().and_then(|cs| {
976 cs.borrow()
977 .constraint_traces
978 .iter()
979 .map(|trace| {
980 let mut constraint_path = String::new();
981 let mut prev_module_path = "";
982 let mut prefixes = ark_std::collections::BTreeSet::new();
983 for step in trace.as_ref()?.path() {
984 let module_path = if prev_module_path == step.module_path {
985 prefixes.insert(step.module_path.to_string());
986 String::new()
987 } else {
988 let mut parts = step
989 .module_path
990 .split("::")
991 .filter(|&part| part != "r1cs_std" && part != "constraints");
992 let mut path_so_far = String::new();
993 for part in parts.by_ref() {
994 if path_so_far.is_empty() {
995 path_so_far += part;
996 } else {
997 path_so_far += &["::", part].join("");
998 }
999 if prefixes.contains(&path_so_far) {
1000 continue;
1001 } else {
1002 prefixes.insert(path_so_far.clone());
1003 break;
1004 }
1005 }
1006 parts.collect::<Vec<_>>().join("::") + "::"
1007 };
1008 prev_module_path = step.module_path;
1009 constraint_path += &["/", &module_path, step.name].join("");
1010 }
1011 Some(constraint_path)
1012 })
1013 .collect::<Option<Vec<_>>>()
1014 })
1015 }
1016 #[cfg(not(feature = "std"))]
1017 {
1018 None
1019 }
1020 }
1021}
1022
1023#[cfg(test)]
1024mod tests {
1025 use crate::r1cs::*;
1026 use ark_ff::One;
1027 use ark_test_curves::bls12_381::Fr;
1028
1029 #[test]
1030 fn matrix_generation() -> crate::r1cs::Result<()> {
1031 let cs = ConstraintSystem::<Fr>::new_ref();
1032 let two = Fr::one() + Fr::one();
1033 let a = cs.new_input_variable(|| Ok(Fr::one()))?;
1034 let b = cs.new_witness_variable(|| Ok(Fr::one()))?;
1035 let c = cs.new_witness_variable(|| Ok(two))?;
1036 cs.enforce_constraint(lc!() + a, lc!() + (two, b), lc!() + c)?;
1037 let d = cs.new_lc(lc!() + a + b)?;
1038 cs.enforce_constraint(lc!() + a, lc!() + d, lc!() + d)?;
1039 let e = cs.new_lc(lc!() + d + d)?;
1040 cs.enforce_constraint(lc!() + Variable::One, lc!() + e, lc!() + e)?;
1041 cs.inline_all_lcs();
1042 let matrices = cs.to_matrices().unwrap();
1043 assert_eq!(matrices.a[0], vec![(Fr::one(), 1)]);
1044 assert_eq!(matrices.b[0], vec![(two, 2)]);
1045 assert_eq!(matrices.c[0], vec![(Fr::one(), 3)]);
1046
1047 assert_eq!(matrices.a[1], vec![(Fr::one(), 1)]);
1048 assert_eq!(matrices.b[1], vec![(Fr::one(), 1), (Fr::one(), 2)]);
1049 assert_eq!(matrices.c[1], vec![(Fr::one(), 1), (Fr::one(), 2)]);
1050
1051 assert_eq!(matrices.a[2], vec![(Fr::one(), 0)]);
1052 assert_eq!(matrices.b[2], vec![(two, 1), (two, 2)]);
1053 assert_eq!(matrices.c[2], vec![(two, 1), (two, 2)]);
1054 Ok(())
1055 }
1056
1057 #[test]
1058 fn matrix_generation_outlined() -> crate::r1cs::Result<()> {
1059 let cs = ConstraintSystem::<Fr>::new_ref();
1060 cs.set_optimization_goal(OptimizationGoal::Weight);
1061 let two = Fr::one() + Fr::one();
1062 let a = cs.new_input_variable(|| Ok(Fr::one()))?;
1063 let b = cs.new_witness_variable(|| Ok(Fr::one()))?;
1064 let c = cs.new_witness_variable(|| Ok(two))?;
1065 cs.enforce_constraint(lc!() + a, lc!() + (two, b), lc!() + c)?;
1066
1067 let d = cs.new_lc(lc!() + a + b)?;
1068 cs.enforce_constraint(lc!() + a, lc!() + d, lc!() + d)?;
1069
1070 let e = cs.new_lc(lc!() + d + d)?;
1071 cs.enforce_constraint(lc!() + Variable::One, lc!() + e, lc!() + e)?;
1072
1073 cs.finalize();
1074 assert!(cs.is_satisfied().unwrap());
1075 let matrices = cs.to_matrices().unwrap();
1076 assert_eq!(matrices.a[0], vec![(Fr::one(), 1)]);
1077 assert_eq!(matrices.b[0], vec![(two, 2)]);
1078 assert_eq!(matrices.c[0], vec![(Fr::one(), 3)]);
1079
1080 assert_eq!(matrices.a[1], vec![(Fr::one(), 1)]);
1081 // Notice here how the variable allocated for d is outlined
1082 // compared to the example in previous test case.
1083 // We are optimising for weight: there are less non-zero elements.
1084 assert_eq!(matrices.b[1], vec![(Fr::one(), 4)]);
1085 assert_eq!(matrices.c[1], vec![(Fr::one(), 4)]);
1086
1087 assert_eq!(matrices.a[2], vec![(Fr::one(), 0)]);
1088 assert_eq!(matrices.b[2], vec![(two, 4)]);
1089 assert_eq!(matrices.c[2], vec![(two, 4)]);
1090 Ok(())
1091 }
1092
1093 /// Example meant to follow as closely as possible the excellent R1CS
1094 /// write-up by [Vitalik Buterin](https://vitalik.eth.limo/general/2016/12/10/qap.html)
1095 /// and demonstrate how to construct such matrices in arkworks.
1096 #[test]
1097 fn matrix_generation_example() -> crate::r1cs::Result<()> {
1098 let cs = ConstraintSystem::<Fr>::new_ref();
1099 // helper definitions
1100 let three = Fr::from(3u8);
1101 let five = Fr::from(5u8);
1102 let nine = Fr::from(9u8);
1103 // There will be six variables in the system, in the order governed by adding
1104 // them to the constraint system (Note that the CS is initialised with
1105 // `Variable::One` in the first position implicitly).
1106 // Note also that the all public variables will always be placed before all witnesses
1107 //
1108 // Variable::One
1109 // Variable::Instance(35)
1110 // Variable::Witness(3) ( == x )
1111 // Variable::Witness(9) ( == sym_1 )
1112 // Variable::Witness(27) ( == y )
1113 // Variable::Witness(30) ( == sym_2 )
1114
1115 // let one = Variable::One; // public input, implicitly defined
1116 let out = cs.new_input_variable(|| Ok(nine * three + three + five))?; // public input
1117 let x = cs.new_witness_variable(|| Ok(three))?; // explicit witness
1118 let sym_1 = cs.new_witness_variable(|| Ok(nine))?; // intermediate witness variable
1119 let y = cs.new_witness_variable(|| Ok(nine * three))?; // intermediate witness variable
1120 let sym_2 = cs.new_witness_variable(|| Ok(nine * three + three))?; // intermediate witness variable
1121
1122 cs.enforce_constraint(lc!() + x, lc!() + x, lc!() + sym_1)?;
1123 cs.enforce_constraint(lc!() + sym_1, lc!() + x, lc!() + y)?;
1124 cs.enforce_constraint(lc!() + y + x, lc!() + Variable::One, lc!() + sym_2)?;
1125 cs.enforce_constraint(
1126 lc!() + sym_2 + (five, Variable::One),
1127 lc!() + Variable::One,
1128 lc!() + out,
1129 )?;
1130
1131 cs.finalize();
1132 assert!(cs.is_satisfied().unwrap());
1133 let matrices = cs.to_matrices().unwrap();
1134 // There are four gates(constraints), each generating a row.
1135 // Resulting matrices:
1136 // (Note how 2nd & 3rd columns are swapped compared to the online example.
1137 // This results from an implementation detail of placing all Variable::Instances(_) first.
1138 //
1139 // A
1140 // [0, 0, 1, 0, 0, 0]
1141 // [0, 0, 0, 1, 0, 0]
1142 // [0, 0, 1, 0, 1, 0]
1143 // [5, 0, 0, 0, 0, 1]
1144 // B
1145 // [0, 0, 1, 0, 0, 0]
1146 // [0, 0, 1, 0, 0, 0]
1147 // [1, 0, 0, 0, 0, 0]
1148 // [1, 0, 0, 0, 0, 0]
1149 // C
1150 // [0, 0, 0, 1, 0, 0]
1151 // [0, 0, 0, 0, 1, 0]
1152 // [0, 0, 0, 0, 0, 1]
1153 // [0, 1, 0, 0, 0, 0]
1154 assert_eq!(matrices.a[0], vec![(Fr::one(), 2)]);
1155 assert_eq!(matrices.b[0], vec![(Fr::one(), 2)]);
1156 assert_eq!(matrices.c[0], vec![(Fr::one(), 3)]);
1157
1158 assert_eq!(matrices.a[1], vec![(Fr::one(), 3)]);
1159 assert_eq!(matrices.b[1], vec![(Fr::one(), 2)]);
1160 assert_eq!(matrices.c[1], vec![(Fr::one(), 4)]);
1161
1162 assert_eq!(matrices.a[2], vec![(Fr::one(), 2), (Fr::one(), 4)]);
1163 assert_eq!(matrices.b[2], vec![(Fr::one(), 0)]);
1164 assert_eq!(matrices.c[2], vec![(Fr::one(), 5)]);
1165
1166 assert_eq!(matrices.a[3], vec![(five, 0), (Fr::one(), 5)]);
1167 assert_eq!(matrices.b[3], vec![(Fr::one(), 0)]);
1168 assert_eq!(matrices.c[3], vec![(Fr::one(), 1)]);
1169 Ok(())
1170 }
1171}