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}