1use 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#[derive(Debug, Clone)]
44pub struct ConstraintSystem<F: Field> {
45 mode: SynthesisMode,
51
52 #[doc(hidden)]
55 pub num_instance_variables: usize,
56
57 #[doc(hidden)]
60 pub num_witness_variables: usize,
61
62 #[doc(hidden)]
64 pub num_linear_combinations: usize,
65
66 optimization_goal: OptimizationGoal,
69
70 instance_outliner: Option<InstanceOutliner<F>>,
75
76 pub assignments: Assignments<F>,
79
80 pub cache_map: Rc<RefCell<BTreeMap<TypeId, Box<dyn Any>>>>,
82
83 #[doc(hidden)]
86 pub lc_map: LcMap<F>,
87
88 field_interner: FieldInterner<F>,
89
90 #[doc(hidden)]
92 pub predicate_constraint_systems: BTreeMap<Label, PredicateConstraintSystem<F>>,
93
94 #[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 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 pub fn new_ref() -> ConstraintSystemRef<F> {
143 ConstraintSystemRef::new(Self::new())
144 }
145
146 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 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 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 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 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 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 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 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 pub fn num_constraints(&self) -> usize {
211 self.predicate_constraint_systems
212 .values()
213 .map(|p| p.num_constraints())
214 .sum()
215 }
216
217 pub fn num_instance_variables(&self) -> usize {
219 self.num_instance_variables
220 }
221
222 pub fn num_witness_variables(&self) -> usize {
224 self.num_witness_variables
225 }
226
227 pub fn num_variables(&self) -> usize {
229 self.num_witness_variables() + self.num_instance_variables()
230 }
231
232 pub fn num_predicates(&self) -> usize {
234 self.predicate_constraint_systems.len()
235 }
236
237 #[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 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 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 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 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 #[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 #[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 #[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 #[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 [] | [(_, Variable::Zero)] => Variable::symbolic_lc(0),
483 [(c, var)] if c.is_one() => *var,
486 _ => {
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 #[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 #[inline]
523 pub fn new_lc(
524 &mut self,
525 lc: impl FnOnce() -> LinearCombination<F>,
526 ) -> crate::gr1cs::Result<Variable> {
527 Ok(self.new_lc_helper(lc))
532 }
533
534 pub fn set_mode(&mut self, mode: SynthesisMode) {
536 self.mode = mode;
537 }
538
539 pub fn is_in_setup_mode(&self) -> bool {
544 self.mode == SynthesisMode::Setup
545 }
546
547 pub fn optimization_goal(&self) -> OptimizationGoal {
550 self.optimization_goal
551 }
552
553 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 pub fn set_optimization_goal(&mut self, goal: OptimizationGoal) {
564 assert!(self.is_new());
565 self.optimization_goal = goal;
566 }
567
568 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 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 #[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 #[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 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 pub fn remove_predicate(&mut self, predicate_label: &str) {
635 self.predicate_constraint_systems.remove(predicate_label);
636 }
637
638 pub fn has_predicate(&self, predicate_label: &str) -> bool {
640 self.predicate_constraint_systems
641 .contains_key(predicate_label)
642 }
643
644 pub fn assigned_value(&self, v: Variable) -> Option<F> {
646 self.assignments.assigned_value(v)
647 }
648
649 pub fn is_satisfied(&self) -> crate::utils::Result<bool> {
653 self.which_is_unsatisfied().map(|s| s.is_none())
654 }
655
656 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 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 let timer_instance_outlining = start_timer!(|| "Instance Outlining");
698 if let Some(instance_outliner) = self.instance_outliner.take() {
699 if self.has_predicate(&instance_outliner.pred_label) {
701 let _ = self.perform_instance_outlining(instance_outliner);
703 }
704 }
705 end_timer!(timer_instance_outlining);
706 end_timer!(timer_finalize);
707 }
708
709 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 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 fn any_lcs_used(&self) -> bool {
763 cfg_iter!(self.lc_map).any(|mut lc| lc.any(|(_, v)| v.is_lc()))
764 }
765
766 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 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 #[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 pub(crate) fn set_instance_outliner(&mut self, instance_outliner: InstanceOutliner<F>) {
808 self.instance_outliner = Some(instance_outliner);
809 }
810
811 pub(crate) fn should_outline_instances(&self) -> bool {
814 self.instance_outliner.is_some()
815 }
816
817 pub fn perform_instance_outlining(
827 &mut self,
828 outliner: InstanceOutliner<F>,
829 ) -> crate::gr1cs::Result<()> {
830 let mut instance_to_witness_map = Vec::<Variable>::new();
832 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 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 #[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}