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(
55 feature = "ocaml_types",
56 derive(ocaml::IntoValue, ocaml::FromValue, ocaml_gen::Struct)
57)]
58#[cfg_attr(feature = "wasm_types", wasm_bindgen::prelude::wasm_bindgen)]
59#[derive(Copy, Clone, Serialize, Deserialize, Debug)]
60pub struct FeatureFlags {
61 pub range_check0: bool,
67 pub range_check1: bool,
71 pub foreign_field_add: bool,
73 pub foreign_field_mul: bool,
75 pub xor: bool,
77 pub rot: bool,
79 pub lookup_features: LookupFeatures,
82}
83
84impl Default for FeatureFlags {
85 fn default() -> FeatureFlags {
87 FeatureFlags {
88 range_check0: false,
89 range_check1: false,
90 lookup_features: LookupFeatures {
91 patterns: LookupPatterns {
92 xor: false,
93 lookup: false,
94 range_check: false,
95 foreign_field_mul: false,
96 },
97 joint_lookup_used: false,
98 uses_runtime_tables: false,
99 },
100 foreign_field_add: false,
101 foreign_field_mul: false,
102 xor: false,
103 rot: false,
104 }
105 }
106}
107
108#[serde_as]
110#[derive(Clone, Serialize, Deserialize, Debug)]
111pub struct EvaluatedColumnCoefficients<F: PrimeField> {
112 #[serde_as(as = "[o1_utils::serialization::SerdeAs; PERMUTS]")]
114 pub permutation_coefficients: [DP<F>; PERMUTS],
115
116 #[serde_as(as = "[o1_utils::serialization::SerdeAs; COLUMNS]")]
118 pub coefficients: [DP<F>; COLUMNS],
119
120 #[serde_as(as = "o1_utils::serialization::SerdeAs")]
122 pub generic_selector: DP<F>,
123
124 #[serde_as(as = "o1_utils::serialization::SerdeAs")]
126 pub poseidon_selector: DP<F>,
127}
128
129#[serde_as]
132#[derive(Clone, Serialize, Deserialize, Debug)]
133pub struct ColumnEvaluations<F: PrimeField> {
134 #[serde_as(as = "[o1_utils::serialization::SerdeAs; PERMUTS]")]
136 pub permutation_coefficients8: [E<F, D<F>>; PERMUTS],
137
138 #[serde_as(as = "[o1_utils::serialization::SerdeAs; COLUMNS]")]
140 pub coefficients8: [E<F, D<F>>; COLUMNS],
141
142 #[serde_as(as = "o1_utils::serialization::SerdeAs")]
144 pub generic_selector4: E<F, D<F>>,
145
146 #[serde_as(as = "o1_utils::serialization::SerdeAs")]
148 pub poseidon_selector8: E<F, D<F>>,
149
150 #[serde_as(as = "o1_utils::serialization::SerdeAs")]
152 pub complete_add_selector4: E<F, D<F>>,
153
154 #[serde_as(as = "o1_utils::serialization::SerdeAs")]
156 pub mul_selector8: E<F, D<F>>,
157
158 #[serde_as(as = "o1_utils::serialization::SerdeAs")]
160 pub emul_selector8: E<F, D<F>>,
161
162 #[serde_as(as = "o1_utils::serialization::SerdeAs")]
164 pub endomul_scalar_selector8: E<F, D<F>>,
165
166 #[serde_as(as = "Option<o1_utils::serialization::SerdeAs>")]
168 pub range_check0_selector8: Option<E<F, D<F>>>,
169
170 #[serde_as(as = "Option<o1_utils::serialization::SerdeAs>")]
172 pub range_check1_selector8: Option<E<F, D<F>>>,
173
174 #[serde_as(as = "Option<o1_utils::serialization::SerdeAs>")]
176 pub foreign_field_add_selector8: Option<E<F, D<F>>>,
177
178 #[serde_as(as = "Option<o1_utils::serialization::SerdeAs>")]
180 pub foreign_field_mul_selector8: Option<E<F, D<F>>>,
181
182 #[serde_as(as = "Option<o1_utils::serialization::SerdeAs>")]
184 pub xor_selector8: Option<E<F, D<F>>>,
185
186 #[serde_as(as = "Option<o1_utils::serialization::SerdeAs>")]
188 pub rot_selector8: Option<E<F, D<F>>>,
189}
190
191#[serde_as]
192#[derive(Clone, Serialize, Debug)]
193pub struct ConstraintSystem<F: PrimeField> {
194 pub public: usize,
198 pub prev_challenges: usize,
200 #[serde(bound = "EvaluationDomains<F>: Serialize + DeserializeOwned")]
202 pub domain: EvaluationDomains<F>,
203 #[serde(bound = "CircuitGate<F>: Serialize + DeserializeOwned")]
205 pub gates: Arc<Vec<CircuitGate<F>>>,
206
207 pub zk_rows: u64,
208
209 pub feature_flags: FeatureFlags,
211
212 #[serde_as(as = "Vec<o1_utils::serialization::SerdeAs>")]
214 pub sid: Vec<F>,
215
216 #[serde_as(as = "[o1_utils::serialization::SerdeAs; PERMUTS]")]
218 pub shift: [F; PERMUTS],
219 #[serde_as(as = "o1_utils::serialization::SerdeAs")]
221 pub endo: F,
222 #[serde(bound = "LookupConstraintSystem<F>: Serialize + DeserializeOwned")]
224 pub lookup_constraint_system: Arc<LookupConstraintSystemCache<F>>,
225 #[serde(skip)]
227 precomputations: Arc<LazyCache<Arc<DomainConstantEvaluations<F>>>>,
228
229 pub disable_gates_checks: bool,
231}
232
233pub(crate) type LookupConstraintSystemCache<F> =
234 LazyCache<Result<Option<LookupConstraintSystem<F>>, LookupError>>;
235
236impl<'de, F> Deserialize<'de> for ConstraintSystem<F>
237where
238 F: PrimeField,
239 EvaluationDomains<F>: Serialize + DeserializeOwned,
240 CircuitGate<F>: Serialize + DeserializeOwned,
241 LookupConstraintSystem<F>: Serialize + DeserializeOwned,
242{
243 fn deserialize<D>(deserializer: D) -> Result<ConstraintSystem<F>, D::Error>
244 where
245 D: serde::Deserializer<'de>,
246 {
247 #[serde_as]
248 #[derive(Clone, Serialize, Deserialize, Debug)]
249 struct ConstraintSystemSerde<F: PrimeField> {
250 public: usize,
251 prev_challenges: usize,
252 #[serde(bound = "EvaluationDomains<F>: Serialize + DeserializeOwned")]
253 domain: EvaluationDomains<F>,
254 #[serde(bound = "CircuitGate<F>: Serialize + DeserializeOwned")]
255 gates: Arc<Vec<CircuitGate<F>>>,
256 zk_rows: u64,
257 feature_flags: FeatureFlags,
258 #[serde_as(as = "Vec<o1_utils::serialization::SerdeAs>")]
259 sid: Vec<F>,
260 #[serde_as(as = "[o1_utils::serialization::SerdeAs; PERMUTS]")]
261 shift: [F; PERMUTS],
262 #[serde_as(as = "o1_utils::serialization::SerdeAs")]
263 endo: F,
264 #[serde(bound = "LookupConstraintSystem<F>: Serialize + DeserializeOwned")]
265 lookup_constraint_system: Arc<LookupConstraintSystemCache<F>>,
266 disable_gates_checks: bool,
267 }
268
269 let cs = ConstraintSystemSerde::<F>::deserialize(deserializer)?;
271
272 let precomputations = Arc::new({
273 LazyCache::new(move || {
274 Arc::new(DomainConstantEvaluations::create(cs.domain, cs.zk_rows).unwrap())
275 })
276 });
277
278 Ok(ConstraintSystem {
279 public: cs.public,
280 prev_challenges: cs.prev_challenges,
281 domain: cs.domain,
282 gates: cs.gates,
283 zk_rows: cs.zk_rows,
284 feature_flags: cs.feature_flags,
285 sid: cs.sid,
286 shift: cs.shift,
287 endo: cs.endo,
288 lookup_constraint_system: cs.lookup_constraint_system,
289 disable_gates_checks: cs.disable_gates_checks,
290 precomputations,
291 })
292 }
293}
294
295#[derive(Debug)]
297pub enum GateError {
298 DisconnectedWires(Wire, Wire),
300 IncorrectPublic(usize),
302 Custom { row: usize, err: String },
304}
305
306pub struct Builder<F: PrimeField> {
307 gates: Vec<CircuitGate<F>>,
308 public: usize,
309 prev_challenges: usize,
310 lookup_tables: Vec<LookupTable<F>>,
311 runtime_tables: Option<Vec<RuntimeTableCfg<F>>>,
312 precomputations: Option<Arc<DomainConstantEvaluations<F>>>,
313 disable_gates_checks: bool,
314 max_poly_size: Option<usize>,
315 lazy_mode: bool,
316}
317
318pub fn selector_polynomial<F: PrimeField>(
320 gate_type: GateType,
321 gates: &[CircuitGate<F>],
322 domain: &EvaluationDomains<F>,
323 target_domain: &D<F>,
324 disable_gates_checks: bool,
325) -> E<F, D<F>> {
326 if cfg!(debug_assertions) && disable_gates_checks {
327 DP::<F>::zero().evaluate_over_domain_by_ref(*target_domain)
328 } else {
329 let coeff = E::<F, D<F>>::from_vec_and_domain(
331 gates
332 .iter()
333 .map(|gate| {
334 if gate.typ == gate_type {
335 F::one()
336 } else {
337 F::zero()
338 }
339 })
340 .collect(),
341 domain.d1,
342 )
343 .interpolate();
344
345 coeff.evaluate_over_domain_by_ref(*target_domain)
346 }
347}
348
349impl<F: PrimeField> ConstraintSystem<F> {
350 pub fn create(gates: Vec<CircuitGate<F>>) -> Builder<F> {
366 Builder {
367 gates,
368 public: 0,
369 prev_challenges: 0,
370 lookup_tables: vec![],
371 runtime_tables: None,
372 precomputations: None,
373 disable_gates_checks: false,
374 max_poly_size: None,
375 lazy_mode: false,
376 }
377 }
378
379 pub fn precomputations(&self) -> Arc<DomainConstantEvaluations<F>> {
380 self.precomputations.get().clone()
381 }
382
383 pub fn for_testing(gates: Vec<CircuitGate<F>>) -> Self {
385 let public = 0;
386 ConstraintSystem::<F>::create(gates)
388 .public(public)
389 .build()
390 .unwrap()
391 }
392
393 pub fn fp_for_testing(gates: Vec<CircuitGate<F>>) -> Self {
394 Self::for_testing(gates)
395 }
396}
397
398impl<const FULL_ROUNDS: usize, F, G, Srs> ProverIndex<FULL_ROUNDS, G, Srs>
399where
400 F: PrimeField,
401 G: KimchiCurve<FULL_ROUNDS, ScalarField = F>,
402 Srs: SRS<G>,
403{
404 pub fn verify(&self, witness: &[Vec<F>; COLUMNS], public: &[F]) -> Result<(), GateError> {
409 let pad = vec![F::zero(); self.cs.domain.d1.size() - witness[0].len()];
411 let witness: [Vec<F>; COLUMNS] = array::from_fn(|i| {
412 let mut w = witness[i].to_vec();
413 w.extend_from_slice(&pad);
414 w
415 });
416
417 for (row, gate) in self.cs.gates.iter().enumerate() {
419 for col in 0..PERMUTS {
421 let wire = gate.wires[col];
422
423 if wire.col >= PERMUTS {
424 return Err(GateError::Custom {
425 row,
426 err: format!("a wire can only be connected to the first {PERMUTS} columns"),
427 });
428 }
429
430 if witness[col][row] != witness[wire.col][wire.row] {
431 return Err(GateError::DisconnectedWires(
432 Wire { col, row },
433 Wire {
434 col: wire.col,
435 row: wire.row,
436 },
437 ));
438 }
439 }
440
441 if row < self.cs.public && gate.coeffs.first() != Some(&F::one()) {
443 return Err(GateError::IncorrectPublic(row));
444 }
445
446 gate.verify(row, &witness, self, public)
448 .map_err(|err| GateError::Custom { row, err })?;
449 }
450
451 Ok(())
453 }
454}
455
456impl<F: PrimeField> ConstraintSystem<F> {
457 pub fn evaluate(&self, w: &[DP<F>; COLUMNS], z: &DP<F>) -> WitnessOverDomains<F> {
459 let (w8, z8): ([E<F, D<F>>; COLUMNS], _) = {
461 let mut res = w
462 .par_iter()
463 .chain(rayon::iter::once(z))
464 .map(|elem| elem.evaluate_over_domain_by_ref(self.domain.d8))
465 .collect::<Vec<_>>();
466 let z8 = res[COLUMNS].clone();
467 res.truncate(COLUMNS);
468 (res.try_into().unwrap(), z8)
469 };
470
471 let w4: [E<F, D<F>>; COLUMNS] = (0..COLUMNS)
472 .into_par_iter()
473 .map(|i| {
474 E::<F, D<F>>::from_vec_and_domain(
475 (0..self.domain.d4.size)
476 .map(|j| w8[i].evals[2 * j as usize])
477 .collect(),
478 self.domain.d4,
479 )
480 })
481 .collect::<Vec<_>>()
482 .try_into()
483 .unwrap();
484
485 let z4 = DP::<F>::zero().evaluate_over_domain_by_ref(D::<F>::new(1).unwrap());
486 let z8_shift8 = z8.shift(8);
487
488 let d4_next_w: [_; COLUMNS] = w4
489 .par_iter()
490 .map(|w4_i| w4_i.shift(4))
491 .collect::<Vec<_>>()
492 .try_into()
493 .unwrap();
494
495 let d8_next_w: [_; COLUMNS] = w8
496 .par_iter()
497 .map(|w8_i| w8_i.shift(8))
498 .collect::<Vec<_>>()
499 .try_into()
500 .unwrap();
501
502 WitnessOverDomains {
503 d4: WitnessShifts {
504 next: WitnessEvals {
505 w: d4_next_w,
506 z: z4.clone(), },
509 this: WitnessEvals {
510 w: w4,
511 z: z4, },
513 },
514 d8: WitnessShifts {
515 next: WitnessEvals {
516 w: d8_next_w,
517 z: z8_shift8,
518 },
519 this: WitnessEvals { w: w8, z: z8 },
520 },
521 }
522 }
523
524 pub(crate) fn evaluated_column_coefficients(&self) -> EvaluatedColumnCoefficients<F> {
525 let shifts = Shifts::new(&self.domain.d1);
527
528 let n = self.domain.d1.size();
529
530 let mut sigmal1: [Vec<F>; PERMUTS] = array::from_fn(|_| vec![F::zero(); n]);
531
532 for (row, gate) in self.gates.iter().enumerate() {
533 for (cell, sigma) in gate.wires.iter().zip(sigmal1.iter_mut()) {
534 sigma[row] = shifts.cell_to_field(cell);
535 }
536 }
537
538 for row in n + 2 - (self.zk_rows as usize)..n - 1 {
541 for sigma in sigmal1.iter_mut() {
542 sigma[row] = F::zero();
543 }
544 }
545
546 let sigmal1: [_; PERMUTS] = {
547 let [s0, s1, s2, s3, s4, s5, s6] = sigmal1;
548 [
549 E::<F, D<F>>::from_vec_and_domain(s0, self.domain.d1),
550 E::<F, D<F>>::from_vec_and_domain(s1, self.domain.d1),
551 E::<F, D<F>>::from_vec_and_domain(s2, self.domain.d1),
552 E::<F, D<F>>::from_vec_and_domain(s3, self.domain.d1),
553 E::<F, D<F>>::from_vec_and_domain(s4, self.domain.d1),
554 E::<F, D<F>>::from_vec_and_domain(s5, self.domain.d1),
555 E::<F, D<F>>::from_vec_and_domain(s6, self.domain.d1),
556 ]
557 };
558
559 let permutation_coefficients: [DP<F>; PERMUTS] =
560 array::from_fn(|i| sigmal1[i].clone().interpolate());
561
562 let poseidon_selector = E::<F, D<F>>::from_vec_and_domain(
564 self.gates.iter().map(|gate| gate.ps()).collect(),
565 self.domain.d1,
566 )
567 .interpolate();
568
569 let generic_selector = E::<F, D<F>>::from_vec_and_domain(
571 self.gates
572 .iter()
573 .map(|gate| {
574 if matches!(gate.typ, GateType::Generic) {
575 F::one()
576 } else {
577 F::zero()
578 }
579 })
580 .collect(),
581 self.domain.d1,
582 )
583 .interpolate();
584
585 let coefficients: [_; COLUMNS] = array::from_fn(|i| {
587 let padded = self
588 .gates
589 .iter()
590 .map(|gate| gate.coeffs.get(i).cloned().unwrap_or_else(F::zero))
591 .collect();
592 let eval = E::from_vec_and_domain(padded, self.domain.d1);
593 eval.interpolate()
594 });
595
596 EvaluatedColumnCoefficients {
597 permutation_coefficients,
598 coefficients,
599 generic_selector,
600 poseidon_selector,
601 }
602 }
603
604 pub(crate) fn column_evaluations(
605 &self,
606 evaluated_column_coefficients: &EvaluatedColumnCoefficients<F>,
607 ) -> ColumnEvaluations<F> {
608 let permutation_coefficients8 = array::from_fn(|i| {
609 evaluated_column_coefficients.permutation_coefficients[i]
610 .evaluate_over_domain_by_ref(self.domain.d8)
611 });
612
613 let poseidon_selector8 = evaluated_column_coefficients
614 .poseidon_selector
615 .evaluate_over_domain_by_ref(self.domain.d8);
616
617 let complete_add_selector4 = selector_polynomial(
619 GateType::CompleteAdd,
620 &self.gates,
621 &self.domain,
622 &self.domain.d4,
623 self.disable_gates_checks,
624 );
625
626 let mul_selector8 = selector_polynomial(
627 GateType::VarBaseMul,
628 &self.gates,
629 &self.domain,
630 &self.domain.d8,
631 self.disable_gates_checks,
632 );
633
634 let emul_selector8 = selector_polynomial(
635 GateType::EndoMul,
636 &self.gates,
637 &self.domain,
638 &self.domain.d8,
639 self.disable_gates_checks,
640 );
641
642 let endomul_scalar_selector8 = selector_polynomial(
643 GateType::EndoMulScalar,
644 &self.gates,
645 &self.domain,
646 &self.domain.d8,
647 self.disable_gates_checks,
648 );
649
650 let generic_selector4 = evaluated_column_coefficients
651 .generic_selector
652 .evaluate_over_domain_by_ref(self.domain.d4);
653
654 let range_check0_selector8 = {
656 if !self.feature_flags.range_check0 {
657 None
658 } else {
659 Some(selector_polynomial(
660 GateType::RangeCheck0,
661 &self.gates,
662 &self.domain,
663 &self.domain.d8,
664 self.disable_gates_checks,
665 ))
666 }
667 };
668
669 let range_check1_selector8 = {
671 if !self.feature_flags.range_check1 {
672 None
673 } else {
674 Some(selector_polynomial(
675 GateType::RangeCheck1,
676 &self.gates,
677 &self.domain,
678 &self.domain.d8,
679 self.disable_gates_checks,
680 ))
681 }
682 };
683
684 let foreign_field_add_selector8 = {
686 if !self.feature_flags.foreign_field_add {
687 None
688 } else {
689 Some(selector_polynomial(
690 GateType::ForeignFieldAdd,
691 &self.gates,
692 &self.domain,
693 &self.domain.d8,
694 self.disable_gates_checks,
695 ))
696 }
697 };
698
699 let foreign_field_mul_selector8 = {
701 if !self.feature_flags.foreign_field_mul {
702 None
703 } else {
704 Some(selector_polynomial(
705 GateType::ForeignFieldMul,
706 &self.gates,
707 &self.domain,
708 &self.domain.d8,
709 self.disable_gates_checks,
710 ))
711 }
712 };
713
714 let xor_selector8 = {
715 if !self.feature_flags.xor {
716 None
717 } else {
718 Some(selector_polynomial(
719 GateType::Xor16,
720 &self.gates,
721 &self.domain,
722 &self.domain.d8,
723 self.disable_gates_checks,
724 ))
725 }
726 };
727
728 let rot_selector8 = {
729 if !self.feature_flags.rot {
730 None
731 } else {
732 Some(selector_polynomial(
733 GateType::Rot64,
734 &self.gates,
735 &self.domain,
736 &self.domain.d8,
737 self.disable_gates_checks,
738 ))
739 }
740 };
741
742 let coefficients8 = array::from_fn(|i| {
744 evaluated_column_coefficients.coefficients[i]
745 .evaluate_over_domain_by_ref(self.domain.d8)
746 });
747
748 ColumnEvaluations {
749 permutation_coefficients8,
750 coefficients8,
751 generic_selector4,
752 poseidon_selector8,
753 complete_add_selector4,
754 mul_selector8,
755 emul_selector8,
756 endomul_scalar_selector8,
757 range_check0_selector8,
758 range_check1_selector8,
759 foreign_field_add_selector8,
760 foreign_field_mul_selector8,
761 xor_selector8,
762 rot_selector8,
763 }
764 }
765}
766
767pub const NUM_CHUNKS_BY_DEFAULT: usize = 1;
769
770pub const ZK_ROWS_BY_DEFAULT: u64 = 3;
772
773pub fn zk_rows_strict_lower_bound(num_chunks: usize) -> usize {
784 (2 * (PERMUTS + 1) * num_chunks - 2) / PERMUTS
785}
786
787impl FeatureFlags {
788 pub fn from_gates_and_lookup_features<F: PrimeField>(
790 gates: &[CircuitGate<F>],
791 lookup_features: LookupFeatures,
792 ) -> FeatureFlags {
793 let mut feature_flags = FeatureFlags {
794 range_check0: false,
795 range_check1: false,
796 lookup_features,
797 foreign_field_add: false,
798 foreign_field_mul: false,
799 xor: false,
800 rot: false,
801 };
802
803 for gate in gates {
804 match gate.typ {
805 GateType::RangeCheck0 => feature_flags.range_check0 = true,
806 GateType::RangeCheck1 => feature_flags.range_check1 = true,
807 GateType::ForeignFieldAdd => feature_flags.foreign_field_add = true,
808 GateType::ForeignFieldMul => feature_flags.foreign_field_mul = true,
809 GateType::Xor16 => feature_flags.xor = true,
810 GateType::Rot64 => feature_flags.rot = true,
811 _ => (),
812 }
813 }
814
815 feature_flags
816 }
817
818 pub fn from_gates<F: PrimeField>(
823 gates: &[CircuitGate<F>],
824 uses_runtime_tables: bool,
825 ) -> FeatureFlags {
826 FeatureFlags::from_gates_and_lookup_features(
827 gates,
828 LookupFeatures::from_gates(gates, uses_runtime_tables),
829 )
830 }
831}
832
833impl<F: PrimeField> Builder<F> {
834 pub fn public(mut self, public: usize) -> Self {
837 self.public = public;
838 self
839 }
840
841 pub fn prev_challenges(mut self, prev_challenges: usize) -> Self {
844 self.prev_challenges = prev_challenges;
845 self
846 }
847
848 pub fn lookup(mut self, lookup_tables: Vec<LookupTable<F>>) -> Self {
857 self.lookup_tables = lookup_tables;
858 self
859 }
860
861 pub fn runtime(mut self, runtime_tables: Option<Vec<RuntimeTableCfg<F>>>) -> Self {
869 self.runtime_tables = runtime_tables;
870 self
871 }
872
873 pub fn shared_precomputations(
876 mut self,
877 shared_precomputations: Arc<DomainConstantEvaluations<F>>,
878 ) -> Self {
879 self.precomputations = Some(shared_precomputations);
880 self
881 }
882
883 pub fn disable_gates_checks(mut self, disable_gates_checks: bool) -> Self {
885 self.disable_gates_checks = disable_gates_checks;
886 self
887 }
888
889 pub fn max_poly_size(mut self, max_poly_size: Option<usize>) -> Self {
890 self.max_poly_size = max_poly_size;
891 self
892 }
893
894 pub fn lazy_mode(mut self, lazy_mode: bool) -> Self {
895 self.lazy_mode = lazy_mode;
896 self
897 }
898
899 pub fn build(self) -> Result<ConstraintSystem<F>, SetupError> {
901 let mut gates = self.gates;
902 let lookup_tables = self.lookup_tables.clone();
903 let runtime_tables = self.runtime_tables.clone();
904
905 assert!(gates.len() > 1);
908
909 let feature_flags = FeatureFlags::from_gates(&gates, runtime_tables.is_some());
910
911 let lookup_domain_size = {
912 let mut has_table_with_id_0 = false;
914 let mut lookup_domain_size: usize = lookup_tables
915 .iter()
916 .map(|LookupTable { id, data }| {
917 if *id == 0_i32 {
919 has_table_with_id_0 = true
920 }
921 if data.is_empty() {
922 0
923 } else {
924 data[0].len()
925 }
926 })
927 .sum();
928 if let Some(runtime_tables) = &runtime_tables {
930 for runtime_table in runtime_tables.iter() {
933 lookup_domain_size += runtime_table.len();
934 }
935 }
936 let LookupFeatures { patterns, .. } = &feature_flags.lookup_features;
938 let mut gate_lookup_tables = GateLookupTables {
939 xor: false,
940 range_check: false,
941 };
942 for pattern in patterns.into_iter() {
943 if let Some(gate_table) = pattern.table() {
944 gate_lookup_tables[gate_table] = true
945 }
946 }
947 for gate_table in gate_lookup_tables.into_iter() {
948 lookup_domain_size += gate_table.table_size();
949 }
950
951 if has_table_with_id_0 {
954 lookup_domain_size
955 } else {
956 lookup_domain_size + 1
957 }
958 };
959
960 let (zk_rows, domain_size_lower_bound) = {
984 let circuit_lower_bound = core::cmp::max(gates.len(), lookup_domain_size + 1);
988 let get_domain_size_lower_bound = |zk_rows: u64| circuit_lower_bound + zk_rows as usize;
989
990 let mut zk_rows = 3;
991 let mut domain_size_lower_bound = get_domain_size_lower_bound(zk_rows);
992 if let Some(max_poly_size) = self.max_poly_size {
993 while {
999 let domain_size = D::<F>::compute_size_of_domain(domain_size_lower_bound)
1000 .ok_or(SetupError::DomainCreation(
1001 DomainCreationError::DomainSizeFailed(domain_size_lower_bound),
1002 ))?;
1003 let num_chunks = if domain_size < max_poly_size {
1004 1
1005 } else {
1006 domain_size / max_poly_size
1007 };
1008 zk_rows = (zk_rows_strict_lower_bound(num_chunks) + 1) as u64;
1009 domain_size_lower_bound = get_domain_size_lower_bound(zk_rows);
1010 domain_size < domain_size_lower_bound
1011 } {}
1012 }
1013 (zk_rows, domain_size_lower_bound)
1014 };
1015
1016 let domain = EvaluationDomains::<F>::create(domain_size_lower_bound)
1020 .map_err(SetupError::DomainCreation)?;
1021
1022 assert!(domain.d1.size > zk_rows);
1023
1024 let d1_size = domain.d1.size();
1026 let mut padding = (gates.len()..d1_size)
1027 .map(|i| {
1028 CircuitGate::<F>::zero(array::from_fn(|j| Wire {
1029 col: WIRES[j],
1030 row: i,
1031 }))
1032 })
1033 .collect();
1034 gates.append(&mut padding);
1035
1036 let shifts = Shifts::new(&domain.d1);
1038
1039 let gates = Arc::new(gates);
1043 let gates_clone = Arc::clone(&gates);
1044 let lookup_constraint_system = LazyCache::new(move || {
1045 LookupConstraintSystem::create(
1046 &gates_clone,
1047 self.lookup_tables,
1048 self.runtime_tables,
1049 &domain,
1050 zk_rows as usize,
1051 )
1052 });
1053 if !self.lazy_mode {
1054 lookup_constraint_system
1056 .try_get_or_err()
1057 .map_err(SetupError::from)?;
1058 }
1059
1060 let sid = shifts.map[0].clone();
1061
1062 let endo = F::zero();
1064
1065 let precomputations = if !self.lazy_mode {
1066 match self.precomputations {
1067 Some(t) => LazyCache::preinit(t),
1068 None => LazyCache::preinit(Arc::new(
1069 DomainConstantEvaluations::create(domain, zk_rows).unwrap(),
1070 )),
1071 }
1072 } else {
1073 LazyCache::new(move || {
1074 Arc::new(DomainConstantEvaluations::create(domain, zk_rows).unwrap())
1075 })
1076 };
1077
1078 let constraints = ConstraintSystem {
1079 domain,
1080 public: self.public,
1081 prev_challenges: self.prev_challenges,
1082 sid,
1083 gates,
1084 shift: shifts.shifts,
1085 endo,
1086 zk_rows,
1087 lookup_constraint_system: Arc::new(lookup_constraint_system),
1089 feature_flags,
1090 precomputations: Arc::new(precomputations),
1091 disable_gates_checks: self.disable_gates_checks,
1092 };
1093
1094 Ok(constraints)
1095 }
1096}