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::SRS;
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<const FULL_ROUNDS: usize, F, G, Srs> ProverIndex<FULL_ROUNDS, G, Srs>
378where
379 F: PrimeField,
380 G: KimchiCurve<FULL_ROUNDS, ScalarField = F>,
381 Srs: SRS<G>,
382{
383 pub fn verify(&self, witness: &[Vec<F>; COLUMNS], public: &[F]) -> Result<(), GateError> {
388 let pad = vec![F::zero(); self.cs.domain.d1.size() - witness[0].len()];
390 let witness: [Vec<F>; COLUMNS] = array::from_fn(|i| {
391 let mut w = witness[i].to_vec();
392 w.extend_from_slice(&pad);
393 w
394 });
395
396 for (row, gate) in self.cs.gates.iter().enumerate() {
398 for col in 0..PERMUTS {
400 let wire = gate.wires[col];
401
402 if wire.col >= PERMUTS {
403 return Err(GateError::Custom {
404 row,
405 err: format!("a wire can only be connected to the first {PERMUTS} columns"),
406 });
407 }
408
409 if witness[col][row] != witness[wire.col][wire.row] {
410 return Err(GateError::DisconnectedWires(
411 Wire { col, row },
412 Wire {
413 col: wire.col,
414 row: wire.row,
415 },
416 ));
417 }
418 }
419
420 if row < self.cs.public && gate.coeffs.first() != Some(&F::one()) {
422 return Err(GateError::IncorrectPublic(row));
423 }
424
425 gate.verify(row, &witness, self, public)
427 .map_err(|err| GateError::Custom { row, err })?;
428 }
429
430 Ok(())
432 }
433}
434
435impl<F: PrimeField> ConstraintSystem<F> {
436 pub fn evaluate(&self, w: &[DP<F>; COLUMNS], z: &DP<F>) -> WitnessOverDomains<F> {
438 let (w8, z8): ([E<F, D<F>>; COLUMNS], _) = {
440 let mut res = w
441 .par_iter()
442 .chain(rayon::iter::once(z))
443 .map(|elem| elem.evaluate_over_domain_by_ref(self.domain.d8))
444 .collect::<Vec<_>>();
445 let z8 = res[COLUMNS].clone();
446 res.truncate(COLUMNS);
447 (res.try_into().unwrap(), z8)
448 };
449
450 let w4: [E<F, D<F>>; COLUMNS] = (0..COLUMNS)
451 .into_par_iter()
452 .map(|i| {
453 E::<F, D<F>>::from_vec_and_domain(
454 (0..self.domain.d4.size)
455 .map(|j| w8[i].evals[2 * j as usize])
456 .collect(),
457 self.domain.d4,
458 )
459 })
460 .collect::<Vec<_>>()
461 .try_into()
462 .unwrap();
463
464 let z4 = DP::<F>::zero().evaluate_over_domain_by_ref(D::<F>::new(1).unwrap());
465 let z8_shift8 = z8.shift(8);
466
467 let d4_next_w: [_; COLUMNS] = w4
468 .par_iter()
469 .map(|w4_i| w4_i.shift(4))
470 .collect::<Vec<_>>()
471 .try_into()
472 .unwrap();
473
474 let d8_next_w: [_; COLUMNS] = w8
475 .par_iter()
476 .map(|w8_i| w8_i.shift(8))
477 .collect::<Vec<_>>()
478 .try_into()
479 .unwrap();
480
481 WitnessOverDomains {
482 d4: WitnessShifts {
483 next: WitnessEvals {
484 w: d4_next_w,
485 z: z4.clone(), },
488 this: WitnessEvals {
489 w: w4,
490 z: z4, },
492 },
493 d8: WitnessShifts {
494 next: WitnessEvals {
495 w: d8_next_w,
496 z: z8_shift8,
497 },
498 this: WitnessEvals { w: w8, z: z8 },
499 },
500 }
501 }
502
503 pub(crate) fn evaluated_column_coefficients(&self) -> EvaluatedColumnCoefficients<F> {
504 let shifts = Shifts::new(&self.domain.d1);
506
507 let n = self.domain.d1.size();
508
509 let mut sigmal1: [Vec<F>; PERMUTS] = array::from_fn(|_| vec![F::zero(); n]);
510
511 for (row, gate) in self.gates.iter().enumerate() {
512 for (cell, sigma) in gate.wires.iter().zip(sigmal1.iter_mut()) {
513 sigma[row] = shifts.cell_to_field(cell);
514 }
515 }
516
517 for row in n + 2 - (self.zk_rows as usize)..n - 1 {
520 for sigma in sigmal1.iter_mut() {
521 sigma[row] = F::zero();
522 }
523 }
524
525 let sigmal1: [_; PERMUTS] = {
526 let [s0, s1, s2, s3, s4, s5, s6] = sigmal1;
527 [
528 E::<F, D<F>>::from_vec_and_domain(s0, self.domain.d1),
529 E::<F, D<F>>::from_vec_and_domain(s1, self.domain.d1),
530 E::<F, D<F>>::from_vec_and_domain(s2, self.domain.d1),
531 E::<F, D<F>>::from_vec_and_domain(s3, self.domain.d1),
532 E::<F, D<F>>::from_vec_and_domain(s4, self.domain.d1),
533 E::<F, D<F>>::from_vec_and_domain(s5, self.domain.d1),
534 E::<F, D<F>>::from_vec_and_domain(s6, self.domain.d1),
535 ]
536 };
537
538 let permutation_coefficients: [DP<F>; PERMUTS] =
539 array::from_fn(|i| sigmal1[i].clone().interpolate());
540
541 let poseidon_selector = E::<F, D<F>>::from_vec_and_domain(
543 self.gates.iter().map(|gate| gate.ps()).collect(),
544 self.domain.d1,
545 )
546 .interpolate();
547
548 let generic_selector = E::<F, D<F>>::from_vec_and_domain(
550 self.gates
551 .iter()
552 .map(|gate| {
553 if matches!(gate.typ, GateType::Generic) {
554 F::one()
555 } else {
556 F::zero()
557 }
558 })
559 .collect(),
560 self.domain.d1,
561 )
562 .interpolate();
563
564 let coefficients: [_; COLUMNS] = array::from_fn(|i| {
566 let padded = self
567 .gates
568 .iter()
569 .map(|gate| gate.coeffs.get(i).cloned().unwrap_or_else(F::zero))
570 .collect();
571 let eval = E::from_vec_and_domain(padded, self.domain.d1);
572 eval.interpolate()
573 });
574
575 EvaluatedColumnCoefficients {
576 permutation_coefficients,
577 coefficients,
578 generic_selector,
579 poseidon_selector,
580 }
581 }
582
583 pub(crate) fn column_evaluations(
584 &self,
585 evaluated_column_coefficients: &EvaluatedColumnCoefficients<F>,
586 ) -> ColumnEvaluations<F> {
587 let permutation_coefficients8 = array::from_fn(|i| {
588 evaluated_column_coefficients.permutation_coefficients[i]
589 .evaluate_over_domain_by_ref(self.domain.d8)
590 });
591
592 let poseidon_selector8 = evaluated_column_coefficients
593 .poseidon_selector
594 .evaluate_over_domain_by_ref(self.domain.d8);
595
596 let complete_add_selector4 = selector_polynomial(
598 GateType::CompleteAdd,
599 &self.gates,
600 &self.domain,
601 &self.domain.d4,
602 self.disable_gates_checks,
603 );
604
605 let mul_selector8 = selector_polynomial(
606 GateType::VarBaseMul,
607 &self.gates,
608 &self.domain,
609 &self.domain.d8,
610 self.disable_gates_checks,
611 );
612
613 let emul_selector8 = selector_polynomial(
614 GateType::EndoMul,
615 &self.gates,
616 &self.domain,
617 &self.domain.d8,
618 self.disable_gates_checks,
619 );
620
621 let endomul_scalar_selector8 = selector_polynomial(
622 GateType::EndoMulScalar,
623 &self.gates,
624 &self.domain,
625 &self.domain.d8,
626 self.disable_gates_checks,
627 );
628
629 let generic_selector4 = evaluated_column_coefficients
630 .generic_selector
631 .evaluate_over_domain_by_ref(self.domain.d4);
632
633 let range_check0_selector8 = {
635 if !self.feature_flags.range_check0 {
636 None
637 } else {
638 Some(selector_polynomial(
639 GateType::RangeCheck0,
640 &self.gates,
641 &self.domain,
642 &self.domain.d8,
643 self.disable_gates_checks,
644 ))
645 }
646 };
647
648 let range_check1_selector8 = {
650 if !self.feature_flags.range_check1 {
651 None
652 } else {
653 Some(selector_polynomial(
654 GateType::RangeCheck1,
655 &self.gates,
656 &self.domain,
657 &self.domain.d8,
658 self.disable_gates_checks,
659 ))
660 }
661 };
662
663 let foreign_field_add_selector8 = {
665 if !self.feature_flags.foreign_field_add {
666 None
667 } else {
668 Some(selector_polynomial(
669 GateType::ForeignFieldAdd,
670 &self.gates,
671 &self.domain,
672 &self.domain.d8,
673 self.disable_gates_checks,
674 ))
675 }
676 };
677
678 let foreign_field_mul_selector8 = {
680 if !self.feature_flags.foreign_field_mul {
681 None
682 } else {
683 Some(selector_polynomial(
684 GateType::ForeignFieldMul,
685 &self.gates,
686 &self.domain,
687 &self.domain.d8,
688 self.disable_gates_checks,
689 ))
690 }
691 };
692
693 let xor_selector8 = {
694 if !self.feature_flags.xor {
695 None
696 } else {
697 Some(selector_polynomial(
698 GateType::Xor16,
699 &self.gates,
700 &self.domain,
701 &self.domain.d8,
702 self.disable_gates_checks,
703 ))
704 }
705 };
706
707 let rot_selector8 = {
708 if !self.feature_flags.rot {
709 None
710 } else {
711 Some(selector_polynomial(
712 GateType::Rot64,
713 &self.gates,
714 &self.domain,
715 &self.domain.d8,
716 self.disable_gates_checks,
717 ))
718 }
719 };
720
721 let coefficients8 = array::from_fn(|i| {
723 evaluated_column_coefficients.coefficients[i]
724 .evaluate_over_domain_by_ref(self.domain.d8)
725 });
726
727 ColumnEvaluations {
728 permutation_coefficients8,
729 coefficients8,
730 generic_selector4,
731 poseidon_selector8,
732 complete_add_selector4,
733 mul_selector8,
734 emul_selector8,
735 endomul_scalar_selector8,
736 range_check0_selector8,
737 range_check1_selector8,
738 foreign_field_add_selector8,
739 foreign_field_mul_selector8,
740 xor_selector8,
741 rot_selector8,
742 }
743 }
744}
745
746pub const NUM_CHUNKS_BY_DEFAULT: usize = 1;
748
749pub const ZK_ROWS_BY_DEFAULT: u64 = 3;
751
752pub fn zk_rows_strict_lower_bound(num_chunks: usize) -> usize {
763 (2 * (PERMUTS + 1) * num_chunks - 2) / PERMUTS
764}
765
766impl FeatureFlags {
767 pub fn from_gates_and_lookup_features<F: PrimeField>(
768 gates: &[CircuitGate<F>],
769 lookup_features: LookupFeatures,
770 ) -> FeatureFlags {
771 let mut feature_flags = FeatureFlags {
772 range_check0: false,
773 range_check1: false,
774 lookup_features,
775 foreign_field_add: false,
776 foreign_field_mul: false,
777 xor: false,
778 rot: false,
779 };
780
781 for gate in gates {
782 match gate.typ {
783 GateType::RangeCheck0 => feature_flags.range_check0 = true,
784 GateType::RangeCheck1 => feature_flags.range_check1 = true,
785 GateType::ForeignFieldAdd => feature_flags.foreign_field_add = true,
786 GateType::ForeignFieldMul => feature_flags.foreign_field_mul = true,
787 GateType::Xor16 => feature_flags.xor = true,
788 GateType::Rot64 => feature_flags.rot = true,
789 _ => (),
790 }
791 }
792
793 feature_flags
794 }
795
796 pub fn from_gates<F: PrimeField>(
797 gates: &[CircuitGate<F>],
798 uses_runtime_tables: bool,
799 ) -> FeatureFlags {
800 FeatureFlags::from_gates_and_lookup_features(
801 gates,
802 LookupFeatures::from_gates(gates, uses_runtime_tables),
803 )
804 }
805}
806
807impl<F: PrimeField> Builder<F> {
808 pub fn public(mut self, public: usize) -> Self {
811 self.public = public;
812 self
813 }
814
815 pub fn prev_challenges(mut self, prev_challenges: usize) -> Self {
818 self.prev_challenges = prev_challenges;
819 self
820 }
821
822 pub fn lookup(mut self, lookup_tables: Vec<LookupTable<F>>) -> Self {
831 self.lookup_tables = lookup_tables;
832 self
833 }
834
835 pub fn runtime(mut self, runtime_tables: Option<Vec<RuntimeTableCfg<F>>>) -> Self {
843 self.runtime_tables = runtime_tables;
844 self
845 }
846
847 pub fn shared_precomputations(
850 mut self,
851 shared_precomputations: Arc<DomainConstantEvaluations<F>>,
852 ) -> Self {
853 self.precomputations = Some(shared_precomputations);
854 self
855 }
856
857 pub fn disable_gates_checks(mut self, disable_gates_checks: bool) -> Self {
859 self.disable_gates_checks = disable_gates_checks;
860 self
861 }
862
863 pub fn max_poly_size(mut self, max_poly_size: Option<usize>) -> Self {
864 self.max_poly_size = max_poly_size;
865 self
866 }
867
868 pub fn lazy_mode(mut self, lazy_mode: bool) -> Self {
869 self.lazy_mode = lazy_mode;
870 self
871 }
872
873 pub fn build(self) -> Result<ConstraintSystem<F>, SetupError> {
875 let mut gates = self.gates;
876 let lookup_tables = self.lookup_tables.clone();
877 let runtime_tables = self.runtime_tables.clone();
878
879 assert!(gates.len() > 1);
882
883 let feature_flags = FeatureFlags::from_gates(&gates, runtime_tables.is_some());
884
885 let lookup_domain_size = {
886 let mut has_table_with_id_0 = false;
888 let mut lookup_domain_size: usize = lookup_tables
889 .iter()
890 .map(|LookupTable { id, data }| {
891 if *id == 0_i32 {
893 has_table_with_id_0 = true
894 }
895 if data.is_empty() {
896 0
897 } else {
898 data[0].len()
899 }
900 })
901 .sum();
902 if let Some(runtime_tables) = &runtime_tables {
904 for runtime_table in runtime_tables.iter() {
907 lookup_domain_size += runtime_table.len();
908 }
909 }
910 let LookupFeatures { patterns, .. } = &feature_flags.lookup_features;
912 let mut gate_lookup_tables = GateLookupTables {
913 xor: false,
914 range_check: false,
915 };
916 for pattern in patterns.into_iter() {
917 if let Some(gate_table) = pattern.table() {
918 gate_lookup_tables[gate_table] = true
919 }
920 }
921 for gate_table in gate_lookup_tables.into_iter() {
922 lookup_domain_size += gate_table.table_size();
923 }
924
925 if has_table_with_id_0 {
928 lookup_domain_size
929 } else {
930 lookup_domain_size + 1
931 }
932 };
933
934 let (zk_rows, domain_size_lower_bound) = {
958 let circuit_lower_bound = core::cmp::max(gates.len(), lookup_domain_size + 1);
962 let get_domain_size_lower_bound = |zk_rows: u64| circuit_lower_bound + zk_rows as usize;
963
964 let mut zk_rows = 3;
965 let mut domain_size_lower_bound = get_domain_size_lower_bound(zk_rows);
966 if let Some(max_poly_size) = self.max_poly_size {
967 while {
973 let domain_size = D::<F>::compute_size_of_domain(domain_size_lower_bound)
974 .ok_or(SetupError::DomainCreation(
975 DomainCreationError::DomainSizeFailed(domain_size_lower_bound),
976 ))?;
977 let num_chunks = if domain_size < max_poly_size {
978 1
979 } else {
980 domain_size / max_poly_size
981 };
982 zk_rows = (zk_rows_strict_lower_bound(num_chunks) + 1) as u64;
983 domain_size_lower_bound = get_domain_size_lower_bound(zk_rows);
984 domain_size < domain_size_lower_bound
985 } {}
986 }
987 (zk_rows, domain_size_lower_bound)
988 };
989
990 let domain = EvaluationDomains::<F>::create(domain_size_lower_bound)
994 .map_err(SetupError::DomainCreation)?;
995
996 assert!(domain.d1.size > zk_rows);
997
998 let d1_size = domain.d1.size();
1000 let mut padding = (gates.len()..d1_size)
1001 .map(|i| {
1002 CircuitGate::<F>::zero(array::from_fn(|j| Wire {
1003 col: WIRES[j],
1004 row: i,
1005 }))
1006 })
1007 .collect();
1008 gates.append(&mut padding);
1009
1010 let shifts = Shifts::new(&domain.d1);
1012
1013 let gates = Arc::new(gates);
1017 let gates_clone = Arc::clone(&gates);
1018 let lookup_constraint_system = LazyCache::new(move || {
1019 LookupConstraintSystem::create(
1020 &gates_clone,
1021 self.lookup_tables,
1022 self.runtime_tables,
1023 &domain,
1024 zk_rows as usize,
1025 )
1026 });
1027 if !self.lazy_mode {
1028 lookup_constraint_system
1030 .try_get_or_err()
1031 .map_err(SetupError::from)?;
1032 }
1033
1034 let sid = shifts.map[0].clone();
1035
1036 let endo = F::zero();
1038
1039 let precomputations = if !self.lazy_mode {
1040 match self.precomputations {
1041 Some(t) => LazyCache::preinit(t),
1042 None => LazyCache::preinit(Arc::new(
1043 DomainConstantEvaluations::create(domain, zk_rows).unwrap(),
1044 )),
1045 }
1046 } else {
1047 LazyCache::new(move || {
1048 Arc::new(DomainConstantEvaluations::create(domain, zk_rows).unwrap())
1049 })
1050 };
1051
1052 let constraints = ConstraintSystem {
1053 domain,
1054 public: self.public,
1055 prev_challenges: self.prev_challenges,
1056 sid,
1057 gates,
1058 shift: shifts.shifts,
1059 endo,
1060 zk_rows,
1061 lookup_constraint_system: Arc::new(lookup_constraint_system),
1063 feature_flags,
1064 precomputations: Arc::new(precomputations),
1065 disable_gates_checks: self.disable_gates_checks,
1066 };
1067
1068 Ok(constraints)
1069 }
1070}