snarkvm_r1cs/
test_constraint_system.rs

1// Copyright (C) 2019-2023 Aleo Systems Inc.
2// This file is part of the snarkVM library.
3
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at:
7// http://www.apache.org/licenses/LICENSE-2.0
8
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use crate::{errors::SynthesisError, ConstraintSystem, Index, LinearCombination, OptionalVec, Variable};
16use snarkvm_fields::Field;
17
18use cfg_if::cfg_if;
19use fxhash::{FxBuildHasher, FxHashMap};
20use indexmap::{map::Entry, IndexMap, IndexSet};
21use itertools::Itertools;
22
23/// This field is the scalar field (Fr) of BLS12-377.
24pub type Fr = snarkvm_curves::bls12_377::Fr;
25
26#[derive(Debug, Clone)]
27enum NamedObject {
28    Constraint(usize),
29    Var(Variable),
30    // contains the list of named objects that belong to it
31    Namespace(Namespace),
32}
33
34#[derive(Debug, Clone, Default)]
35struct Namespace {
36    children: Vec<NamedObject>,
37    idx: NamespaceIndex,
38}
39
40impl Namespace {
41    fn push(&mut self, child: NamedObject) {
42        self.children.push(child);
43    }
44}
45
46type InternedField = usize;
47type InternedPathSegment = usize;
48type NamespaceIndex = usize;
49
50#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
51pub struct InternedPath {
52    parent_namespace: NamespaceIndex,
53    last_segment: InternedPathSegment,
54}
55
56#[derive(PartialEq, Eq, Hash)]
57pub struct TestConstraint {
58    interned_path: InternedPath,
59    a: Vec<(Variable, InternedField)>,
60    b: Vec<(Variable, InternedField)>,
61    c: Vec<(Variable, InternedField)>,
62}
63
64#[derive(Default, Debug)]
65pub struct CurrentNamespace {
66    segments: Vec<InternedPathSegment>,
67    indices: Vec<NamespaceIndex>,
68}
69
70impl CurrentNamespace {
71    fn idx(&self) -> usize {
72        self.indices.last().copied().unwrap_or(0)
73    }
74
75    fn pop(&mut self) {
76        assert!(self.segments.pop().is_some());
77        assert!(self.indices.pop().is_some());
78    }
79}
80
81/// Constraint system for testing purposes.
82pub struct TestConstraintSystem<F: Field> {
83    // used to intern full paths in test scenarios, for get and set purposes
84    interned_full_paths: FxHashMap<Vec<InternedPathSegment>, InternedPath>,
85    // used to intern namespace segments
86    interned_path_segments: IndexSet<String, FxBuildHasher>,
87    // used to intern fields belonging to F
88    interned_fields: IndexSet<F, FxBuildHasher>,
89    // contains named objects bound to their (interned) paths; the indices are
90    // used for NamespaceIndex lookups
91    named_objects: IndexMap<InternedPath, NamedObject, FxBuildHasher>,
92    // a stack of current path's segments and the index of the current path's
93    // index in the named_objects map
94    current_namespace: CurrentNamespace,
95    // the list of currently applicable constraints
96    constraints: OptionalVec<TestConstraint>,
97    // the list of currently applicable input variables
98    public_variables: OptionalVec<InternedField>,
99    // the list of currently applicable auxiliary variables
100    private_variables: OptionalVec<InternedField>,
101}
102
103impl<F: Field> Default for TestConstraintSystem<F> {
104    fn default() -> Self {
105        let mut interned_path_segments = IndexSet::with_hasher(FxBuildHasher::default());
106        let path_segment = "ONE".to_owned();
107        let interned_path_segment = interned_path_segments.insert_full(path_segment).0;
108        let interned_path = InternedPath { parent_namespace: 0, last_segment: interned_path_segment };
109
110        cfg_if! {
111            if #[cfg(debug_assertions)] {
112                let mut interned_full_paths = FxHashMap::default();
113                interned_full_paths.insert(vec![interned_path_segment], interned_path);
114            } else {
115                let interned_full_paths = FxHashMap::default();
116            }
117        }
118
119        let mut named_objects = IndexMap::with_hasher(FxBuildHasher::default());
120        named_objects.insert_full(interned_path, NamedObject::Var(TestConstraintSystem::<F>::one()));
121
122        let mut interned_fields = IndexSet::with_hasher(FxBuildHasher::default());
123        let interned_field = interned_fields.insert_full(F::one()).0;
124
125        let mut inputs: OptionalVec<InternedField> = Default::default();
126        inputs.insert(interned_field);
127
128        let constraints = OptionalVec::default();
129
130        TestConstraintSystem {
131            interned_full_paths,
132            interned_fields,
133            interned_path_segments,
134            named_objects,
135            current_namespace: Default::default(),
136            constraints,
137            public_variables: inputs,
138            private_variables: Default::default(),
139        }
140    }
141}
142
143impl<F: Field> TestConstraintSystem<F> {
144    pub fn new() -> Self {
145        Self::default()
146    }
147
148    /// Prints the constraint at which `self` and `other` differ.
149    pub fn diff(&self, other: &Self) {
150        for (i, (self_c, other_c)) in self.constraints.iter().zip(other.constraints.iter()).enumerate() {
151            let self_interned_path = self_c.interned_path;
152            let other_interned_path = other_c.interned_path;
153            if self_c.a != other_c.a {
154                println!("A row {i} is different:");
155                println!("self: {}", self.unintern_path(self_interned_path));
156                println!("other: {}", other.unintern_path(other_interned_path));
157                break;
158            }
159
160            if self_c.b != other_c.b {
161                println!("B row {i} is different:");
162                println!("self: {}", self.unintern_path(self_interned_path));
163                println!("other: {}", other.unintern_path(other_interned_path));
164                break;
165            }
166
167            if self_c.c != other_c.c {
168                println!("C row {i} is different:");
169                println!("self: {}", self.unintern_path(self_interned_path));
170                println!("other: {}", other.unintern_path(other_interned_path));
171                break;
172            }
173        }
174    }
175
176    #[inline]
177    fn intern_path(&self, path: &str) -> InternedPath {
178        let mut vec = vec![];
179
180        for segment in path.split('/') {
181            vec.push(self.interned_path_segments.get_index_of(segment).unwrap());
182        }
183
184        *self.interned_full_paths.get(&vec).unwrap()
185    }
186
187    fn unintern_path(&self, interned_path: InternedPath) -> String {
188        let last_segment = self.interned_path_segments.get_index(interned_path.last_segment).unwrap();
189        let mut reversed_uninterned_segments = vec![last_segment];
190
191        let mut parent_ns = interned_path.parent_namespace;
192        while parent_ns != 0 {
193            let interned_parent_ns = self.named_objects.get_index(parent_ns).unwrap().0;
194            let parent_segment = self.interned_path_segments.get_index(interned_parent_ns.last_segment).unwrap();
195            reversed_uninterned_segments.push(parent_segment);
196            parent_ns = interned_parent_ns.parent_namespace;
197        }
198
199        let segments = reversed_uninterned_segments.into_iter().map(|s| s.as_str()).rev();
200        Itertools::intersperse(segments, "/").collect()
201    }
202
203    pub fn print_named_objects(&self) {
204        for TestConstraint { interned_path, .. } in self.constraints.iter() {
205            println!("{}", self.unintern_path(*interned_path));
206        }
207    }
208
209    fn eval_lc(&self, terms: &[(Variable, InternedField)]) -> F {
210        let mut acc = F::zero();
211
212        for &(var, interned_coeff) in terms {
213            let interned_tmp = match var.get_unchecked() {
214                Index::Public(index) => self.public_variables[index],
215                Index::Private(index) => self.private_variables[index],
216            };
217            let mut tmp = *self.interned_fields.get_index(interned_tmp).unwrap();
218            let coeff = self.interned_fields.get_index(interned_coeff).unwrap();
219
220            tmp.mul_assign(coeff);
221            acc.add_assign(tmp);
222        }
223
224        acc
225    }
226
227    pub fn which_is_unsatisfied(&self) -> Option<String> {
228        for TestConstraint { interned_path, a, b, c } in self.constraints.iter() {
229            let mut a = self.eval_lc(a.as_ref());
230            let b = self.eval_lc(b.as_ref());
231            let c = self.eval_lc(c.as_ref());
232
233            a.mul_assign(&b);
234
235            if a != c {
236                return Some(self.unintern_path(*interned_path));
237            }
238        }
239
240        None
241    }
242
243    #[inline]
244    pub fn is_satisfied(&self) -> bool {
245        self.which_is_unsatisfied().is_none()
246    }
247
248    #[inline]
249    pub fn num_non_zero(&self) -> (usize, usize, usize) {
250        let mut non_zero_a = 0;
251        let mut non_zero_b = 0;
252        let mut non_zero_c = 0;
253        for TestConstraint { a, b, c, .. } in self.constraints.iter() {
254            non_zero_a += a.len();
255            non_zero_b += b.len();
256            non_zero_c += c.len();
257        }
258        (non_zero_a, non_zero_b, non_zero_c)
259    }
260
261    #[inline]
262    pub fn num_constraints(&self) -> usize {
263        self.constraints.len()
264    }
265
266    #[inline]
267    pub fn get_constraint_path(&self, i: usize) -> String {
268        self.unintern_path(self.constraints.iter().nth(i).unwrap().interned_path)
269    }
270
271    pub fn set(&mut self, path: &str, to: F) {
272        let interned_path = self.intern_path(path);
273        let interned_field = self.interned_fields.insert_full(to).0;
274
275        match self.named_objects.get(&interned_path) {
276            Some(NamedObject::Var(v)) => match v.get_unchecked() {
277                Index::Public(index) => self.public_variables[index] = interned_field,
278                Index::Private(index) => self.private_variables[index] = interned_field,
279            },
280            Some(e) => panic!("tried to set path `{path}` to value, but `{e:?}` already exists there."),
281            _ => panic!("no variable exists at path: {path}"),
282        }
283    }
284
285    pub fn get(&mut self, path: &str) -> F {
286        let interned_path = self.intern_path(path);
287
288        let interned_field = match self.named_objects.get(&interned_path) {
289            Some(NamedObject::Var(v)) => match v.get_unchecked() {
290                Index::Public(index) => self.public_variables[index],
291                Index::Private(index) => self.private_variables[index],
292            },
293            Some(e) => panic!("tried to get value of path `{path}`, but `{e:?}` exists there (not a variable)"),
294            _ => panic!("no variable exists at path: {path}"),
295        };
296
297        *self.interned_fields.get_index(interned_field).unwrap()
298    }
299
300    #[inline]
301    fn set_named_obj(&mut self, interned_path: InternedPath, to: NamedObject) -> NamespaceIndex {
302        match self.named_objects.entry(interned_path) {
303            Entry::Vacant(e) => {
304                let ns_idx = e.index();
305                e.insert(to);
306                ns_idx
307            }
308            Entry::Occupied(e) => {
309                let interned_segments = e.remove_entry().0;
310                panic!("tried to create object at existing path: {}", self.unintern_path(interned_segments));
311            }
312        }
313    }
314
315    #[inline]
316    fn compute_path(&mut self, new_segment: &str) -> InternedPath {
317        let (interned_segment, new) = if let Some(index) = self.interned_path_segments.get_index_of(new_segment) {
318            (index, false)
319        } else {
320            self.interned_path_segments.insert_full(new_segment.to_owned())
321        };
322
323        // only perform the check for segments not seen before
324        assert!(!new || !new_segment.contains('/'), "'/' is not allowed in names");
325
326        let interned_path =
327            InternedPath { parent_namespace: self.current_namespace.idx(), last_segment: interned_segment };
328
329        cfg_if! {
330            if #[cfg(debug_assertions)] {
331                let mut full_path = self.current_namespace.segments.clone();
332                full_path.push(interned_segment);
333                self.interned_full_paths.insert(full_path, interned_path);
334            }
335        }
336
337        interned_path
338    }
339
340    #[cfg(not(debug_assertions))]
341    fn purge_namespace(&mut self, namespace: Namespace) {
342        for child_obj in namespace.children {
343            match child_obj {
344                NamedObject::Var(var) => match var.get_unchecked() {
345                    Index::Private(idx) => {
346                        self.private_variables.remove(idx);
347                    }
348                    Index::Public(idx) => {
349                        self.public_variables.remove(idx);
350                    }
351                },
352                NamedObject::Constraint(idx) => {
353                    self.constraints.remove(idx);
354                }
355                NamedObject::Namespace(children) => {
356                    self.purge_namespace(children);
357                }
358            }
359            self.named_objects.swap_remove_index(namespace.idx);
360        }
361    }
362
363    #[inline]
364    fn register_object_in_namespace(&mut self, named_obj: NamedObject) {
365        if let NamedObject::Namespace(ref mut ns) =
366            self.named_objects.get_index_mut(self.current_namespace.idx()).unwrap().1
367        {
368            ns.push(named_obj);
369        }
370    }
371}
372
373impl<F: Field> ConstraintSystem<F> for TestConstraintSystem<F> {
374    type Root = Self;
375
376    fn alloc<Fn, A, AR>(&mut self, annotation: A, f: Fn) -> Result<Variable, SynthesisError>
377    where
378        Fn: FnOnce() -> Result<F, SynthesisError>,
379        A: FnOnce() -> AR,
380        AR: AsRef<str>,
381    {
382        let interned_path = self.compute_path(annotation().as_ref());
383        let interned_field = self.interned_fields.insert_full(f()?).0;
384        let index = self.private_variables.insert(interned_field);
385        let var = Variable::new_unchecked(Index::Private(index));
386        let named_obj = NamedObject::Var(var);
387        self.register_object_in_namespace(named_obj.clone());
388        self.set_named_obj(interned_path, named_obj);
389
390        Ok(var)
391    }
392
393    fn alloc_input<Fn, A, AR>(&mut self, annotation: A, f: Fn) -> Result<Variable, SynthesisError>
394    where
395        Fn: FnOnce() -> Result<F, SynthesisError>,
396        A: FnOnce() -> AR,
397        AR: AsRef<str>,
398    {
399        let interned_path = self.compute_path(annotation().as_ref());
400        let interned_field = self.interned_fields.insert_full(f()?).0;
401        let index = self.public_variables.insert(interned_field);
402        let var = Variable::new_unchecked(Index::Public(index));
403        let named_obj = NamedObject::Var(var);
404        self.register_object_in_namespace(named_obj.clone());
405        self.set_named_obj(interned_path, named_obj);
406
407        Ok(var)
408    }
409
410    fn enforce<A, AR, LA, LB, LC>(&mut self, annotation: A, a: LA, b: LB, c: LC)
411    where
412        A: FnOnce() -> AR,
413        AR: AsRef<str>,
414        LA: FnOnce(LinearCombination<F>) -> LinearCombination<F>,
415        LB: FnOnce(LinearCombination<F>) -> LinearCombination<F>,
416        LC: FnOnce(LinearCombination<F>) -> LinearCombination<F>,
417    {
418        let interned_path = self.compute_path(annotation().as_ref());
419        let index = self.constraints.next_idx();
420        let named_obj = NamedObject::Constraint(index);
421        self.register_object_in_namespace(named_obj.clone());
422        self.set_named_obj(interned_path, named_obj);
423
424        let mut intern_fields = |uninterned: Vec<(Variable, F)>| -> Vec<(Variable, InternedField)> {
425            uninterned
426                .into_iter()
427                .map(|(var, field)| {
428                    let interned_field = self.interned_fields.insert_full(field).0;
429                    (var, interned_field)
430                })
431                .collect()
432        };
433
434        let a = intern_fields(a(LinearCombination::zero()).0);
435        let b = intern_fields(b(LinearCombination::zero()).0);
436        let c = intern_fields(c(LinearCombination::zero()).0);
437
438        self.constraints.insert(TestConstraint { interned_path, a, b, c });
439    }
440
441    fn push_namespace<NR: AsRef<str>, N: FnOnce() -> NR>(&mut self, name_fn: N) {
442        let name = name_fn();
443        let interned_path = self.compute_path(name.as_ref());
444        let new_segment = interned_path.last_segment;
445        let named_obj = NamedObject::Namespace(Default::default());
446        self.register_object_in_namespace(named_obj.clone());
447        let namespace_idx = self.set_named_obj(interned_path, named_obj);
448        if let NamedObject::Namespace(ref mut ns) = self.named_objects[namespace_idx] {
449            ns.idx = namespace_idx;
450        };
451
452        self.current_namespace.segments.push(new_segment);
453        self.current_namespace.indices.push(namespace_idx);
454    }
455
456    #[cfg(not(debug_assertions))]
457    fn pop_namespace(&mut self) {
458        let namespace = if let NamedObject::Namespace(no) =
459            self.named_objects.swap_remove_index(self.current_namespace.idx()).unwrap().1
460        {
461            no
462        } else {
463            unreachable!()
464        };
465
466        // remove object belonging to the popped namespace
467        self.purge_namespace(namespace);
468
469        // update the current namespace
470        self.current_namespace.pop();
471    }
472
473    #[cfg(debug_assertions)]
474    fn pop_namespace(&mut self) {
475        // don't perform a full cleanup in test conditions, so that all the variables,
476        // constraints and namespace indices remain available throughout the tests
477
478        self.current_namespace.pop();
479    }
480
481    #[inline]
482    fn get_root(&mut self) -> &mut Self::Root {
483        self
484    }
485
486    #[inline]
487    fn num_constraints(&self) -> usize {
488        self.num_constraints()
489    }
490
491    #[inline]
492    fn num_public_variables(&self) -> usize {
493        self.public_variables.len()
494    }
495
496    #[inline]
497    fn num_private_variables(&self) -> usize {
498        self.private_variables.len()
499    }
500
501    #[inline]
502    fn is_in_setup_mode(&self) -> bool {
503        false
504    }
505}