1use super::lookup::runtime_tables::RuntimeTableCfg;
3use crate::{
4 circuits::{
5 domain_constant_evaluation::DomainConstantEvaluations,
6 domains::EvaluationDomains,
7 gate::{CircuitGate, GateType},
8 lookup::{
9 index::{LookupConstraintSystem, LookupError},
10 lookups::{LookupFeatures, LookupPatterns},
11 tables::{GateLookupTables, LookupTable},
12 },
13 polynomial::{WitnessEvals, WitnessOverDomains, WitnessShifts},
14 polynomials::permutation::Shifts,
15 wires::*,
16 },
17 curve::KimchiCurve,
18 error::{DomainCreationError, SetupError},
19 o1_utils::lazy_cache::LazyCache,
20 prover_index::ProverIndex,
21};
22use ark_ff::{PrimeField, Zero};
23use ark_poly::{
24 univariate::DensePolynomial as DP, EvaluationDomain, Evaluations as E,
25 Radix2EvaluationDomain as D,
26};
27use core::{array, default::Default};
28use o1_utils::ExtendedEvaluations;
29use poly_commitment::OpenProof;
30use rayon::prelude::*;
31use serde::{de::DeserializeOwned, Deserialize, Serialize};
32use serde_with::serde_as;
33use std::sync::Arc;
34
35#[cfg_attr(
41 feature = "ocaml_types",
42 derive(ocaml::IntoValue, ocaml::FromValue, ocaml_gen::Struct)
43)]
44#[cfg_attr(feature = "wasm_types", wasm_bindgen::prelude::wasm_bindgen)]
45#[derive(Copy, Clone, Serialize, Deserialize, Debug)]
46pub struct FeatureFlags {
47 pub range_check0: bool,
49 pub range_check1: bool,
51 pub foreign_field_add: bool,
53 pub foreign_field_mul: bool,
55 pub xor: bool,
57 pub rot: bool,
59 pub lookup_features: LookupFeatures,
61}
62
63impl Default for FeatureFlags {
64 fn default() -> FeatureFlags {
66 FeatureFlags {
67 range_check0: false,
68 range_check1: false,
69 lookup_features: LookupFeatures {
70 patterns: LookupPatterns {
71 xor: false,
72 lookup: false,
73 range_check: false,
74 foreign_field_mul: false,
75 },
76 joint_lookup_used: false,
77 uses_runtime_tables: false,
78 },
79 foreign_field_add: false,
80 foreign_field_mul: false,
81 xor: false,
82 rot: false,
83 }
84 }
85}
86
87#[serde_as]
89#[derive(Clone, Serialize, Deserialize, Debug)]
90pub struct EvaluatedColumnCoefficients<F: PrimeField> {
91 #[serde_as(as = "[o1_utils::serialization::SerdeAs; PERMUTS]")]
93 pub permutation_coefficients: [DP<F>; PERMUTS],
94
95 #[serde_as(as = "[o1_utils::serialization::SerdeAs; COLUMNS]")]
97 pub coefficients: [DP<F>; COLUMNS],
98
99 #[serde_as(as = "o1_utils::serialization::SerdeAs")]
101 pub generic_selector: DP<F>,
102
103 #[serde_as(as = "o1_utils::serialization::SerdeAs")]
105 pub poseidon_selector: DP<F>,
106}
107
108#[serde_as]
111#[derive(Clone, Serialize, Deserialize, Debug)]
112pub struct ColumnEvaluations<F: PrimeField> {
113 #[serde_as(as = "[o1_utils::serialization::SerdeAs; PERMUTS]")]
115 pub permutation_coefficients8: [E<F, D<F>>; PERMUTS],
116
117 #[serde_as(as = "[o1_utils::serialization::SerdeAs; COLUMNS]")]
119 pub coefficients8: [E<F, D<F>>; COLUMNS],
120
121 #[serde_as(as = "o1_utils::serialization::SerdeAs")]
123 pub generic_selector4: E<F, D<F>>,
124
125 #[serde_as(as = "o1_utils::serialization::SerdeAs")]
127 pub poseidon_selector8: E<F, D<F>>,
128
129 #[serde_as(as = "o1_utils::serialization::SerdeAs")]
131 pub complete_add_selector4: E<F, D<F>>,
132
133 #[serde_as(as = "o1_utils::serialization::SerdeAs")]
135 pub mul_selector8: E<F, D<F>>,
136
137 #[serde_as(as = "o1_utils::serialization::SerdeAs")]
139 pub emul_selector8: E<F, D<F>>,
140
141 #[serde_as(as = "o1_utils::serialization::SerdeAs")]
143 pub endomul_scalar_selector8: E<F, D<F>>,
144
145 #[serde_as(as = "Option<o1_utils::serialization::SerdeAs>")]
147 pub range_check0_selector8: Option<E<F, D<F>>>,
148
149 #[serde_as(as = "Option<o1_utils::serialization::SerdeAs>")]
151 pub range_check1_selector8: Option<E<F, D<F>>>,
152
153 #[serde_as(as = "Option<o1_utils::serialization::SerdeAs>")]
155 pub foreign_field_add_selector8: Option<E<F, D<F>>>,
156
157 #[serde_as(as = "Option<o1_utils::serialization::SerdeAs>")]
159 pub foreign_field_mul_selector8: Option<E<F, D<F>>>,
160
161 #[serde_as(as = "Option<o1_utils::serialization::SerdeAs>")]
163 pub xor_selector8: Option<E<F, D<F>>>,
164
165 #[serde_as(as = "Option<o1_utils::serialization::SerdeAs>")]
167 pub rot_selector8: Option<E<F, D<F>>>,
168}
169
170#[serde_as]
171#[derive(Clone, Serialize, Debug)]
172pub struct ConstraintSystem<F: PrimeField> {
173 pub public: usize,
177 pub prev_challenges: usize,
179 #[serde(bound = "EvaluationDomains<F>: Serialize + DeserializeOwned")]
181 pub domain: EvaluationDomains<F>,
182 #[serde(bound = "CircuitGate<F>: Serialize + DeserializeOwned")]
184 pub gates: Arc<Vec<CircuitGate<F>>>,
185
186 pub zk_rows: u64,
187
188 pub feature_flags: FeatureFlags,
190
191 #[serde_as(as = "Vec<o1_utils::serialization::SerdeAs>")]
193 pub sid: Vec<F>,
194
195 #[serde_as(as = "[o1_utils::serialization::SerdeAs; PERMUTS]")]
197 pub shift: [F; PERMUTS],
198 #[serde_as(as = "o1_utils::serialization::SerdeAs")]
200 pub endo: F,
201 #[serde(bound = "LookupConstraintSystem<F>: Serialize + DeserializeOwned")]
203 pub lookup_constraint_system: Arc<LookupConstraintSystemCache<F>>,
204 #[serde(skip)]
206 precomputations: Arc<LazyCache<Arc<DomainConstantEvaluations<F>>>>,
207
208 pub disable_gates_checks: bool,
210}
211
212pub(crate) type LookupConstraintSystemCache<F> =
213 LazyCache<Result<Option<LookupConstraintSystem<F>>, LookupError>>;
214
215impl<'de, F> Deserialize<'de> for ConstraintSystem<F>
216where
217 F: PrimeField,
218 EvaluationDomains<F>: Serialize + DeserializeOwned,
219 CircuitGate<F>: Serialize + DeserializeOwned,
220 LookupConstraintSystem<F>: Serialize + DeserializeOwned,
221{
222 fn deserialize<D>(deserializer: D) -> Result<ConstraintSystem<F>, D::Error>
223 where
224 D: serde::Deserializer<'de>,
225 {
226 #[serde_as]
227 #[derive(Clone, Serialize, Deserialize, Debug)]
228 struct ConstraintSystemSerde<F: PrimeField> {
229 public: usize,
230 prev_challenges: usize,
231 #[serde(bound = "EvaluationDomains<F>: Serialize + DeserializeOwned")]
232 domain: EvaluationDomains<F>,
233 #[serde(bound = "CircuitGate<F>: Serialize + DeserializeOwned")]
234 gates: Arc<Vec<CircuitGate<F>>>,
235 zk_rows: u64,
236 feature_flags: FeatureFlags,
237 lazy_mode: bool,
238 #[serde_as(as = "Vec<o1_utils::serialization::SerdeAs>")]
239 sid: Vec<F>,
240 #[serde_as(as = "[o1_utils::serialization::SerdeAs; PERMUTS]")]
241 shift: [F; PERMUTS],
242 #[serde_as(as = "o1_utils::serialization::SerdeAs")]
243 endo: F,
244 #[serde(bound = "LookupConstraintSystem<F>: Serialize + DeserializeOwned")]
245 lookup_constraint_system: Arc<LookupConstraintSystemCache<F>>,
246 disable_gates_checks: bool,
247 }
248
249 let cs = ConstraintSystemSerde::<F>::deserialize(deserializer)?;
251
252 let precomputations = Arc::new({
253 LazyCache::new(move || {
254 Arc::new(DomainConstantEvaluations::create(cs.domain, cs.zk_rows).unwrap())
255 })
256 });
257
258 Ok(ConstraintSystem {
259 public: cs.public,
260 prev_challenges: cs.prev_challenges,
261 domain: cs.domain,
262 gates: cs.gates,
263 zk_rows: cs.zk_rows,
264 feature_flags: cs.feature_flags,
265 sid: cs.sid,
266 shift: cs.shift,
267 endo: cs.endo,
268 lookup_constraint_system: cs.lookup_constraint_system,
269 disable_gates_checks: cs.disable_gates_checks,
270 precomputations,
271 })
272 }
273}
274
275#[derive(Debug)]
277pub enum GateError {
278 DisconnectedWires(Wire, Wire),
280 IncorrectPublic(usize),
282 Custom { row: usize, err: String },
284}
285
286pub struct Builder<F: PrimeField> {
287 gates: Vec<CircuitGate<F>>,
288 public: usize,
289 prev_challenges: usize,
290 lookup_tables: Vec<LookupTable<F>>,
291 runtime_tables: Option<Vec<RuntimeTableCfg<F>>>,
292 precomputations: Option<Arc<DomainConstantEvaluations<F>>>,
293 disable_gates_checks: bool,
294 max_poly_size: Option<usize>,
295 lazy_mode: bool,
296}
297
298pub fn selector_polynomial<F: PrimeField>(
300 gate_type: GateType,
301 gates: &[CircuitGate<F>],
302 domain: &EvaluationDomains<F>,
303 target_domain: &D<F>,
304 disable_gates_checks: bool,
305) -> E<F, D<F>> {
306 if cfg!(debug_assertions) && disable_gates_checks {
307 DP::<F>::zero().evaluate_over_domain_by_ref(*target_domain)
308 } else {
309 let coeff = E::<F, D<F>>::from_vec_and_domain(
311 gates
312 .iter()
313 .map(|gate| {
314 if gate.typ == gate_type {
315 F::one()
316 } else {
317 F::zero()
318 }
319 })
320 .collect(),
321 domain.d1,
322 )
323 .interpolate();
324
325 coeff.evaluate_over_domain_by_ref(*target_domain)
326 }
327}
328
329impl<F: PrimeField> ConstraintSystem<F> {
330 pub fn create(gates: Vec<CircuitGate<F>>) -> Builder<F> {
346 Builder {
347 gates,
348 public: 0,
349 prev_challenges: 0,
350 lookup_tables: vec![],
351 runtime_tables: None,
352 precomputations: None,
353 disable_gates_checks: false,
354 max_poly_size: None,
355 lazy_mode: false,
356 }
357 }
358
359 pub fn precomputations(&self) -> Arc<DomainConstantEvaluations<F>> {
360 self.precomputations.get().clone()
361 }
362
363 pub fn for_testing(gates: Vec<CircuitGate<F>>) -> Self {
365 let public = 0;
366 ConstraintSystem::<F>::create(gates)
368 .public(public)
369 .build()
370 .unwrap()
371 }
372
373 pub fn fp_for_testing(gates: Vec<CircuitGate<F>>) -> Self {
374 Self::for_testing(gates)
375 }
376}
377
378impl<F: PrimeField, G: KimchiCurve<ScalarField = F>, OpeningProof: OpenProof<G>>
379 ProverIndex<G, OpeningProof>
380{
381 pub fn verify(&self, witness: &[Vec<F>; COLUMNS], public: &[F]) -> Result<(), GateError> {
386 let pad = vec![F::zero(); self.cs.domain.d1.size() - witness[0].len()];
388 let witness: [Vec<F>; COLUMNS] = array::from_fn(|i| {
389 let mut w = witness[i].to_vec();
390 w.extend_from_slice(&pad);
391 w
392 });
393
394 for (row, gate) in self.cs.gates.iter().enumerate() {
396 for col in 0..PERMUTS {
398 let wire = gate.wires[col];
399
400 if wire.col >= PERMUTS {
401 return Err(GateError::Custom {
402 row,
403 err: format!("a wire can only be connected to the first {PERMUTS} columns"),
404 });
405 }
406
407 if witness[col][row] != witness[wire.col][wire.row] {
408 return Err(GateError::DisconnectedWires(
409 Wire { col, row },
410 Wire {
411 col: wire.col,
412 row: wire.row,
413 },
414 ));
415 }
416 }
417
418 if row < self.cs.public && gate.coeffs.first() != Some(&F::one()) {
420 return Err(GateError::IncorrectPublic(row));
421 }
422
423 gate.verify(row, &witness, self, public)
425 .map_err(|err| GateError::Custom { row, err })?;
426 }
427
428 Ok(())
430 }
431}
432
433impl<F: PrimeField> ConstraintSystem<F> {
434 pub fn evaluate(&self, w: &[DP<F>; COLUMNS], z: &DP<F>) -> WitnessOverDomains<F> {
436 let (w8, z8): ([E<F, D<F>>; COLUMNS], _) = {
438 let mut res = w
439 .par_iter()
440 .chain(rayon::iter::once(z))
441 .map(|elem| elem.evaluate_over_domain_by_ref(self.domain.d8))
442 .collect::<Vec<_>>();
443 let z8 = res[COLUMNS].clone();
444 res.truncate(COLUMNS);
445 (res.try_into().unwrap(), z8)
446 };
447
448 let w4: [E<F, D<F>>; COLUMNS] = (0..COLUMNS)
449 .into_par_iter()
450 .map(|i| {
451 E::<F, D<F>>::from_vec_and_domain(
452 (0..self.domain.d4.size)
453 .map(|j| w8[i].evals[2 * j as usize])
454 .collect(),
455 self.domain.d4,
456 )
457 })
458 .collect::<Vec<_>>()
459 .try_into()
460 .unwrap();
461
462 let z4 = DP::<F>::zero().evaluate_over_domain_by_ref(D::<F>::new(1).unwrap());
463 let z8_shift8 = z8.shift(8);
464
465 let d4_next_w: [_; COLUMNS] = w4
466 .par_iter()
467 .map(|w4_i| w4_i.shift(4))
468 .collect::<Vec<_>>()
469 .try_into()
470 .unwrap();
471
472 let d8_next_w: [_; COLUMNS] = w8
473 .par_iter()
474 .map(|w8_i| w8_i.shift(8))
475 .collect::<Vec<_>>()
476 .try_into()
477 .unwrap();
478
479 WitnessOverDomains {
480 d4: WitnessShifts {
481 next: WitnessEvals {
482 w: d4_next_w,
483 z: z4.clone(), },
486 this: WitnessEvals {
487 w: w4,
488 z: z4, },
490 },
491 d8: WitnessShifts {
492 next: WitnessEvals {
493 w: d8_next_w,
494 z: z8_shift8,
495 },
496 this: WitnessEvals { w: w8, z: z8 },
497 },
498 }
499 }
500
501 pub(crate) fn evaluated_column_coefficients(&self) -> EvaluatedColumnCoefficients<F> {
502 let shifts = Shifts::new(&self.domain.d1);
504
505 let n = self.domain.d1.size();
506
507 let mut sigmal1: [Vec<F>; PERMUTS] = array::from_fn(|_| vec![F::zero(); n]);
508
509 for (row, gate) in self.gates.iter().enumerate() {
510 for (cell, sigma) in gate.wires.iter().zip(sigmal1.iter_mut()) {
511 sigma[row] = shifts.cell_to_field(cell);
512 }
513 }
514
515 for row in n + 2 - (self.zk_rows as usize)..n - 1 {
518 for sigma in sigmal1.iter_mut() {
519 sigma[row] = F::zero();
520 }
521 }
522
523 let sigmal1: [_; PERMUTS] = {
524 let [s0, s1, s2, s3, s4, s5, s6] = sigmal1;
525 [
526 E::<F, D<F>>::from_vec_and_domain(s0, self.domain.d1),
527 E::<F, D<F>>::from_vec_and_domain(s1, self.domain.d1),
528 E::<F, D<F>>::from_vec_and_domain(s2, self.domain.d1),
529 E::<F, D<F>>::from_vec_and_domain(s3, self.domain.d1),
530 E::<F, D<F>>::from_vec_and_domain(s4, self.domain.d1),
531 E::<F, D<F>>::from_vec_and_domain(s5, self.domain.d1),
532 E::<F, D<F>>::from_vec_and_domain(s6, self.domain.d1),
533 ]
534 };
535
536 let permutation_coefficients: [DP<F>; PERMUTS] =
537 array::from_fn(|i| sigmal1[i].clone().interpolate());
538
539 let poseidon_selector = E::<F, D<F>>::from_vec_and_domain(
541 self.gates.iter().map(|gate| gate.ps()).collect(),
542 self.domain.d1,
543 )
544 .interpolate();
545
546 let generic_selector = E::<F, D<F>>::from_vec_and_domain(
548 self.gates
549 .iter()
550 .map(|gate| {
551 if matches!(gate.typ, GateType::Generic) {
552 F::one()
553 } else {
554 F::zero()
555 }
556 })
557 .collect(),
558 self.domain.d1,
559 )
560 .interpolate();
561
562 let coefficients: [_; COLUMNS] = array::from_fn(|i| {
564 let padded = self
565 .gates
566 .iter()
567 .map(|gate| gate.coeffs.get(i).cloned().unwrap_or_else(F::zero))
568 .collect();
569 let eval = E::from_vec_and_domain(padded, self.domain.d1);
570 eval.interpolate()
571 });
572
573 EvaluatedColumnCoefficients {
574 permutation_coefficients,
575 coefficients,
576 generic_selector,
577 poseidon_selector,
578 }
579 }
580
581 pub(crate) fn column_evaluations(
582 &self,
583 evaluated_column_coefficients: &EvaluatedColumnCoefficients<F>,
584 ) -> ColumnEvaluations<F> {
585 let permutation_coefficients8 = array::from_fn(|i| {
586 evaluated_column_coefficients.permutation_coefficients[i]
587 .evaluate_over_domain_by_ref(self.domain.d8)
588 });
589
590 let poseidon_selector8 = evaluated_column_coefficients
591 .poseidon_selector
592 .evaluate_over_domain_by_ref(self.domain.d8);
593
594 let complete_add_selector4 = selector_polynomial(
596 GateType::CompleteAdd,
597 &self.gates,
598 &self.domain,
599 &self.domain.d4,
600 self.disable_gates_checks,
601 );
602
603 let mul_selector8 = selector_polynomial(
604 GateType::VarBaseMul,
605 &self.gates,
606 &self.domain,
607 &self.domain.d8,
608 self.disable_gates_checks,
609 );
610
611 let emul_selector8 = selector_polynomial(
612 GateType::EndoMul,
613 &self.gates,
614 &self.domain,
615 &self.domain.d8,
616 self.disable_gates_checks,
617 );
618
619 let endomul_scalar_selector8 = selector_polynomial(
620 GateType::EndoMulScalar,
621 &self.gates,
622 &self.domain,
623 &self.domain.d8,
624 self.disable_gates_checks,
625 );
626
627 let generic_selector4 = evaluated_column_coefficients
628 .generic_selector
629 .evaluate_over_domain_by_ref(self.domain.d4);
630
631 let range_check0_selector8 = {
633 if !self.feature_flags.range_check0 {
634 None
635 } else {
636 Some(selector_polynomial(
637 GateType::RangeCheck0,
638 &self.gates,
639 &self.domain,
640 &self.domain.d8,
641 self.disable_gates_checks,
642 ))
643 }
644 };
645
646 let range_check1_selector8 = {
648 if !self.feature_flags.range_check1 {
649 None
650 } else {
651 Some(selector_polynomial(
652 GateType::RangeCheck1,
653 &self.gates,
654 &self.domain,
655 &self.domain.d8,
656 self.disable_gates_checks,
657 ))
658 }
659 };
660
661 let foreign_field_add_selector8 = {
663 if !self.feature_flags.foreign_field_add {
664 None
665 } else {
666 Some(selector_polynomial(
667 GateType::ForeignFieldAdd,
668 &self.gates,
669 &self.domain,
670 &self.domain.d8,
671 self.disable_gates_checks,
672 ))
673 }
674 };
675
676 let foreign_field_mul_selector8 = {
678 if !self.feature_flags.foreign_field_mul {
679 None
680 } else {
681 Some(selector_polynomial(
682 GateType::ForeignFieldMul,
683 &self.gates,
684 &self.domain,
685 &self.domain.d8,
686 self.disable_gates_checks,
687 ))
688 }
689 };
690
691 let xor_selector8 = {
692 if !self.feature_flags.xor {
693 None
694 } else {
695 Some(selector_polynomial(
696 GateType::Xor16,
697 &self.gates,
698 &self.domain,
699 &self.domain.d8,
700 self.disable_gates_checks,
701 ))
702 }
703 };
704
705 let rot_selector8 = {
706 if !self.feature_flags.rot {
707 None
708 } else {
709 Some(selector_polynomial(
710 GateType::Rot64,
711 &self.gates,
712 &self.domain,
713 &self.domain.d8,
714 self.disable_gates_checks,
715 ))
716 }
717 };
718
719 let coefficients8 = array::from_fn(|i| {
721 evaluated_column_coefficients.coefficients[i]
722 .evaluate_over_domain_by_ref(self.domain.d8)
723 });
724
725 ColumnEvaluations {
726 permutation_coefficients8,
727 coefficients8,
728 generic_selector4,
729 poseidon_selector8,
730 complete_add_selector4,
731 mul_selector8,
732 emul_selector8,
733 endomul_scalar_selector8,
734 range_check0_selector8,
735 range_check1_selector8,
736 foreign_field_add_selector8,
737 foreign_field_mul_selector8,
738 xor_selector8,
739 rot_selector8,
740 }
741 }
742}
743
744pub const NUM_CHUNKS_BY_DEFAULT: usize = 1;
746
747pub const ZK_ROWS_BY_DEFAULT: u64 = 3;
749
750pub fn zk_rows_strict_lower_bound(num_chunks: usize) -> usize {
761 (2 * (PERMUTS + 1) * num_chunks - 2) / PERMUTS
762}
763
764impl FeatureFlags {
765 pub fn from_gates_and_lookup_features<F: PrimeField>(
766 gates: &[CircuitGate<F>],
767 lookup_features: LookupFeatures,
768 ) -> FeatureFlags {
769 let mut feature_flags = FeatureFlags {
770 range_check0: false,
771 range_check1: false,
772 lookup_features,
773 foreign_field_add: false,
774 foreign_field_mul: false,
775 xor: false,
776 rot: false,
777 };
778
779 for gate in gates {
780 match gate.typ {
781 GateType::RangeCheck0 => feature_flags.range_check0 = true,
782 GateType::RangeCheck1 => feature_flags.range_check1 = true,
783 GateType::ForeignFieldAdd => feature_flags.foreign_field_add = true,
784 GateType::ForeignFieldMul => feature_flags.foreign_field_mul = true,
785 GateType::Xor16 => feature_flags.xor = true,
786 GateType::Rot64 => feature_flags.rot = true,
787 _ => (),
788 }
789 }
790
791 feature_flags
792 }
793
794 pub fn from_gates<F: PrimeField>(
795 gates: &[CircuitGate<F>],
796 uses_runtime_tables: bool,
797 ) -> FeatureFlags {
798 FeatureFlags::from_gates_and_lookup_features(
799 gates,
800 LookupFeatures::from_gates(gates, uses_runtime_tables),
801 )
802 }
803}
804
805impl<F: PrimeField> Builder<F> {
806 pub fn public(mut self, public: usize) -> Self {
809 self.public = public;
810 self
811 }
812
813 pub fn prev_challenges(mut self, prev_challenges: usize) -> Self {
816 self.prev_challenges = prev_challenges;
817 self
818 }
819
820 pub fn lookup(mut self, lookup_tables: Vec<LookupTable<F>>) -> Self {
829 self.lookup_tables = lookup_tables;
830 self
831 }
832
833 pub fn runtime(mut self, runtime_tables: Option<Vec<RuntimeTableCfg<F>>>) -> Self {
841 self.runtime_tables = runtime_tables;
842 self
843 }
844
845 pub fn shared_precomputations(
848 mut self,
849 shared_precomputations: Arc<DomainConstantEvaluations<F>>,
850 ) -> Self {
851 self.precomputations = Some(shared_precomputations);
852 self
853 }
854
855 pub fn disable_gates_checks(mut self, disable_gates_checks: bool) -> Self {
857 self.disable_gates_checks = disable_gates_checks;
858 self
859 }
860
861 pub fn max_poly_size(mut self, max_poly_size: Option<usize>) -> Self {
862 self.max_poly_size = max_poly_size;
863 self
864 }
865
866 pub fn lazy_mode(mut self, lazy_mode: bool) -> Self {
867 self.lazy_mode = lazy_mode;
868 self
869 }
870
871 pub fn build(self) -> Result<ConstraintSystem<F>, SetupError> {
873 let mut gates = self.gates;
874 let lookup_tables = self.lookup_tables.clone();
875 let runtime_tables = self.runtime_tables.clone();
876
877 assert!(gates.len() > 1);
880
881 let feature_flags = FeatureFlags::from_gates(&gates, runtime_tables.is_some());
882
883 let lookup_domain_size = {
884 let mut has_table_with_id_0 = false;
886 let mut lookup_domain_size: usize = lookup_tables
887 .iter()
888 .map(|LookupTable { id, data }| {
889 if *id == 0_i32 {
891 has_table_with_id_0 = true
892 }
893 if data.is_empty() {
894 0
895 } else {
896 data[0].len()
897 }
898 })
899 .sum();
900 if let Some(runtime_tables) = &runtime_tables {
902 for runtime_table in runtime_tables.iter() {
905 lookup_domain_size += runtime_table.len();
906 }
907 }
908 let LookupFeatures { patterns, .. } = &feature_flags.lookup_features;
910 let mut gate_lookup_tables = GateLookupTables {
911 xor: false,
912 range_check: false,
913 };
914 for pattern in patterns.into_iter() {
915 if let Some(gate_table) = pattern.table() {
916 gate_lookup_tables[gate_table] = true
917 }
918 }
919 for gate_table in gate_lookup_tables.into_iter() {
920 lookup_domain_size += gate_table.table_size();
921 }
922
923 if has_table_with_id_0 {
926 lookup_domain_size
927 } else {
928 lookup_domain_size + 1
929 }
930 };
931
932 let (zk_rows, domain_size_lower_bound) = {
956 let circuit_lower_bound = core::cmp::max(gates.len(), lookup_domain_size + 1);
960 let get_domain_size_lower_bound = |zk_rows: u64| circuit_lower_bound + zk_rows as usize;
961
962 let mut zk_rows = 3;
963 let mut domain_size_lower_bound = get_domain_size_lower_bound(zk_rows);
964 if let Some(max_poly_size) = self.max_poly_size {
965 while {
971 let domain_size = D::<F>::compute_size_of_domain(domain_size_lower_bound)
972 .ok_or(SetupError::DomainCreation(
973 DomainCreationError::DomainSizeFailed(domain_size_lower_bound),
974 ))?;
975 let num_chunks = if domain_size < max_poly_size {
976 1
977 } else {
978 domain_size / max_poly_size
979 };
980 zk_rows = (zk_rows_strict_lower_bound(num_chunks) + 1) as u64;
981 domain_size_lower_bound = get_domain_size_lower_bound(zk_rows);
982 domain_size < domain_size_lower_bound
983 } {}
984 }
985 (zk_rows, domain_size_lower_bound)
986 };
987
988 let domain = EvaluationDomains::<F>::create(domain_size_lower_bound)
992 .map_err(SetupError::DomainCreation)?;
993
994 assert!(domain.d1.size > zk_rows);
995
996 let d1_size = domain.d1.size();
998 let mut padding = (gates.len()..d1_size)
999 .map(|i| {
1000 CircuitGate::<F>::zero(array::from_fn(|j| Wire {
1001 col: WIRES[j],
1002 row: i,
1003 }))
1004 })
1005 .collect();
1006 gates.append(&mut padding);
1007
1008 let shifts = Shifts::new(&domain.d1);
1010
1011 let gates = Arc::new(gates);
1015 let gates_clone = Arc::clone(&gates);
1016 let lookup_constraint_system = LazyCache::new(move || {
1017 LookupConstraintSystem::create(
1018 &gates_clone,
1019 self.lookup_tables,
1020 self.runtime_tables,
1021 &domain,
1022 zk_rows as usize,
1023 )
1024 });
1025 if !self.lazy_mode {
1026 lookup_constraint_system
1028 .try_get_or_err()
1029 .map_err(SetupError::from)?;
1030 }
1031
1032 let sid = shifts.map[0].clone();
1033
1034 let endo = F::zero();
1036
1037 let precomputations = if !self.lazy_mode {
1038 match self.precomputations {
1039 Some(t) => LazyCache::preinit(t),
1040 None => LazyCache::preinit(Arc::new(
1041 DomainConstantEvaluations::create(domain, zk_rows).unwrap(),
1042 )),
1043 }
1044 } else {
1045 LazyCache::new(move || {
1046 Arc::new(DomainConstantEvaluations::create(domain, zk_rows).unwrap())
1047 })
1048 };
1049
1050 let constraints = ConstraintSystem {
1051 domain,
1052 public: self.public,
1053 prev_challenges: self.prev_challenges,
1054 sid,
1055 gates,
1056 shift: shifts.shifts,
1057 endo,
1058 zk_rows,
1059 lookup_constraint_system: Arc::new(lookup_constraint_system),
1061 feature_flags,
1062 precomputations: Arc::new(precomputations),
1063 disable_gates_checks: self.disable_gates_checks,
1064 };
1065
1066 Ok(constraints)
1067 }
1068}