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 #[serde_as(as = "Vec<o1_utils::serialization::SerdeAs>")]
238 sid: Vec<F>,
239 #[serde_as(as = "[o1_utils::serialization::SerdeAs; PERMUTS]")]
240 shift: [F; PERMUTS],
241 #[serde_as(as = "o1_utils::serialization::SerdeAs")]
242 endo: F,
243 #[serde(bound = "LookupConstraintSystem<F>: Serialize + DeserializeOwned")]
244 lookup_constraint_system: Arc<LookupConstraintSystemCache<F>>,
245 disable_gates_checks: bool,
246 }
247
248 let cs = ConstraintSystemSerde::<F>::deserialize(deserializer)?;
250
251 let precomputations = Arc::new({
252 LazyCache::new(move || {
253 Arc::new(DomainConstantEvaluations::create(cs.domain, cs.zk_rows).unwrap())
254 })
255 });
256
257 Ok(ConstraintSystem {
258 public: cs.public,
259 prev_challenges: cs.prev_challenges,
260 domain: cs.domain,
261 gates: cs.gates,
262 zk_rows: cs.zk_rows,
263 feature_flags: cs.feature_flags,
264 sid: cs.sid,
265 shift: cs.shift,
266 endo: cs.endo,
267 lookup_constraint_system: cs.lookup_constraint_system,
268 disable_gates_checks: cs.disable_gates_checks,
269 precomputations,
270 })
271 }
272}
273
274#[derive(Debug)]
276pub enum GateError {
277 DisconnectedWires(Wire, Wire),
279 IncorrectPublic(usize),
281 Custom { row: usize, err: String },
283}
284
285pub struct Builder<F: PrimeField> {
286 gates: Vec<CircuitGate<F>>,
287 public: usize,
288 prev_challenges: usize,
289 lookup_tables: Vec<LookupTable<F>>,
290 runtime_tables: Option<Vec<RuntimeTableCfg<F>>>,
291 precomputations: Option<Arc<DomainConstantEvaluations<F>>>,
292 disable_gates_checks: bool,
293 max_poly_size: Option<usize>,
294 lazy_mode: bool,
295}
296
297pub fn selector_polynomial<F: PrimeField>(
299 gate_type: GateType,
300 gates: &[CircuitGate<F>],
301 domain: &EvaluationDomains<F>,
302 target_domain: &D<F>,
303 disable_gates_checks: bool,
304) -> E<F, D<F>> {
305 if cfg!(debug_assertions) && disable_gates_checks {
306 DP::<F>::zero().evaluate_over_domain_by_ref(*target_domain)
307 } else {
308 let coeff = E::<F, D<F>>::from_vec_and_domain(
310 gates
311 .iter()
312 .map(|gate| {
313 if gate.typ == gate_type {
314 F::one()
315 } else {
316 F::zero()
317 }
318 })
319 .collect(),
320 domain.d1,
321 )
322 .interpolate();
323
324 coeff.evaluate_over_domain_by_ref(*target_domain)
325 }
326}
327
328impl<F: PrimeField> ConstraintSystem<F> {
329 pub fn create(gates: Vec<CircuitGate<F>>) -> Builder<F> {
345 Builder {
346 gates,
347 public: 0,
348 prev_challenges: 0,
349 lookup_tables: vec![],
350 runtime_tables: None,
351 precomputations: None,
352 disable_gates_checks: false,
353 max_poly_size: None,
354 lazy_mode: false,
355 }
356 }
357
358 pub fn precomputations(&self) -> Arc<DomainConstantEvaluations<F>> {
359 self.precomputations.get().clone()
360 }
361
362 pub fn for_testing(gates: Vec<CircuitGate<F>>) -> Self {
364 let public = 0;
365 ConstraintSystem::<F>::create(gates)
367 .public(public)
368 .build()
369 .unwrap()
370 }
371
372 pub fn fp_for_testing(gates: Vec<CircuitGate<F>>) -> Self {
373 Self::for_testing(gates)
374 }
375}
376
377impl<F: PrimeField, G: KimchiCurve<ScalarField = F>, OpeningProof: OpenProof<G>>
378 ProverIndex<G, OpeningProof>
379{
380 pub fn verify(&self, witness: &[Vec<F>; COLUMNS], public: &[F]) -> Result<(), GateError> {
385 let pad = vec![F::zero(); self.cs.domain.d1.size() - witness[0].len()];
387 let witness: [Vec<F>; COLUMNS] = array::from_fn(|i| {
388 let mut w = witness[i].to_vec();
389 w.extend_from_slice(&pad);
390 w
391 });
392
393 for (row, gate) in self.cs.gates.iter().enumerate() {
395 for col in 0..PERMUTS {
397 let wire = gate.wires[col];
398
399 if wire.col >= PERMUTS {
400 return Err(GateError::Custom {
401 row,
402 err: format!("a wire can only be connected to the first {PERMUTS} columns"),
403 });
404 }
405
406 if witness[col][row] != witness[wire.col][wire.row] {
407 return Err(GateError::DisconnectedWires(
408 Wire { col, row },
409 Wire {
410 col: wire.col,
411 row: wire.row,
412 },
413 ));
414 }
415 }
416
417 if row < self.cs.public && gate.coeffs.first() != Some(&F::one()) {
419 return Err(GateError::IncorrectPublic(row));
420 }
421
422 gate.verify(row, &witness, self, public)
424 .map_err(|err| GateError::Custom { row, err })?;
425 }
426
427 Ok(())
429 }
430}
431
432impl<F: PrimeField> ConstraintSystem<F> {
433 pub fn evaluate(&self, w: &[DP<F>; COLUMNS], z: &DP<F>) -> WitnessOverDomains<F> {
435 let (w8, z8): ([E<F, D<F>>; COLUMNS], _) = {
437 let mut res = w
438 .par_iter()
439 .chain(rayon::iter::once(z))
440 .map(|elem| elem.evaluate_over_domain_by_ref(self.domain.d8))
441 .collect::<Vec<_>>();
442 let z8 = res[COLUMNS].clone();
443 res.truncate(COLUMNS);
444 (res.try_into().unwrap(), z8)
445 };
446
447 let w4: [E<F, D<F>>; COLUMNS] = (0..COLUMNS)
448 .into_par_iter()
449 .map(|i| {
450 E::<F, D<F>>::from_vec_and_domain(
451 (0..self.domain.d4.size)
452 .map(|j| w8[i].evals[2 * j as usize])
453 .collect(),
454 self.domain.d4,
455 )
456 })
457 .collect::<Vec<_>>()
458 .try_into()
459 .unwrap();
460
461 let z4 = DP::<F>::zero().evaluate_over_domain_by_ref(D::<F>::new(1).unwrap());
462 let z8_shift8 = z8.shift(8);
463
464 let d4_next_w: [_; COLUMNS] = w4
465 .par_iter()
466 .map(|w4_i| w4_i.shift(4))
467 .collect::<Vec<_>>()
468 .try_into()
469 .unwrap();
470
471 let d8_next_w: [_; COLUMNS] = w8
472 .par_iter()
473 .map(|w8_i| w8_i.shift(8))
474 .collect::<Vec<_>>()
475 .try_into()
476 .unwrap();
477
478 WitnessOverDomains {
479 d4: WitnessShifts {
480 next: WitnessEvals {
481 w: d4_next_w,
482 z: z4.clone(), },
485 this: WitnessEvals {
486 w: w4,
487 z: z4, },
489 },
490 d8: WitnessShifts {
491 next: WitnessEvals {
492 w: d8_next_w,
493 z: z8_shift8,
494 },
495 this: WitnessEvals { w: w8, z: z8 },
496 },
497 }
498 }
499
500 pub(crate) fn evaluated_column_coefficients(&self) -> EvaluatedColumnCoefficients<F> {
501 let shifts = Shifts::new(&self.domain.d1);
503
504 let n = self.domain.d1.size();
505
506 let mut sigmal1: [Vec<F>; PERMUTS] = array::from_fn(|_| vec![F::zero(); n]);
507
508 for (row, gate) in self.gates.iter().enumerate() {
509 for (cell, sigma) in gate.wires.iter().zip(sigmal1.iter_mut()) {
510 sigma[row] = shifts.cell_to_field(cell);
511 }
512 }
513
514 for row in n + 2 - (self.zk_rows as usize)..n - 1 {
517 for sigma in sigmal1.iter_mut() {
518 sigma[row] = F::zero();
519 }
520 }
521
522 let sigmal1: [_; PERMUTS] = {
523 let [s0, s1, s2, s3, s4, s5, s6] = sigmal1;
524 [
525 E::<F, D<F>>::from_vec_and_domain(s0, self.domain.d1),
526 E::<F, D<F>>::from_vec_and_domain(s1, self.domain.d1),
527 E::<F, D<F>>::from_vec_and_domain(s2, self.domain.d1),
528 E::<F, D<F>>::from_vec_and_domain(s3, self.domain.d1),
529 E::<F, D<F>>::from_vec_and_domain(s4, self.domain.d1),
530 E::<F, D<F>>::from_vec_and_domain(s5, self.domain.d1),
531 E::<F, D<F>>::from_vec_and_domain(s6, self.domain.d1),
532 ]
533 };
534
535 let permutation_coefficients: [DP<F>; PERMUTS] =
536 array::from_fn(|i| sigmal1[i].clone().interpolate());
537
538 let poseidon_selector = E::<F, D<F>>::from_vec_and_domain(
540 self.gates.iter().map(|gate| gate.ps()).collect(),
541 self.domain.d1,
542 )
543 .interpolate();
544
545 let generic_selector = E::<F, D<F>>::from_vec_and_domain(
547 self.gates
548 .iter()
549 .map(|gate| {
550 if matches!(gate.typ, GateType::Generic) {
551 F::one()
552 } else {
553 F::zero()
554 }
555 })
556 .collect(),
557 self.domain.d1,
558 )
559 .interpolate();
560
561 let coefficients: [_; COLUMNS] = array::from_fn(|i| {
563 let padded = self
564 .gates
565 .iter()
566 .map(|gate| gate.coeffs.get(i).cloned().unwrap_or_else(F::zero))
567 .collect();
568 let eval = E::from_vec_and_domain(padded, self.domain.d1);
569 eval.interpolate()
570 });
571
572 EvaluatedColumnCoefficients {
573 permutation_coefficients,
574 coefficients,
575 generic_selector,
576 poseidon_selector,
577 }
578 }
579
580 pub(crate) fn column_evaluations(
581 &self,
582 evaluated_column_coefficients: &EvaluatedColumnCoefficients<F>,
583 ) -> ColumnEvaluations<F> {
584 let permutation_coefficients8 = array::from_fn(|i| {
585 evaluated_column_coefficients.permutation_coefficients[i]
586 .evaluate_over_domain_by_ref(self.domain.d8)
587 });
588
589 let poseidon_selector8 = evaluated_column_coefficients
590 .poseidon_selector
591 .evaluate_over_domain_by_ref(self.domain.d8);
592
593 let complete_add_selector4 = selector_polynomial(
595 GateType::CompleteAdd,
596 &self.gates,
597 &self.domain,
598 &self.domain.d4,
599 self.disable_gates_checks,
600 );
601
602 let mul_selector8 = selector_polynomial(
603 GateType::VarBaseMul,
604 &self.gates,
605 &self.domain,
606 &self.domain.d8,
607 self.disable_gates_checks,
608 );
609
610 let emul_selector8 = selector_polynomial(
611 GateType::EndoMul,
612 &self.gates,
613 &self.domain,
614 &self.domain.d8,
615 self.disable_gates_checks,
616 );
617
618 let endomul_scalar_selector8 = selector_polynomial(
619 GateType::EndoMulScalar,
620 &self.gates,
621 &self.domain,
622 &self.domain.d8,
623 self.disable_gates_checks,
624 );
625
626 let generic_selector4 = evaluated_column_coefficients
627 .generic_selector
628 .evaluate_over_domain_by_ref(self.domain.d4);
629
630 let range_check0_selector8 = {
632 if !self.feature_flags.range_check0 {
633 None
634 } else {
635 Some(selector_polynomial(
636 GateType::RangeCheck0,
637 &self.gates,
638 &self.domain,
639 &self.domain.d8,
640 self.disable_gates_checks,
641 ))
642 }
643 };
644
645 let range_check1_selector8 = {
647 if !self.feature_flags.range_check1 {
648 None
649 } else {
650 Some(selector_polynomial(
651 GateType::RangeCheck1,
652 &self.gates,
653 &self.domain,
654 &self.domain.d8,
655 self.disable_gates_checks,
656 ))
657 }
658 };
659
660 let foreign_field_add_selector8 = {
662 if !self.feature_flags.foreign_field_add {
663 None
664 } else {
665 Some(selector_polynomial(
666 GateType::ForeignFieldAdd,
667 &self.gates,
668 &self.domain,
669 &self.domain.d8,
670 self.disable_gates_checks,
671 ))
672 }
673 };
674
675 let foreign_field_mul_selector8 = {
677 if !self.feature_flags.foreign_field_mul {
678 None
679 } else {
680 Some(selector_polynomial(
681 GateType::ForeignFieldMul,
682 &self.gates,
683 &self.domain,
684 &self.domain.d8,
685 self.disable_gates_checks,
686 ))
687 }
688 };
689
690 let xor_selector8 = {
691 if !self.feature_flags.xor {
692 None
693 } else {
694 Some(selector_polynomial(
695 GateType::Xor16,
696 &self.gates,
697 &self.domain,
698 &self.domain.d8,
699 self.disable_gates_checks,
700 ))
701 }
702 };
703
704 let rot_selector8 = {
705 if !self.feature_flags.rot {
706 None
707 } else {
708 Some(selector_polynomial(
709 GateType::Rot64,
710 &self.gates,
711 &self.domain,
712 &self.domain.d8,
713 self.disable_gates_checks,
714 ))
715 }
716 };
717
718 let coefficients8 = array::from_fn(|i| {
720 evaluated_column_coefficients.coefficients[i]
721 .evaluate_over_domain_by_ref(self.domain.d8)
722 });
723
724 ColumnEvaluations {
725 permutation_coefficients8,
726 coefficients8,
727 generic_selector4,
728 poseidon_selector8,
729 complete_add_selector4,
730 mul_selector8,
731 emul_selector8,
732 endomul_scalar_selector8,
733 range_check0_selector8,
734 range_check1_selector8,
735 foreign_field_add_selector8,
736 foreign_field_mul_selector8,
737 xor_selector8,
738 rot_selector8,
739 }
740 }
741}
742
743pub const NUM_CHUNKS_BY_DEFAULT: usize = 1;
745
746pub const ZK_ROWS_BY_DEFAULT: u64 = 3;
748
749pub fn zk_rows_strict_lower_bound(num_chunks: usize) -> usize {
760 (2 * (PERMUTS + 1) * num_chunks - 2) / PERMUTS
761}
762
763impl FeatureFlags {
764 pub fn from_gates_and_lookup_features<F: PrimeField>(
765 gates: &[CircuitGate<F>],
766 lookup_features: LookupFeatures,
767 ) -> FeatureFlags {
768 let mut feature_flags = FeatureFlags {
769 range_check0: false,
770 range_check1: false,
771 lookup_features,
772 foreign_field_add: false,
773 foreign_field_mul: false,
774 xor: false,
775 rot: false,
776 };
777
778 for gate in gates {
779 match gate.typ {
780 GateType::RangeCheck0 => feature_flags.range_check0 = true,
781 GateType::RangeCheck1 => feature_flags.range_check1 = true,
782 GateType::ForeignFieldAdd => feature_flags.foreign_field_add = true,
783 GateType::ForeignFieldMul => feature_flags.foreign_field_mul = true,
784 GateType::Xor16 => feature_flags.xor = true,
785 GateType::Rot64 => feature_flags.rot = true,
786 _ => (),
787 }
788 }
789
790 feature_flags
791 }
792
793 pub fn from_gates<F: PrimeField>(
794 gates: &[CircuitGate<F>],
795 uses_runtime_tables: bool,
796 ) -> FeatureFlags {
797 FeatureFlags::from_gates_and_lookup_features(
798 gates,
799 LookupFeatures::from_gates(gates, uses_runtime_tables),
800 )
801 }
802}
803
804impl<F: PrimeField> Builder<F> {
805 pub fn public(mut self, public: usize) -> Self {
808 self.public = public;
809 self
810 }
811
812 pub fn prev_challenges(mut self, prev_challenges: usize) -> Self {
815 self.prev_challenges = prev_challenges;
816 self
817 }
818
819 pub fn lookup(mut self, lookup_tables: Vec<LookupTable<F>>) -> Self {
828 self.lookup_tables = lookup_tables;
829 self
830 }
831
832 pub fn runtime(mut self, runtime_tables: Option<Vec<RuntimeTableCfg<F>>>) -> Self {
840 self.runtime_tables = runtime_tables;
841 self
842 }
843
844 pub fn shared_precomputations(
847 mut self,
848 shared_precomputations: Arc<DomainConstantEvaluations<F>>,
849 ) -> Self {
850 self.precomputations = Some(shared_precomputations);
851 self
852 }
853
854 pub fn disable_gates_checks(mut self, disable_gates_checks: bool) -> Self {
856 self.disable_gates_checks = disable_gates_checks;
857 self
858 }
859
860 pub fn max_poly_size(mut self, max_poly_size: Option<usize>) -> Self {
861 self.max_poly_size = max_poly_size;
862 self
863 }
864
865 pub fn lazy_mode(mut self, lazy_mode: bool) -> Self {
866 self.lazy_mode = lazy_mode;
867 self
868 }
869
870 pub fn build(self) -> Result<ConstraintSystem<F>, SetupError> {
872 let mut gates = self.gates;
873 let lookup_tables = self.lookup_tables.clone();
874 let runtime_tables = self.runtime_tables.clone();
875
876 assert!(gates.len() > 1);
879
880 let feature_flags = FeatureFlags::from_gates(&gates, runtime_tables.is_some());
881
882 let lookup_domain_size = {
883 let mut has_table_with_id_0 = false;
885 let mut lookup_domain_size: usize = lookup_tables
886 .iter()
887 .map(|LookupTable { id, data }| {
888 if *id == 0_i32 {
890 has_table_with_id_0 = true
891 }
892 if data.is_empty() {
893 0
894 } else {
895 data[0].len()
896 }
897 })
898 .sum();
899 if let Some(runtime_tables) = &runtime_tables {
901 for runtime_table in runtime_tables.iter() {
904 lookup_domain_size += runtime_table.len();
905 }
906 }
907 let LookupFeatures { patterns, .. } = &feature_flags.lookup_features;
909 let mut gate_lookup_tables = GateLookupTables {
910 xor: false,
911 range_check: false,
912 };
913 for pattern in patterns.into_iter() {
914 if let Some(gate_table) = pattern.table() {
915 gate_lookup_tables[gate_table] = true
916 }
917 }
918 for gate_table in gate_lookup_tables.into_iter() {
919 lookup_domain_size += gate_table.table_size();
920 }
921
922 if has_table_with_id_0 {
925 lookup_domain_size
926 } else {
927 lookup_domain_size + 1
928 }
929 };
930
931 let (zk_rows, domain_size_lower_bound) = {
955 let circuit_lower_bound = core::cmp::max(gates.len(), lookup_domain_size + 1);
959 let get_domain_size_lower_bound = |zk_rows: u64| circuit_lower_bound + zk_rows as usize;
960
961 let mut zk_rows = 3;
962 let mut domain_size_lower_bound = get_domain_size_lower_bound(zk_rows);
963 if let Some(max_poly_size) = self.max_poly_size {
964 while {
970 let domain_size = D::<F>::compute_size_of_domain(domain_size_lower_bound)
971 .ok_or(SetupError::DomainCreation(
972 DomainCreationError::DomainSizeFailed(domain_size_lower_bound),
973 ))?;
974 let num_chunks = if domain_size < max_poly_size {
975 1
976 } else {
977 domain_size / max_poly_size
978 };
979 zk_rows = (zk_rows_strict_lower_bound(num_chunks) + 1) as u64;
980 domain_size_lower_bound = get_domain_size_lower_bound(zk_rows);
981 domain_size < domain_size_lower_bound
982 } {}
983 }
984 (zk_rows, domain_size_lower_bound)
985 };
986
987 let domain = EvaluationDomains::<F>::create(domain_size_lower_bound)
991 .map_err(SetupError::DomainCreation)?;
992
993 assert!(domain.d1.size > zk_rows);
994
995 let d1_size = domain.d1.size();
997 let mut padding = (gates.len()..d1_size)
998 .map(|i| {
999 CircuitGate::<F>::zero(array::from_fn(|j| Wire {
1000 col: WIRES[j],
1001 row: i,
1002 }))
1003 })
1004 .collect();
1005 gates.append(&mut padding);
1006
1007 let shifts = Shifts::new(&domain.d1);
1009
1010 let gates = Arc::new(gates);
1014 let gates_clone = Arc::clone(&gates);
1015 let lookup_constraint_system = LazyCache::new(move || {
1016 LookupConstraintSystem::create(
1017 &gates_clone,
1018 self.lookup_tables,
1019 self.runtime_tables,
1020 &domain,
1021 zk_rows as usize,
1022 )
1023 });
1024 if !self.lazy_mode {
1025 lookup_constraint_system
1027 .try_get_or_err()
1028 .map_err(SetupError::from)?;
1029 }
1030
1031 let sid = shifts.map[0].clone();
1032
1033 let endo = F::zero();
1035
1036 let precomputations = if !self.lazy_mode {
1037 match self.precomputations {
1038 Some(t) => LazyCache::preinit(t),
1039 None => LazyCache::preinit(Arc::new(
1040 DomainConstantEvaluations::create(domain, zk_rows).unwrap(),
1041 )),
1042 }
1043 } else {
1044 LazyCache::new(move || {
1045 Arc::new(DomainConstantEvaluations::create(domain, zk_rows).unwrap())
1046 })
1047 };
1048
1049 let constraints = ConstraintSystem {
1050 domain,
1051 public: self.public,
1052 prev_challenges: self.prev_challenges,
1053 sid,
1054 gates,
1055 shift: shifts.shifts,
1056 endo,
1057 zk_rows,
1058 lookup_constraint_system: Arc::new(lookup_constraint_system),
1060 feature_flags,
1061 precomputations: Arc::new(precomputations),
1062 disable_gates_checks: self.disable_gates_checks,
1063 };
1064
1065 Ok(constraints)
1066 }
1067}