Skip to main content

ark_relations/gr1cs/
constraint_system_ref.rs

1//! This module contains the implementation of a sharef reference to
2//! `ConstraintSystem` struct. Refer to the `ConstraintSystem` struct for the
3//! inner struct. Most of the functions of `ConstraintSystemRef` are just
4//! wrappers around the functions of `ConstraintSystem`.
5
6use crate::utils::IndexMap;
7use ark_std::boxed::Box;
8use ark_std::collections::BTreeMap;
9use core::cell::{Ref, RefCell, RefMut};
10
11use super::{
12    constraint_system::ConstraintSystem,
13    instance_outliner::InstanceOutliner,
14    predicate::{
15        polynomial_constraint::{R1CS_PREDICATE_LABEL, SR1CS_PREDICATE_LABEL},
16        Predicate, PredicateConstraintSystem,
17    },
18    Label, LinearCombination, Matrix, OptimizationGoal, SynthesisError, SynthesisMode, Variable,
19};
20use ark_ff::Field;
21use ark_std::{rc::Rc, string::String, vec::Vec};
22
23/// A shared reference to a constraint system that can be stored in high level
24/// variables.
25#[derive(Debug, Clone)]
26pub enum ConstraintSystemRef<F: Field> {
27    /// Represents the case where we *don't* need to allocate variables or
28    /// enforce constraints. Encountered when operating over constant
29    /// values.
30    None,
31    /// Represents the case where we *do* allocate variables or enforce
32    /// constraints.
33    CS(Rc<RefCell<ConstraintSystem<F>>>),
34}
35
36impl<F: Field> PartialEq for ConstraintSystemRef<F> {
37    fn eq(&self, other: &Self) -> bool {
38        match (self, other) {
39            (Self::None, Self::None) => true,
40            (..) => false,
41        }
42    }
43}
44
45impl<F: Field> Eq for ConstraintSystemRef<F> {}
46
47impl<F: Field> ConstraintSystemRef<F> {
48    /// Construct a `ConstraintSystemRef` from a `ConstraintSystem`.
49    #[inline]
50    pub fn new(inner: ConstraintSystem<F>) -> Self {
51        Self::CS(Rc::new(RefCell::new(inner)))
52    }
53
54    /// Returns the number of constraints in each predicate
55    pub fn get_all_predicates_num_constraints(&self) -> IndexMap<Label, usize> {
56        self.inner().map_or_else(IndexMap::default, |cs| {
57            cs.borrow().get_all_predicates_num_constraints()
58        })
59    }
60
61    /// Returns the number of constraints in the predicate with the given label
62    pub fn get_predicates_num_constraints(&self, predicate_label: &str) -> Option<usize> {
63        self.inner()
64            .and_then(|cs| cs.borrow().get_predicate_num_constraints(predicate_label))
65    }
66
67    /// Returns the arity of each predicate
68    pub fn get_all_predicate_arities(&self) -> IndexMap<Label, usize> {
69        self.inner().map_or_else(IndexMap::default, |cs| {
70            cs.borrow().get_all_predicate_arities()
71        })
72    }
73
74    /// Returns the predicate type of the predicate with the given label
75    pub fn get_predicate_arity(&self, predicate_label: &str) -> Option<usize> {
76        self.inner()
77            .and_then(|cs| cs.borrow().get_predicate_arity(predicate_label))
78    }
79
80    /// Returns the predicate types of each predicate
81    pub fn get_all_predicate_types(&self) -> BTreeMap<Label, Predicate<F>> {
82        self.inner()
83            .map_or(BTreeMap::new(), |cs| cs.borrow().get_all_predicate_types())
84    }
85
86    /// Returns the predicate type of the predicate with the given label
87    pub fn get_predicate_type(&self, predicate_label: &str) -> Option<Predicate<F>> {
88        self.inner()
89            .and_then(|cs| cs.borrow().get_predicate_type(predicate_label))
90    }
91
92    /// Returns the number of constraints which is the sum of the number of
93    /// constraints in each  predicate.    #[inline]
94    pub fn num_constraints(&self) -> usize {
95        self.inner().map_or(0, |cs| cs.borrow().num_constraints())
96    }
97
98    /// Returns the number of instance variables.
99    #[inline]
100    pub fn num_instance_variables(&self) -> usize {
101        self.inner()
102            .map_or(0, |cs| cs.borrow().num_instance_variables())
103    }
104
105    /// Returns the number of instance variables.
106    #[inline]
107    pub fn instance_assignment(&self) -> crate::gr1cs::Result<Vec<F>> {
108        self.inner()
109            .ok_or(SynthesisError::AssignmentMissing)
110            .and_then(|cs| cs.borrow().instance_assignment().map(|v| v.to_vec()))
111    }
112
113    /// Returns the number of instance variables.
114    #[inline]
115    pub fn witness_assignment(&self) -> crate::gr1cs::Result<Vec<F>> {
116        self.inner()
117            .ok_or(SynthesisError::AssignmentMissing)
118            .and_then(|cs| cs.borrow().witness_assignment().map(|v| v.to_vec()))
119    }
120
121    /// Returns the number of instance variables.
122    #[inline]
123    pub fn num_variables(&self) -> usize {
124        self.inner().map_or(0, |cs| cs.borrow().num_variables())
125    }
126
127    #[inline]
128    /// Returns the number of predicates.
129    pub fn num_predicates(&self) -> usize {
130        self.inner().map_or(0, |cs| cs.borrow().num_predicates())
131    }
132
133    /// Returns the number of witness variables.
134    #[inline]
135    pub fn num_witness_variables(&self) -> usize {
136        self.inner()
137            .map_or(0, |cs| cs.borrow().num_witness_variables())
138    }
139
140    /// Enforce a constraint in the constraint system. It takes a
141    /// predicate name and enforces a vector of linear combinations of the
142    /// length of the arity of the  predicate enforces the constraint.
143    #[inline]
144    pub fn enforce_constraint(
145        &self,
146        predicate_label: &str,
147        lc_vec: impl IntoIterator<
148            Item = Box<dyn FnOnce() -> LinearCombination<F>>,
149            IntoIter: ExactSizeIterator,
150        >,
151    ) -> crate::gr1cs::Result<()> {
152        self.inner()
153            .ok_or(SynthesisError::MissingCS)
154            .and_then(|cs| cs.borrow_mut().enforce_constraint(predicate_label, lc_vec))
155    }
156
157    /// Enforce a constraint with arity 2.
158    #[inline]
159    pub fn enforce_constraint_arity_2(
160        &self,
161        predicate_label: &str,
162        a: impl FnOnce() -> LinearCombination<F>,
163        b: impl FnOnce() -> LinearCombination<F>,
164    ) -> crate::gr1cs::Result<()> {
165        self.inner()
166            .ok_or(SynthesisError::MissingCS)
167            .and_then(|cs| {
168                cs.borrow_mut()
169                    .enforce_constraint_arity_2(predicate_label, a, b)
170            })
171    }
172
173    /// Enforce a constraint with arity 3.
174    #[inline]
175    pub fn enforce_constraint_arity_3(
176        &self,
177        predicate_label: &str,
178        a: impl FnOnce() -> LinearCombination<F>,
179        b: impl FnOnce() -> LinearCombination<F>,
180        c: impl FnOnce() -> LinearCombination<F>,
181    ) -> crate::gr1cs::Result<()> {
182        self.inner()
183            .ok_or(SynthesisError::MissingCS)
184            .and_then(|cs| {
185                cs.borrow_mut()
186                    .enforce_constraint_arity_3(predicate_label, a, b, c)
187            })
188    }
189
190    /// Enforce a constraint with arity 4.
191    #[inline]
192    pub fn enforce_constraint_arity_4(
193        &self,
194        predicate_label: &str,
195        a: impl FnOnce() -> LinearCombination<F>,
196        b: impl FnOnce() -> LinearCombination<F>,
197        c: impl FnOnce() -> LinearCombination<F>,
198        d: impl FnOnce() -> LinearCombination<F>,
199    ) -> crate::gr1cs::Result<()> {
200        self.inner()
201            .ok_or(SynthesisError::MissingCS)
202            .and_then(|cs| {
203                cs.borrow_mut()
204                    .enforce_constraint_arity_4(predicate_label, a, b, c, d)
205            })
206    }
207
208    /// Enforce a constraint with arity 5.
209    #[inline]
210    pub fn enforce_constraint_arity_5(
211        &self,
212        predicate_label: &str,
213        a: impl FnOnce() -> LinearCombination<F>,
214        b: impl FnOnce() -> LinearCombination<F>,
215        c: impl FnOnce() -> LinearCombination<F>,
216        d: impl FnOnce() -> LinearCombination<F>,
217        e: impl FnOnce() -> LinearCombination<F>,
218    ) -> crate::gr1cs::Result<()> {
219        self.inner()
220            .ok_or(SynthesisError::MissingCS)
221            .and_then(|cs| {
222                cs.borrow_mut()
223                    .enforce_constraint_arity_5(predicate_label, a, b, c, d, e)
224            })
225    }
226
227    /// Enforce an R1CS constraint in the constraint system.
228    /// On input `a`, `b`, and `c`, this method enforces that `a * b = c`.
229    /// If the R1CS predicate has not been already added, this method will add it.
230    ///
231    /// This method is a special case
232    /// of `enforce_constraint` and is used to provide a low-effort way to port prior
233    /// code that assumed that R1CS is the only kind of predicate.
234    #[inline]
235    pub fn enforce_r1cs_constraint(
236        &self,
237        a: impl FnOnce() -> LinearCombination<F>,
238        b: impl FnOnce() -> LinearCombination<F>,
239        c: impl FnOnce() -> LinearCombination<F>,
240    ) -> crate::gr1cs::Result<()> {
241        if !self.should_construct_matrices() {
242            return Ok(());
243        }
244        if !self.has_predicate(R1CS_PREDICATE_LABEL) {
245            return Err(SynthesisError::PredicateNotFound);
246        }
247        self.inner()
248            .ok_or(SynthesisError::MissingCS)
249            .and_then(|cs| cs.borrow_mut().enforce_r1cs_constraint(a, b, c))
250    }
251
252    /// Enforce a SquareR1CS constraint in the constraint system.
253    /// On input `a` and `b`, this method enforces that `a^2 = b`.
254    /// If the SquareR1CS predicate has not been added to the constraint system,
255    /// this method will add it.
256    /// This method is a special case of `enforce_constraint`.
257    #[inline]
258    pub fn enforce_sr1cs_constraint(
259        &self,
260        a: impl FnOnce() -> LinearCombination<F>,
261        b: impl FnOnce() -> LinearCombination<F>,
262    ) -> crate::gr1cs::Result<()> {
263        if !self.should_construct_matrices() {
264            return Ok(());
265        }
266        if !self.has_predicate(SR1CS_PREDICATE_LABEL) {
267            return Err(SynthesisError::PredicateNotFound);
268        }
269        self.inner()
270            .ok_or(SynthesisError::MissingCS)
271            .and_then(|cs| cs.borrow_mut().enforce_sr1cs_constraint(a, b))
272    }
273
274    /// Obtain a new variable representing the linear combination `lc`.
275    #[inline]
276    pub fn new_lc(
277        &self,
278        lc: impl FnOnce() -> LinearCombination<F>,
279    ) -> crate::gr1cs::Result<Variable> {
280        match self.inner() {
281            Some(cs) => cs.borrow_mut().new_lc(lc),
282            None => Err(SynthesisError::MissingCS),
283        }
284    }
285
286    /// Set `self.mode` to `mode`.
287    /// Sets the mode if there exists an underlying `ConstraintSystem`.
288    pub fn set_mode(&self, mode: SynthesisMode) {
289        self.inner().map_or((), |cs| cs.borrow_mut().set_mode(mode))
290    }
291
292    /// Check whether `self.mode == SynthesisMode::Setup`.
293    /// Returns true if
294    /// 1. There is an underlying `ConstraintSystem` and,
295    /// 2. It is in setup mode.
296    #[inline]
297    pub fn is_in_setup_mode(&self) -> bool {
298        self.inner()
299            .is_some_and(|cs| cs.borrow().is_in_setup_mode())
300    }
301
302    /// Check whether this constraint system aims to optimize weight,
303    /// number of constraints, or neither.
304    #[inline]
305    pub fn optimization_goal(&self) -> OptimizationGoal {
306        self.inner().map_or(OptimizationGoal::Constraints, |cs| {
307            cs.borrow().optimization_goal()
308        })
309    }
310
311    /// Specify whether this constraint system should aim to optimize weight,
312    /// number of constraints, or neither.
313    #[inline]
314    pub fn set_optimization_goal(&self, goal: OptimizationGoal) {
315        self.inner()
316            .map_or((), |cs| cs.borrow_mut().set_optimization_goal(goal))
317    }
318
319    /// Should we outline instances according to the optimization specified in
320    /// Section 3, Page 11 of [Polymath](https://eprint.iacr.org/2024/916)
321    /// By default, this flag is `false`.
322    #[inline]
323    pub fn should_outline_instances(&self) -> bool {
324        self.inner()
325            .is_some_and(|cs| cs.borrow().should_outline_instances())
326    }
327
328    /// Specify the strategy for how the instance should be outlined.
329    /// This should be compatible with the predicates in the constraint system.
330    #[inline]
331    pub fn set_instance_outliner(&self, outliner: InstanceOutliner<F>) {
332        self.inner()
333            .map_or((), |cs| cs.borrow_mut().set_instance_outliner(outliner))
334    }
335
336    /// Check whether or not `self` will construct matrices.
337    #[inline]
338    pub fn should_construct_matrices(&self) -> bool {
339        self.inner()
340            .is_some_and(|cs| cs.borrow().should_construct_matrices())
341    }
342
343    /// Obtain a variable representing a new public instance input.
344    #[inline]
345    pub fn new_input_variable<Func>(&self, f: Func) -> crate::utils::Result<Variable>
346    where
347        Func: FnOnce() -> crate::utils::Result<F>,
348    {
349        self.inner()
350            .ok_or(SynthesisError::MissingCS)
351            .and_then(|cs| {
352                if self.is_in_setup_mode() {
353                    cs.borrow_mut().new_input_variable(f)
354                } else {
355                    // This is needed to avoid double-borrows, because `f`
356                    // might itself mutably borrow `cs` (eg: `f = || g.value()`).
357                    let value = f();
358                    cs.borrow_mut().new_input_variable(|| value)
359                }
360            })
361    }
362
363    /// Obtain a new variable representing a new private witness variable.
364    #[inline]
365    pub fn new_witness_variable<Func>(&self, f: Func) -> crate::utils::Result<Variable>
366    where
367        Func: FnOnce() -> crate::utils::Result<F>,
368    {
369        let a = self
370            .inner()
371            .ok_or(SynthesisError::MissingCS)
372            .and_then(|cs| {
373                if self.is_in_setup_mode() {
374                    cs.borrow_mut().new_witness_variable(f)
375                } else {
376                    // This is needed to avoid double-borrows, because `f`
377                    // might itself mutably borrow `cs` (eg: `f = || g.value()`).
378                    let value = f();
379                    cs.borrow_mut().new_witness_variable(|| value)
380                }
381            });
382        a
383    }
384
385    /// Register a  predicate in the constraint system with a given label.
386    pub fn register_predicate(
387        &self,
388        label: &str,
389        predicate: PredicateConstraintSystem<F>,
390    ) -> crate::utils::Result<()> {
391        self.inner()
392            .ok_or(SynthesisError::MissingCS)
393            .and_then(|cs| cs.borrow_mut().register_predicate(label, predicate))
394    }
395
396    /// Remove a predicate with the given `label` from the constraint system.
397    pub fn remove_predicate(&self, label: &str) {
398        self.inner()
399            .map_or((), |cs| cs.borrow_mut().remove_predicate(label))
400    }
401
402    /// Checks if there is a predicate with the given label in the constraint
403    pub fn has_predicate(&self, predicate_label: &str) -> bool {
404        self.inner()
405            .is_some_and(|cs| cs.borrow().has_predicate(predicate_label))
406    }
407
408    /// Obtain the assignment corresponding to the `Variable` `v`.
409    pub fn assigned_value(&self, v: Variable) -> Option<F> {
410        self.inner().and_then(|cs| cs.borrow().assigned_value(v))
411    }
412
413    /// If `self` is satisfied, outputs `Ok(true)`.
414    /// If `self` is unsatisfied, outputs `Ok(false)`.
415    /// If `self.is_in_setup_mode()` or if `self == None`, outputs `Err(())`.
416    pub fn is_satisfied(&self) -> crate::utils::Result<bool> {
417        self.inner()
418            .ok_or(SynthesisError::MissingCS)
419            .and_then(|cs| cs.borrow().is_satisfied())
420    }
421
422    /// If `self` is satisfied, outputs `Ok(None)`.
423    /// If `self` is unsatisfied, outputs `Some(s,i)`, where `s` is the label of
424    /// the unsatisfied  prediacate and  `i` is the index of
425    /// the first unsatisfied constraint in that  predicate.
426    /// If `self.is_in_setup_mode()` or `self == None`, outputs `Err(())`.
427    pub fn which_is_unsatisfied(&self) -> crate::utils::Result<Option<String>> {
428        self.inner()
429            .ok_or(SynthesisError::MissingCS)
430            .and_then(|cs| cs.borrow().which_is_unsatisfied())
431    }
432
433    /// Finalize the constraint system (either by outlining or inlining,
434    /// if an optimization goal is set).
435    pub fn finalize(&self) {
436        if let Some(cs) = self.inner() {
437            cs.borrow_mut().finalize();
438        }
439    }
440
441    /// Naively inlines symbolic linear combinations into the linear
442    /// combinations that use them.
443    ///
444    /// Useful for standard pairing-based SNARKs where addition gates are cheap.
445    /// For example, in the SNARKs such as [\[Groth16\]](https://eprint.iacr.org/2016/260) and
446    /// [\[Groth-Maller17\]](https://eprint.iacr.org/2017/540), addition gates
447    /// do not contribute to the size of the multi-scalar multiplication, which
448    /// is the dominating cost.
449    pub fn inline_all_lcs(&self) {
450        if let Some(cs) = self.inner() {
451            cs.borrow_mut().inline_all_lcs();
452        }
453    }
454
455    /// Returns `self` if `!self.is_none()`, otherwise returns `other`.
456    #[must_use]
457    pub fn or(self, other: Self) -> Self {
458        match self {
459            ConstraintSystemRef::None => other,
460            ConstraintSystemRef::CS(_) => self,
461        }
462    }
463
464    /// Returns `true` is `self == ConstraintSystemRef::None`.
465    pub fn is_none(&self) -> bool {
466        matches!(self, ConstraintSystemRef::None)
467    }
468
469    pub(crate) fn inner(&self) -> Option<&Rc<RefCell<ConstraintSystem<F>>>> {
470        match self {
471            Self::CS(a) => Some(a),
472            Self::None => None,
473        }
474    }
475
476    /// Consumes self to return the inner `ConstraintSystem<F>`. Returns
477    /// `None` if `Self::CS` is `None` or if any other references to
478    /// `Self::CS` exist.  
479    pub fn into_inner(self) -> Option<ConstraintSystem<F>> {
480        match self {
481            Self::CS(a) => Rc::try_unwrap(a).ok().map(|s| s.into_inner()),
482            Self::None => None,
483        }
484    }
485
486    /// Get the matrices corresponding to the  predicates.and the
487    /// corresponding set of matrices
488    #[inline]
489    pub fn to_matrices(&self) -> crate::gr1cs::Result<BTreeMap<Label, Vec<Matrix<F>>>> {
490        self.inner()
491            .ok_or(SynthesisError::MissingCS)
492            .and_then(|cs| cs.borrow().to_matrices())
493    }
494
495    /// Get the linear combination corresponding to the given `lc_index`.
496    /// TODO: This function should ideally return a reference to the linear
497    /// combination and not clone it.
498    pub fn get_lc(&self, var: Variable) -> Option<LinearCombination<F>> {
499        self.inner().map(|cs| cs.borrow().get_lc(var))
500    }
501
502    /// Given a linear combination, create a row in the matrix
503    pub fn make_row(&self, lc: LinearCombination<F>) -> crate::utils::Result<Vec<(F, usize)>> {
504        self.inner()
505            .ok_or(SynthesisError::MissingCS)
506            .map(|cs| cs.borrow().make_row(lc))
507    }
508
509    /// Obtain an immutable reference to the underlying `ConstraintSystem`.
510    ///
511    /// # Panics
512    /// This method panics if `self` is already mutably borrowed.
513    #[inline]
514    pub fn borrow(&self) -> Option<Ref<'_, ConstraintSystem<F>>> {
515        self.inner().map(|cs| cs.borrow())
516    }
517
518    /// Obtain a mutable reference to the underlying `ConstraintSystem`.
519    ///
520    /// # Panics
521    /// This method panics if `self` is already mutably borrowed.
522    #[inline]
523    pub fn borrow_mut(&self) -> Option<RefMut<'_, ConstraintSystem<F>>> {
524        self.inner().map(|cs| cs.borrow_mut())
525    }
526
527    /// Get trace information about all constraints in the system
528    pub fn constraint_names(&self) -> Option<Vec<String>> {
529        #[cfg(feature = "std")]
530        {
531            self.inner().and_then(|cs| {
532                cs.borrow()
533                    .predicate_traces
534                    .iter()
535                    .flat_map(|(key, values)| values.iter().map(move |v| (key.clone(), v)))
536                    .map(|(label, trace)| {
537                        let mut constraint_path = String::new();
538                        let mut prev_module_path = "";
539                        let mut prefixes = ark_std::collections::BTreeSet::new();
540                        for step in trace.as_ref()?.path() {
541                            let module_path = if prev_module_path == step.module_path {
542                                prefixes.insert(step.module_path.to_string());
543                                String::new()
544                            } else {
545                                let mut parts = step
546                                    .module_path
547                                    .split("::")
548                                    .filter(|&part| part != "r1cs_std" && part != "constraints");
549                                let mut path_so_far = String::new();
550                                for part in parts.by_ref() {
551                                    if path_so_far.is_empty() {
552                                        path_so_far += part;
553                                    } else {
554                                        path_so_far += &["::", part].join("");
555                                    }
556                                    if prefixes.contains(&path_so_far) {
557                                        continue;
558                                    } else {
559                                        prefixes.insert(path_so_far.clone());
560                                        break;
561                                    }
562                                }
563                                parts.collect::<Vec<_>>().join("::") + "::"
564                            };
565                            prev_module_path = step.module_path;
566                            constraint_path += &["/", &module_path, step.name].join("");
567                        }
568                        Some(constraint_path + " (predicate:" + &label + ")")
569                    })
570                    .collect::<Option<Vec<_>>>()
571            })
572        }
573        #[cfg(not(feature = "std"))]
574        {
575            None
576        }
577    }
578}