Skip to main content

ark_relations/gr1cs/
constraint_system.rs

1//! This module contains the implementation of the `ConstraintSystem` struct.
2//! a constraint system contains multiple predicate constraint systems,
3//! each of which enforce have separate predicates and constraints. For more information about the terminology and the structure of the constraint system, refer to section 3.3 of <https://eprint.iacr.org/2024/1245>
4
5use super::{
6    instance_outliner::InstanceOutliner,
7    predicate::{
8        polynomial_constraint::{R1CS_PREDICATE_LABEL, SR1CS_PREDICATE_LABEL},
9        Predicate, PredicateConstraintSystem,
10    },
11    ConstraintSystemRef, Label, OptimizationGoal, SynthesisMode,
12};
13#[cfg(feature = "std")]
14use crate::gr1cs::ConstraintTrace;
15use crate::gr1cs::{
16    assignment::Assignments,
17    field_interner::FieldInterner,
18    lc_map::{to_non_interned_lc, LcMap},
19    LinearCombination, Matrix, SynthesisError, Variable,
20};
21use ark_ff::Field;
22use ark_std::{
23    any::{Any, TypeId},
24    boxed::Box,
25    cell::RefCell,
26    collections::BTreeMap,
27    format,
28    rc::Rc,
29    string::{String, ToString},
30    vec::Vec,
31};
32
33use crate::utils::IndexMap;
34#[cfg(feature = "parallel")]
35use rayon::prelude::*;
36///////////////////////////////////////////////////////////////////////////////////////
37
38/// A GR1CS `ConstraintSystem`. Enforces constraints of the form
39///
40/// `L_i(⟨M_{i,1}, z⟩ , ⟨M_{i,2}, z⟩ , ..., ⟨M_{i,t_i}, z⟩) = 0`
41///
42/// More Information: <https://eprint.iacr.org/2024/1245>
43#[derive(Debug, Clone)]
44pub struct ConstraintSystem<F: Field> {
45    /// The mode in which the constraint system is operating. `self` can either
46    /// be in setup mode (i.e., `self.mode == SynthesisMode::Setup`) or in
47    /// proving mode (i.e., `self.mode == SynthesisMode::Prove`). If we are
48    /// in proving mode, then we have the additional option of whether or
49    /// not to construct the A, B, and C matrices of the constraint system
50    mode: SynthesisMode,
51
52    /// The number of variables that are "public inputs" to the constraint
53    /// system.
54    #[doc(hidden)]
55    pub num_instance_variables: usize,
56
57    /// The number of variables that are "private inputs" to the constraint
58    /// system.
59    #[doc(hidden)]
60    pub num_witness_variables: usize,
61
62    /// The number of linear combinations
63    #[doc(hidden)]
64    pub num_linear_combinations: usize,
65
66    /// The parameter we aim to minimize in this constraint system (either the
67    /// number of constraints or their total weight).
68    optimization_goal: OptimizationGoal,
69
70    /// If true, the constraint system will outline the instances. Outlining the instances is a technique used for verification succinctness in some SNARKs like Polymath, Garuda, and Pari. For more information, refer to <https://eprint.iacr.org/2024/1245>.
71    /// It assigns a witness variable to each instance variable and enforces the
72    /// equality of the instance and witness variables. Then only uses the
73    /// witness variables in the constraints.
74    instance_outliner: Option<InstanceOutliner<F>>,
75
76    /// Assignments to the input, witness, and lc variables. This is empty if `self.mode
77    /// == SynthesisMode::Setup`.
78    pub assignments: Assignments<F>,
79
80    /// Map for gadgets to cache computation results.
81    pub cache_map: Rc<RefCell<BTreeMap<TypeId, Box<dyn Any>>>>,
82
83    /// A data structure to store the linear combinations. We use map because
84    /// it's easier to inline and outline the linear combinations.
85    #[doc(hidden)]
86    pub lc_map: LcMap<F>,
87
88    field_interner: FieldInterner<F>,
89
90    /// A map from the the predicate labels to the predicates
91    #[doc(hidden)]
92    pub predicate_constraint_systems: BTreeMap<Label, PredicateConstraintSystem<F>>,
93
94    /// data structure to store the traces for each predicate
95    #[cfg(feature = "std")]
96    pub predicate_traces: BTreeMap<Label, Vec<Option<ConstraintTrace>>>,
97}
98
99impl<F: Field> Default for ConstraintSystem<F> {
100    fn default() -> Self {
101        Self::new()
102    }
103}
104
105impl<F: Field> ConstraintSystem<F> {
106    /// Create a new and empty `ConstraintSystem<F>`.
107    /// Note that by default, the constraint system is
108    /// registered with the R1CS predicate.
109    pub fn new() -> Self {
110        let mut field_interner = FieldInterner::new();
111        let mut lc_map = LcMap::new();
112        lc_map.push(LinearCombination::zero(), &mut field_interner);
113
114        let mut cs = Self {
115            num_instance_variables: 1,
116            num_witness_variables: 0,
117            num_linear_combinations: 1,
118            instance_outliner: None,
119            predicate_constraint_systems: BTreeMap::new(),
120            assignments: Assignments {
121                instance_assignment: vec![F::one()],
122                witness_assignment: Vec::new(),
123                lc_assignment: vec![F::zero()],
124            },
125            cache_map: Rc::new(RefCell::new(BTreeMap::new())),
126            lc_map,
127            field_interner,
128            mode: SynthesisMode::Prove {
129                construct_matrices: true,
130                generate_lc_assignments: true,
131            },
132            optimization_goal: OptimizationGoal::None,
133            #[cfg(feature = "std")]
134            predicate_traces: BTreeMap::new(),
135        };
136        let r1cs_constraint_system = PredicateConstraintSystem::new_r1cs().unwrap();
137        let _ = cs.register_predicate(R1CS_PREDICATE_LABEL, r1cs_constraint_system);
138        cs
139    }
140
141    /// Create a new `ConstraintSystemRef<F>`.
142    pub fn new_ref() -> ConstraintSystemRef<F> {
143        ConstraintSystemRef::new(Self::new())
144    }
145
146    /// Returns a mapping from predicate label to number of constraints for that
147    /// predicate
148    pub fn get_all_predicates_num_constraints(&self) -> IndexMap<Label, usize> {
149        self.predicate_constraint_systems
150            .iter()
151            .map(|(label, predicate)| (label.clone(), predicate.num_constraints()))
152            .collect()
153    }
154
155    /// Returns the number of constraints for a given predicate
156    pub fn get_predicate_num_constraints(&self, predicate_label: &str) -> Option<usize> {
157        self.predicate_constraint_systems
158            .get(predicate_label)
159            .map(|predicate| predicate.num_constraints())
160    }
161
162    /// Returns a mapping from predicate label to arity for that predicate
163    pub fn get_all_predicate_arities(&self) -> IndexMap<Label, usize> {
164        self.predicate_constraint_systems
165            .iter()
166            .map(|(label, predicate)| (label.clone(), predicate.get_arity()))
167            .collect()
168    }
169
170    /// Returns the type of the predicate with the given label
171    pub fn get_predicate_arity(&self, predicate_label: &str) -> Option<usize> {
172        self.predicate_constraint_systems
173            .get(predicate_label)
174            .map(|predicate| predicate.get_arity())
175    }
176
177    /// Returns a mapping from predicate labels to their types
178    pub fn get_all_predicate_types(&self) -> BTreeMap<Label, Predicate<F>> {
179        self.predicate_constraint_systems
180            .iter()
181            .map(|(label, predicate)| (label.clone(), predicate.get_predicate().clone()))
182            .collect()
183    }
184
185    /// Returns the type of the predicate with the given label
186    pub fn get_predicate_type(&self, predicate_label: &str) -> Option<Predicate<F>> {
187        self.predicate_constraint_systems
188            .get(predicate_label)
189            .map(|predicate| predicate.get_predicate().clone())
190    }
191
192    /// Returns the assignment to the public input variables of the constraint
193    pub fn instance_assignment(&self) -> crate::gr1cs::Result<&[F]> {
194        if self.is_in_setup_mode() {
195            return Err(SynthesisError::AssignmentMissing);
196        }
197        Ok(&self.assignments.instance_assignment)
198    }
199
200    /// Returns the assignment to the private input variables of the constraint
201    pub fn witness_assignment(&self) -> crate::gr1cs::Result<&[F]> {
202        if self.is_in_setup_mode() {
203            return Err(SynthesisError::AssignmentMissing);
204        }
205        Ok(&self.assignments.witness_assignment)
206    }
207
208    /// Returns the number of constraints which is the sum of the number of
209    /// constraints in each predicate.
210    pub fn num_constraints(&self) -> usize {
211        self.predicate_constraint_systems
212            .values()
213            .map(|p| p.num_constraints())
214            .sum()
215    }
216
217    /// Returns the number of instance variables.
218    pub fn num_instance_variables(&self) -> usize {
219        self.num_instance_variables
220    }
221
222    /// Returns the number of witness variables.
223    pub fn num_witness_variables(&self) -> usize {
224        self.num_witness_variables
225    }
226
227    /// Returns the number of witness variables.
228    pub fn num_variables(&self) -> usize {
229        self.num_witness_variables() + self.num_instance_variables()
230    }
231
232    /// Returns the number of predicates in the constraint system
233    pub fn num_predicates(&self) -> usize {
234        self.predicate_constraint_systems.len()
235    }
236
237    /// Enforce a constraint in the constraint system. It takes a
238    /// predicate name and enforces a vector of linear combinations of the
239    /// length of the arity of the predicate enforces the constraint.
240    #[inline]
241    pub fn enforce_constraint(
242        &mut self,
243        predicate_label: &str,
244        lcs: impl IntoIterator<
245            Item = Box<dyn FnOnce() -> LinearCombination<F>>,
246            IntoIter: ExactSizeIterator,
247        >,
248    ) -> crate::gr1cs::Result<()> {
249        if !self.has_predicate(predicate_label) {
250            return Err(SynthesisError::PredicateNotFound);
251        }
252
253        if self.should_construct_matrices() {
254            let should_generate_lc_assignments = self.should_generate_lc_assignments();
255            let lc_map = &mut self.lc_map;
256            let num_lcs = &mut self.num_linear_combinations;
257            let assignments = &mut self.assignments;
258            let field_interner = &mut self.field_interner;
259
260            let lc_indices = lcs.into_iter().map(|lc| {
261                let lc = lc();
262                Self::new_lc_add_helper(
263                    lc,
264                    num_lcs,
265                    lc_map,
266                    should_generate_lc_assignments,
267                    assignments,
268                    field_interner,
269                )
270            });
271
272            let predicate = self
273                .predicate_constraint_systems
274                .get_mut(predicate_label)
275                .unwrap();
276
277            predicate.enforce_constraint(lc_indices)?;
278        }
279
280        #[cfg(feature = "std")]
281        match self.predicate_traces.get_mut(predicate_label) {
282            Some(traces) => traces.push(ConstraintTrace::capture()),
283            None => {
284                eprintln!("Constraint trace requires adding the predicate constraint trace")
285            },
286        }
287
288        Ok(())
289    }
290
291    /// Enforce a constraint for a predicate with arity 2.
292    pub fn enforce_constraint_arity_2(
293        &mut self,
294        predicate_label: &str,
295        a: impl FnOnce() -> LinearCombination<F>,
296        b: impl FnOnce() -> LinearCombination<F>,
297    ) -> crate::gr1cs::Result<()> {
298        if !self.has_predicate(predicate_label) {
299            return Err(SynthesisError::PredicateNotFound);
300        }
301
302        if self.should_construct_matrices() {
303            let a = self.new_constraint_lc(a);
304            let b = self.new_constraint_lc(b);
305
306            let predicate = self
307                .predicate_constraint_systems
308                .get_mut(predicate_label)
309                .unwrap();
310
311            predicate.enforce_constraint([a, b])?;
312        }
313
314        #[cfg(feature = "std")]
315        if let Some(traces) = self.predicate_traces.get_mut(predicate_label) {
316            traces.push(ConstraintTrace::capture())
317        }
318
319        Ok(())
320    }
321
322    /// Enforce a constraint for a predicate with arity 3.
323    pub fn enforce_constraint_arity_3(
324        &mut self,
325        predicate_label: &str,
326        a: impl FnOnce() -> LinearCombination<F>,
327        b: impl FnOnce() -> LinearCombination<F>,
328        c: impl FnOnce() -> LinearCombination<F>,
329    ) -> crate::gr1cs::Result<()> {
330        if !self.has_predicate(predicate_label) {
331            return Err(SynthesisError::PredicateNotFound);
332        }
333
334        if self.should_construct_matrices() {
335            let a = self.new_constraint_lc(a);
336            let b = self.new_constraint_lc(b);
337            let c = self.new_constraint_lc(c);
338
339            let predicate = self
340                .predicate_constraint_systems
341                .get_mut(predicate_label)
342                .unwrap();
343
344            predicate.enforce_constraint([a, b, c])?;
345        }
346
347        #[cfg(feature = "std")]
348        if let Some(traces) = self.predicate_traces.get_mut(predicate_label) {
349            traces.push(ConstraintTrace::capture())
350        }
351
352        Ok(())
353    }
354
355    /// Enforce a constraint for a predicate with arity 4.
356    pub fn enforce_constraint_arity_4(
357        &mut self,
358        predicate_label: &str,
359        a: impl FnOnce() -> LinearCombination<F>,
360        b: impl FnOnce() -> LinearCombination<F>,
361        c: impl FnOnce() -> LinearCombination<F>,
362        d: impl FnOnce() -> LinearCombination<F>,
363    ) -> crate::gr1cs::Result<()> {
364        if !self.has_predicate(predicate_label) {
365            return Err(SynthesisError::PredicateNotFound);
366        }
367
368        if self.should_construct_matrices() {
369            let a = self.new_constraint_lc(a);
370            let b = self.new_constraint_lc(b);
371            let c = self.new_constraint_lc(c);
372            let d = self.new_constraint_lc(d);
373
374            let predicate = self
375                .predicate_constraint_systems
376                .get_mut(predicate_label)
377                .unwrap();
378
379            predicate.enforce_constraint([a, b, c, d])?;
380        }
381
382        #[cfg(feature = "std")]
383        if let Some(traces) = self.predicate_traces.get_mut(predicate_label) {
384            traces.push(ConstraintTrace::capture())
385        }
386
387        Ok(())
388    }
389
390    /// Enforce a constraint for a predicate with arity 5.
391    pub fn enforce_constraint_arity_5(
392        &mut self,
393        predicate_label: &str,
394        a: impl FnOnce() -> LinearCombination<F>,
395        b: impl FnOnce() -> LinearCombination<F>,
396        c: impl FnOnce() -> LinearCombination<F>,
397        d: impl FnOnce() -> LinearCombination<F>,
398        e: impl FnOnce() -> LinearCombination<F>,
399    ) -> crate::gr1cs::Result<()> {
400        if !self.has_predicate(predicate_label) {
401            return Err(SynthesisError::PredicateNotFound);
402        }
403
404        if self.should_construct_matrices() {
405            let a = self.new_constraint_lc(a);
406            let b = self.new_constraint_lc(b);
407            let c = self.new_constraint_lc(c);
408            let d = self.new_constraint_lc(d);
409            let e = self.new_constraint_lc(e);
410
411            let predicate = self
412                .predicate_constraint_systems
413                .get_mut(predicate_label)
414                .unwrap();
415
416            predicate.enforce_constraint([a, b, c, d, e])?;
417        }
418
419        #[cfg(feature = "std")]
420        if let Some(traces) = self.predicate_traces.get_mut(predicate_label) {
421            traces.push(ConstraintTrace::capture())
422        }
423
424        Ok(())
425    }
426
427    /// Enforce a constraint in the constraint system. It takes a
428    /// predicate name and enforces a vector of linear combinations of the
429    /// length of the arity of the predicate enforces the constraint.
430    #[inline]
431    pub fn enforce_r1cs_constraint(
432        &mut self,
433        a: impl FnOnce() -> LinearCombination<F>,
434        b: impl FnOnce() -> LinearCombination<F>,
435        c: impl FnOnce() -> LinearCombination<F>,
436    ) -> crate::gr1cs::Result<()> {
437        self.enforce_constraint_arity_3(R1CS_PREDICATE_LABEL, a, b, c)
438    }
439
440    /// Enforce a constraint in the constraint system. It takes a
441    /// predicate name and enforces a vector of linear combinations of the
442    /// length of the arity of the predicate enforces the constraint.
443    #[inline]
444    pub fn enforce_sr1cs_constraint(
445        &mut self,
446        a: impl FnOnce() -> LinearCombination<F>,
447        b: impl FnOnce() -> LinearCombination<F>,
448    ) -> crate::gr1cs::Result<()> {
449        self.enforce_constraint_arity_2(SR1CS_PREDICATE_LABEL, a, b)
450    }
451
452    /// Add a new linear combination to the constraint system.
453    /// This linear combination is to be used only for constraints, not for variables.
454    #[inline]
455    fn new_constraint_lc(&mut self, lc: impl FnOnce() -> LinearCombination<F>) -> Variable {
456        if self.should_construct_matrices() {
457            self.new_lc_helper(lc)
458        } else {
459            self.new_lc_without_adding()
460        }
461    }
462
463    /// Creates a new index for the linear combination without adding the concrete LC expression
464    /// to the map.
465    #[inline]
466    fn new_lc_without_adding(&mut self) -> Variable {
467        let index = self.num_linear_combinations;
468        self.num_linear_combinations += 1;
469        Variable::symbolic_lc(index)
470    }
471
472    fn new_lc_add_helper(
473        lc: LinearCombination<F>,
474        cur_num_lcs: &mut usize,
475        lc_map: &mut LcMap<F>,
476        should_generate_lc_assignments: bool,
477        assignments: &mut Assignments<F>,
478        field_interner: &mut FieldInterner<F>,
479    ) -> Variable {
480        match lc.0.as_slice() {
481            // If the linear combination is empty, we return a symbolic LC with index 0.
482            [] | [(_, Variable::Zero)] => Variable::symbolic_lc(0),
483            // If the linear combination is just another variable
484            // with a coefficient of 1, we return the variable directly.
485            [(c, var)] if c.is_one() => *var,
486            // In all other cases, we create a new linear combination
487            _ => {
488                let index = *cur_num_lcs;
489                debug_assert_eq!(*cur_num_lcs, lc_map.num_lcs());
490                lc_map.push(lc, field_interner);
491                *cur_num_lcs += 1;
492                if should_generate_lc_assignments {
493                    let value = assignments.eval_lc(index, lc_map, field_interner).unwrap();
494                    assignments.lc_assignment.push(value);
495                }
496                Variable::symbolic_lc(index)
497            },
498        }
499    }
500
501    /// Helper function to add a new linear combination to the constraint system.
502    #[inline]
503    fn new_lc_helper(&mut self, lc: impl FnOnce() -> LinearCombination<F>) -> Variable {
504        let should_push = self.should_construct_matrices() || self.should_generate_lc_assignments();
505        let should_generate_lc_assignments = self.should_generate_lc_assignments();
506        if should_push {
507            let lc = lc();
508            Self::new_lc_add_helper(
509                lc,
510                &mut self.num_linear_combinations,
511                &mut self.lc_map,
512                should_generate_lc_assignments,
513                &mut self.assignments,
514                &mut self.field_interner,
515            )
516        } else {
517            self.new_lc_without_adding()
518        }
519    }
520
521    /// Adds a new linear combination to the constraint system.
522    #[inline]
523    pub fn new_lc(
524        &mut self,
525        lc: impl FnOnce() -> LinearCombination<F>,
526    ) -> crate::gr1cs::Result<Variable> {
527        // Because this LC might be used to construct constraints,
528        // we need to ensure that it is added to the lc_map whenever
529        // `self.should_construct_matrices()` is true.
530        // `self.new_lc_helper` will handle this.
531        Ok(self.new_lc_helper(lc))
532    }
533
534    /// Set `self.mode` to `mode`.
535    pub fn set_mode(&mut self, mode: SynthesisMode) {
536        self.mode = mode;
537    }
538
539    /// Check whether `self.mode == SynthesisMode::Setup`.
540    /// Returns true if
541    /// 1. There is an underlying `ConstraintSystem`, and
542    /// 2. It is in setup mode.
543    pub fn is_in_setup_mode(&self) -> bool {
544        self.mode == SynthesisMode::Setup
545    }
546
547    /// Check whether this constraint system aims to optimize weight,
548    /// number of constraints, or neither.
549    pub fn optimization_goal(&self) -> OptimizationGoal {
550        self.optimization_goal
551    }
552
553    /// Check whether this constraint system is new, i.e., it is just created
554    fn is_new(&self) -> bool {
555        self.num_instance_variables == 1
556            && self.num_witness_variables == 0
557            && self.num_constraints() == 0
558            && self.num_linear_combinations == 1
559    }
560
561    /// Specify whether this constraint system should aim to optimize weight,
562    /// number of constraints, or neither.
563    pub fn set_optimization_goal(&mut self, goal: OptimizationGoal) {
564        assert!(self.is_new());
565        self.optimization_goal = goal;
566    }
567
568    /// Check whether or not `self` will construct matrices.
569    pub fn should_construct_matrices(&self) -> bool {
570        match self.mode {
571            SynthesisMode::Setup => true,
572            SynthesisMode::Prove {
573                construct_matrices, ..
574            } => construct_matrices,
575        }
576    }
577
578    /// Check whether or not `self` will construct matrices.
579    pub fn should_generate_lc_assignments(&self) -> bool {
580        match self.mode {
581            SynthesisMode::Setup => false,
582            SynthesisMode::Prove {
583                generate_lc_assignments,
584                ..
585            } => generate_lc_assignments,
586        }
587    }
588
589    /// Obtain a variable representing a new public instance input.
590    #[inline]
591    pub fn new_input_variable<Func>(&mut self, f: Func) -> crate::utils::Result<Variable>
592    where
593        Func: FnOnce() -> crate::utils::Result<F>,
594    {
595        let index = self.num_instance_variables;
596        self.num_instance_variables += 1;
597
598        if !self.is_in_setup_mode() {
599            self.assignments.instance_assignment.push(f()?);
600        }
601        Ok(Variable::instance(index))
602    }
603
604    /// Obtain a variable representing a new private witness input.
605    #[inline]
606    pub fn new_witness_variable<Func>(&mut self, f: Func) -> crate::utils::Result<Variable>
607    where
608        Func: FnOnce() -> crate::utils::Result<F>,
609    {
610        let index = self.num_witness_variables;
611        self.num_witness_variables += 1;
612
613        if !self.is_in_setup_mode() {
614            self.assignments.witness_assignment.push(f()?);
615        }
616        Ok(Variable::witness(index))
617    }
618
619    /// Register a predicate in the constraint system with a given label.
620    pub fn register_predicate(
621        &mut self,
622        predicate_label: &str,
623        predicate: PredicateConstraintSystem<F>,
624    ) -> crate::utils::Result<()> {
625        self.predicate_constraint_systems
626            .insert(predicate_label.to_string(), predicate);
627        #[cfg(feature = "std")]
628        self.predicate_traces
629            .insert(predicate_label.to_string(), Vec::new());
630        Ok(())
631    }
632
633    /// Remove the predicate with the given label from the constraint system.
634    pub fn remove_predicate(&mut self, predicate_label: &str) {
635        self.predicate_constraint_systems.remove(predicate_label);
636    }
637
638    /// check if there is a predicate with the given label
639    pub fn has_predicate(&self, predicate_label: &str) -> bool {
640        self.predicate_constraint_systems
641            .contains_key(predicate_label)
642    }
643
644    /// Obtain the assignment corresponding to the `Variable` `v`.
645    pub fn assigned_value(&self, v: Variable) -> Option<F> {
646        self.assignments.assigned_value(v)
647    }
648
649    /// If `self` is satisfied, outputs `Ok(true)`.
650    /// If `self` is unsatisfied, outputs `Ok(false)`.
651    /// If `self.is_in_setup_mode()` or if `self == None`, outputs `Err(())`.
652    pub fn is_satisfied(&self) -> crate::utils::Result<bool> {
653        self.which_is_unsatisfied().map(|s| s.is_none())
654    }
655
656    /// If `self` is satisfied, outputs `Ok(None)`.
657    /// If `self` is unsatisfied, outputs `Some(s,i)`, where `s` is the label of
658    /// the unsatisfied prediacate and  `i` is the index of
659    /// the first unsatisfied constraint in that predicate.
660    /// If `self.is_in_setup_mode()` or `self == None`, outputs `Err(())`.
661    pub fn which_is_unsatisfied(&self) -> crate::utils::Result<Option<String>> {
662        if self.is_in_setup_mode() {
663            Err(SynthesisError::AssignmentMissing)
664        } else {
665            for (label, predicate) in &self.predicate_constraint_systems {
666                if let Some(unsatisfied_constraint) =
667                    predicate.which_constraint_is_unsatisfied(self)
668                {
669                    #[cfg(feature = "std")]
670                    let trace = self.predicate_traces[label][unsatisfied_constraint]
671                        .as_ref()
672                        .map_or_else(
673                            || {
674                                eprintln!("Constraint trace requires `ConstraintLayer`");
675                                format!("{label} - {unsatisfied_constraint}")
676                            },
677                            |t| format!("{t}"),
678                        )
679                        .to_string();
680                    #[cfg(not(feature = "std"))]
681                    let trace = format!("{label} - {unsatisfied_constraint}");
682                    return Ok(Some(trace));
683                }
684            }
685            Ok(None)
686        }
687    }
688
689    /// Finalize the constraint system (either by outlining or inlining,
690    /// if an optimization goal is set).
691    pub fn finalize(&mut self) {
692        let timer_finalize = start_timer!(|| "Finalize GR1CS");
693        let timer_inline_ouline_lcs = start_timer!(|| "Inline/Outline LCs");
694        self.inline_all_lcs();
695        end_timer!(timer_inline_ouline_lcs);
696        // check if should outline instance or not
697        let timer_instance_outlining = start_timer!(|| "Instance Outlining");
698        if let Some(instance_outliner) = self.instance_outliner.take() {
699            // Check if the predicate to be outlined is in the constraint system
700            if self.has_predicate(&instance_outliner.pred_label) {
701                // Outline the instances
702                let _ = self.perform_instance_outlining(instance_outliner);
703            }
704        }
705        end_timer!(timer_instance_outlining);
706        end_timer!(timer_finalize);
707    }
708
709    /// Naively inlines symbolic linear combinations into the linear
710    /// combinations that use them.
711    ///
712    /// Useful for standard pairing-based SNARKs where addition gates are
713    /// cheap. For example, in the SNARKs such as [\[Groth16\]](https://eprint.iacr.org/2016/260) and
714    /// [\[Groth-Maller17\]](https://eprint.iacr.org/2017/540), addition gates
715    /// do not contribute to the size of the multi-scalar multiplication,
716    /// which is the dominating cost.
717    pub fn inline_all_lcs(&mut self) {
718        if !self.should_construct_matrices() {
719            return;
720        }
721
722        let any_used = self.any_lcs_used();
723        if !any_used {
724            return;
725        }
726        let old_lc_map = core::mem::take(&mut self.lc_map);
727        let mut inlined_lcs =
728            LcMap::<F>::with_capacity(old_lc_map.num_lcs(), old_lc_map.total_lc_size());
729
730        let mut out = LinearCombination(Vec::with_capacity(10));
731        for lc in old_lc_map.iter() {
732            let lc = to_non_interned_lc(lc, &self.field_interner);
733            for (coeff, var) in lc {
734                if let Some(lc_index) = var.get_lc_index() {
735                    // Must already be transformed — guaranteed by ordering.
736                    let inlined = inlined_lcs.get(lc_index).unwrap();
737                    let inlined = to_non_interned_lc(inlined, &self.field_interner);
738
739                    if coeff.is_one() {
740                        out.extend(inlined);
741                    } else {
742                        out.extend(
743                            inlined
744                                .filter(|(c, v)| !v.is_zero() && !c.is_zero())
745                                .map(|(c, v)| (coeff * c, v)),
746                        );
747                    }
748                } else {
749                    out.push((coeff, var));
750                }
751            }
752            out.compactify();
753
754            inlined_lcs.push_by_ref(out.iter(), &mut self.field_interner);
755            out.clear();
756        }
757        self.lc_map = inlined_lcs;
758    }
759
760    /// Returns whether any linear combinations are used
761    /// by other LCs.
762    fn any_lcs_used(&self) -> bool {
763        cfg_iter!(self.lc_map).any(|mut lc| lc.any(|(_, v)| v.is_lc()))
764    }
765
766    /// Get the matrices corresponding to the predicates.and the
767    /// corresponding set of matrices
768    pub fn to_matrices(&self) -> crate::gr1cs::Result<BTreeMap<Label, Vec<Matrix<F>>>> {
769        let mut matrices = BTreeMap::new();
770        for (label, predicate) in &self.predicate_constraint_systems {
771            matrices.insert(label.clone(), predicate.to_matrices(self));
772        }
773        Ok(matrices)
774    }
775
776    /// Get the linear combination corresponding to the given `lc_index`.
777    pub fn get_lc(&self, var: Variable) -> LinearCombination<F> {
778        if var.is_zero() {
779            LinearCombination::zero()
780        } else if var.is_lc() {
781            let idx = var.index().unwrap();
782            LinearCombination(
783                to_non_interned_lc(self.lc_map.get(idx).unwrap(), &self.field_interner).collect(),
784            )
785        } else {
786            LinearCombination::from(var)
787        }
788    }
789
790    /// Given a linear combination, create a row in the matrix
791    #[inline]
792    pub(crate) fn make_row(&self, l: LinearCombination<F>) -> Vec<(F, usize)> {
793        let num_input = self.num_instance_variables();
794        l.0.into_iter()
795            .filter_map(|(coeff, var)| {
796                if coeff.is_zero() || var.is_zero() {
797                    None
798                } else {
799                    let index = var.get_variable_index(num_input);
800                    Some((coeff, index.unwrap()))
801                }
802            })
803            .collect()
804    }
805
806    /// Sets the flag for outlining the instances
807    pub(crate) fn set_instance_outliner(&mut self, instance_outliner: InstanceOutliner<F>) {
808        self.instance_outliner = Some(instance_outliner);
809    }
810
811    /// Returns the flag for outlining the instances, This is by default set
812    /// to false
813    pub(crate) fn should_outline_instances(&self) -> bool {
814        self.instance_outliner.is_some()
815    }
816
817    /// Outlines the instances in the constraint system.
818    ///
819    /// This function creates a new witness variable for each instance
820    /// variable and uses these witness variables in the constraints
821    /// instead of instance variables. This technique is useful for
822    /// verifier succinctness in some SNARKs like Garuda, Pari and
823    /// PolyMath.
824    /// After the function call, instances are only
825    /// used in the `C` matrix of r1cs
826    pub fn perform_instance_outlining(
827        &mut self,
828        outliner: InstanceOutliner<F>,
829    ) -> crate::gr1cs::Result<()> {
830        // First build a map from instance variables to witness variables
831        let mut instance_to_witness_map = Vec::<Variable>::new();
832        // Initialize the map with the one variable, this is done manually because we
833        // certainely need this variable and it might not show up in the lc_map
834        let one_witness_var = self.new_witness_variable(|| Ok(F::ONE))?;
835        instance_to_witness_map.push(one_witness_var);
836        let instance_assignment = &self.assignments.instance_assignment.clone();
837        // Skip the first one because that is the ONE variable.
838        for i in 1..self.num_instance_variables {
839            let value = instance_assignment.get(i).copied();
840            let witness_var =
841                self.new_witness_variable(|| value.ok_or(SynthesisError::AssignmentMissing))?;
842            instance_to_witness_map.push(witness_var);
843        }
844
845        // Now, Go over all the linear combinations and create a new witness for each
846        // instance variable you see
847        #[cfg(feature = "parallel")]
848        let lc_vars_iter_mut = self.lc_map.lc_vars_iter_mut();
849        #[cfg(not(feature = "parallel"))]
850        let lc_vars_iter_mut = self.lc_map.lc_vars_iter_mut();
851
852        lc_vars_iter_mut.for_each(|lc| {
853            for var in lc {
854                if var.is_instance() {
855                    *var = instance_to_witness_map[var.index().unwrap()];
856                } else if var.is_one() {
857                    *var = one_witness_var;
858                }
859            }
860        });
861        (outliner.func)(self, &instance_to_witness_map)?;
862        Ok(())
863    }
864}