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 polynomials::permutation::Shifts,
14 wires::*,
15 },
16 curve::KimchiCurve,
17 error::{DomainCreationError, SetupError},
18 o1_utils::lazy_cache::LazyCache,
19};
20use alloc::{format, string::String, sync::Arc, vec, vec::Vec};
21use ark_ff::{PrimeField, Zero};
22use ark_poly::{
23 univariate::DensePolynomial as DP, EvaluationDomain, Evaluations as E,
24 Radix2EvaluationDomain as D,
25};
26use core::{array, default::Default};
27use serde::{de::DeserializeOwned, Deserialize, Serialize};
28use serde_with::serde_as;
29
30#[cfg(feature = "prover")]
31use {
32 crate::circuits::polynomial::{WitnessEvals, WitnessOverDomains},
33 crate::prover_index::ProverIndex,
34 o1_utils::ExtendedEvaluations,
35 poly_commitment::SRS,
36};
37
38#[cfg(feature = "parallel")]
39use rayon::prelude::*;
40
41#[cfg_attr(
61 feature = "ocaml_types",
62 derive(ocaml::IntoValue, ocaml::FromValue, ocaml_gen::Struct)
63)]
64#[cfg_attr(feature = "wasm_types", wasm_bindgen::prelude::wasm_bindgen)]
65#[derive(Copy, Clone, Serialize, Deserialize, Debug)]
66pub struct FeatureFlags {
67 pub range_check0: bool,
73 pub range_check1: bool,
77 pub foreign_field_add: bool,
79 pub foreign_field_mul: bool,
81 pub xor: bool,
83 pub rot: bool,
85 pub lookup_features: LookupFeatures,
88}
89
90impl Default for FeatureFlags {
91 fn default() -> FeatureFlags {
93 FeatureFlags {
94 range_check0: false,
95 range_check1: false,
96 lookup_features: LookupFeatures {
97 patterns: LookupPatterns {
98 xor: false,
99 lookup: false,
100 range_check: false,
101 foreign_field_mul: false,
102 },
103 joint_lookup_used: false,
104 uses_runtime_tables: false,
105 },
106 foreign_field_add: false,
107 foreign_field_mul: false,
108 xor: false,
109 rot: false,
110 }
111 }
112}
113
114#[serde_as]
116#[derive(Clone, Serialize, Deserialize, Debug)]
117pub struct EvaluatedColumnCoefficients<F: PrimeField> {
118 #[serde_as(as = "[o1_utils::serialization::SerdeAs; PERMUTS]")]
120 pub permutation_coefficients: [DP<F>; PERMUTS],
121
122 #[serde_as(as = "[o1_utils::serialization::SerdeAs; COLUMNS]")]
124 pub coefficients: [DP<F>; COLUMNS],
125
126 #[serde_as(as = "o1_utils::serialization::SerdeAs")]
128 pub generic_selector: DP<F>,
129
130 #[serde_as(as = "o1_utils::serialization::SerdeAs")]
132 pub poseidon_selector: DP<F>,
133}
134
135#[serde_as]
138#[derive(Clone, Serialize, Deserialize, Debug)]
139pub struct ColumnEvaluations<F: PrimeField> {
140 #[serde_as(as = "[o1_utils::serialization::SerdeAs; PERMUTS]")]
142 pub permutation_coefficients8: [E<F, D<F>>; PERMUTS],
143
144 #[serde_as(as = "[o1_utils::serialization::SerdeAs; COLUMNS]")]
146 pub coefficients8: [E<F, D<F>>; COLUMNS],
147
148 #[serde_as(as = "o1_utils::serialization::SerdeAs")]
150 pub generic_selector4: E<F, D<F>>,
151
152 #[serde_as(as = "o1_utils::serialization::SerdeAs")]
154 pub poseidon_selector8: E<F, D<F>>,
155
156 #[serde_as(as = "o1_utils::serialization::SerdeAs")]
158 pub complete_add_selector4: E<F, D<F>>,
159
160 #[serde_as(as = "o1_utils::serialization::SerdeAs")]
162 pub mul_selector8: E<F, D<F>>,
163
164 #[serde_as(as = "o1_utils::serialization::SerdeAs")]
166 pub emul_selector8: E<F, D<F>>,
167
168 #[serde_as(as = "o1_utils::serialization::SerdeAs")]
170 pub endomul_scalar_selector8: E<F, D<F>>,
171
172 #[serde_as(as = "Option<o1_utils::serialization::SerdeAs>")]
174 pub range_check0_selector8: Option<E<F, D<F>>>,
175
176 #[serde_as(as = "Option<o1_utils::serialization::SerdeAs>")]
178 pub range_check1_selector8: Option<E<F, D<F>>>,
179
180 #[serde_as(as = "Option<o1_utils::serialization::SerdeAs>")]
182 pub foreign_field_add_selector8: Option<E<F, D<F>>>,
183
184 #[serde_as(as = "Option<o1_utils::serialization::SerdeAs>")]
186 pub foreign_field_mul_selector8: Option<E<F, D<F>>>,
187
188 #[serde_as(as = "Option<o1_utils::serialization::SerdeAs>")]
190 pub xor_selector8: Option<E<F, D<F>>>,
191
192 #[serde_as(as = "Option<o1_utils::serialization::SerdeAs>")]
194 pub rot_selector8: Option<E<F, D<F>>>,
195}
196
197#[serde_as]
198#[derive(Clone, Serialize, Debug)]
199pub struct ConstraintSystem<F: PrimeField> {
200 pub public: usize,
204 pub prev_challenges: usize,
206 #[serde(bound = "EvaluationDomains<F>: Serialize + DeserializeOwned")]
208 pub domain: EvaluationDomains<F>,
209 #[serde(bound = "CircuitGate<F>: Serialize + DeserializeOwned")]
211 pub gates: Arc<Vec<CircuitGate<F>>>,
212
213 pub zk_rows: u64,
214
215 pub feature_flags: FeatureFlags,
217
218 #[serde_as(as = "Vec<o1_utils::serialization::SerdeAs>")]
220 pub sid: Vec<F>,
221
222 #[serde_as(as = "[o1_utils::serialization::SerdeAs; PERMUTS]")]
224 pub shift: [F; PERMUTS],
225 #[serde_as(as = "o1_utils::serialization::SerdeAs")]
227 pub endo: F,
228 #[serde(bound = "LookupConstraintSystem<F>: Serialize + DeserializeOwned")]
230 pub lookup_constraint_system: Arc<LookupConstraintSystemCache<F>>,
231 #[serde(skip)]
233 pub(crate) precomputations: Arc<LazyCache<Arc<DomainConstantEvaluations<F>>>>,
234
235 pub disable_gates_checks: bool,
237}
238
239pub(crate) type LookupConstraintSystemCache<F> =
240 LazyCache<Result<Option<LookupConstraintSystem<F>>, LookupError>>;
241
242impl<'de, F> Deserialize<'de> for ConstraintSystem<F>
243where
244 F: PrimeField,
245 EvaluationDomains<F>: Serialize + DeserializeOwned,
246 CircuitGate<F>: Serialize + DeserializeOwned,
247 LookupConstraintSystem<F>: Serialize + DeserializeOwned,
248{
249 fn deserialize<D>(deserializer: D) -> Result<ConstraintSystem<F>, D::Error>
250 where
251 D: serde::Deserializer<'de>,
252 {
253 #[serde_as]
254 #[derive(Clone, Serialize, Deserialize, Debug)]
255 struct ConstraintSystemSerde<F: PrimeField> {
256 public: usize,
257 prev_challenges: usize,
258 #[serde(bound = "EvaluationDomains<F>: Serialize + DeserializeOwned")]
259 domain: EvaluationDomains<F>,
260 #[serde(bound = "CircuitGate<F>: Serialize + DeserializeOwned")]
261 gates: Arc<Vec<CircuitGate<F>>>,
262 zk_rows: u64,
263 feature_flags: FeatureFlags,
264 #[serde_as(as = "Vec<o1_utils::serialization::SerdeAs>")]
265 sid: Vec<F>,
266 #[serde_as(as = "[o1_utils::serialization::SerdeAs; PERMUTS]")]
267 shift: [F; PERMUTS],
268 #[serde_as(as = "o1_utils::serialization::SerdeAs")]
269 endo: F,
270 #[serde(bound = "LookupConstraintSystem<F>: Serialize + DeserializeOwned")]
271 lookup_constraint_system: Arc<LookupConstraintSystemCache<F>>,
272 disable_gates_checks: bool,
273 }
274
275 let cs = ConstraintSystemSerde::<F>::deserialize(deserializer)?;
277
278 let precomputations = Arc::new({
279 #[cfg(feature = "std")]
280 {
281 LazyCache::new(move || {
282 Arc::new(DomainConstantEvaluations::create(cs.domain, cs.zk_rows).unwrap())
283 })
284 }
285 #[cfg(not(feature = "std"))]
286 {
287 LazyCache::preinit(Arc::new(
288 DomainConstantEvaluations::create(cs.domain, cs.zk_rows).unwrap(),
289 ))
290 }
291 });
292
293 Ok(ConstraintSystem {
294 public: cs.public,
295 prev_challenges: cs.prev_challenges,
296 domain: cs.domain,
297 gates: cs.gates,
298 zk_rows: cs.zk_rows,
299 feature_flags: cs.feature_flags,
300 sid: cs.sid,
301 shift: cs.shift,
302 endo: cs.endo,
303 lookup_constraint_system: cs.lookup_constraint_system,
304 disable_gates_checks: cs.disable_gates_checks,
305 precomputations,
306 })
307 }
308}
309
310#[derive(Debug)]
312pub enum GateError {
313 DisconnectedWires(Wire, Wire),
315 IncorrectPublic(usize),
317 Custom { row: usize, err: String },
319}
320
321pub struct Builder<F: PrimeField> {
322 gates: Vec<CircuitGate<F>>,
323 public: usize,
324 prev_challenges: usize,
325 lookup_tables: Vec<LookupTable<F>>,
326 runtime_tables: Option<Vec<RuntimeTableCfg<F>>>,
327 precomputations: Option<Arc<DomainConstantEvaluations<F>>>,
328 disable_gates_checks: bool,
329 max_poly_size: Option<usize>,
330 lazy_mode: bool,
331}
332
333pub fn selector_polynomial<F: PrimeField>(
335 gate_type: GateType,
336 gates: &[CircuitGate<F>],
337 domain: &EvaluationDomains<F>,
338 target_domain: &D<F>,
339 disable_gates_checks: bool,
340) -> E<F, D<F>> {
341 if cfg!(debug_assertions) && disable_gates_checks {
342 DP::<F>::zero().evaluate_over_domain_by_ref(*target_domain)
343 } else {
344 let coeff = E::<F, D<F>>::from_vec_and_domain(
346 gates
347 .iter()
348 .map(|gate| {
349 if gate.typ == gate_type {
350 F::one()
351 } else {
352 F::zero()
353 }
354 })
355 .collect(),
356 domain.d1,
357 )
358 .interpolate();
359
360 coeff.evaluate_over_domain_by_ref(*target_domain)
361 }
362}
363
364impl<F: PrimeField> ConstraintSystem<F> {
365 pub fn create(gates: Vec<CircuitGate<F>>) -> Builder<F> {
381 Builder {
382 gates,
383 public: 0,
384 prev_challenges: 0,
385 lookup_tables: vec![],
386 runtime_tables: None,
387 precomputations: None,
388 disable_gates_checks: false,
389 max_poly_size: None,
390 lazy_mode: false,
391 }
392 }
393
394 pub fn precomputations(&self) -> Arc<DomainConstantEvaluations<F>> {
395 self.precomputations.get().clone()
396 }
397
398 pub fn for_testing(gates: Vec<CircuitGate<F>>) -> Self {
400 let public = 0;
401 ConstraintSystem::<F>::create(gates)
403 .public(public)
404 .build()
405 .unwrap()
406 }
407
408 pub fn fp_for_testing(gates: Vec<CircuitGate<F>>) -> Self {
409 Self::for_testing(gates)
410 }
411}
412
413impl<F: PrimeField> ConstraintSystem<F> {
414 pub fn verify_witness<
417 const FULL_ROUNDS: usize,
418 G: KimchiCurve<FULL_ROUNDS, ScalarField = F>,
419 >(
420 &self,
421 witness: &[Vec<F>; COLUMNS],
422 public: &[F],
423 ) -> Result<(), GateError> {
424 let pad = vec![F::zero(); self.domain.d1.size() - witness[0].len()];
426 let witness: [Vec<F>; COLUMNS] = array::from_fn(|i| {
427 let mut w = witness[i].to_vec();
428 w.extend_from_slice(&pad);
429 w
430 });
431
432 for (row, gate) in self.gates.iter().enumerate() {
434 for col in 0..PERMUTS {
436 let wire = gate.wires[col];
437
438 if wire.col >= PERMUTS {
439 return Err(GateError::Custom {
440 row,
441 err: format!("a wire can only be connected to the first {PERMUTS} columns"),
442 });
443 }
444
445 if witness[col][row] != witness[wire.col][wire.row] {
446 return Err(GateError::DisconnectedWires(
447 Wire { col, row },
448 Wire {
449 col: wire.col,
450 row: wire.row,
451 },
452 ));
453 }
454 }
455
456 if row < self.public && gate.coeffs.first() != Some(&F::one()) {
458 return Err(GateError::IncorrectPublic(row));
459 }
460
461 gate.verify::<FULL_ROUNDS, G>(row, &witness, self, public)
463 .map_err(|err| GateError::Custom { row, err })?;
464 }
465
466 Ok(())
468 }
469}
470
471#[cfg(feature = "prover")]
472impl<const FULL_ROUNDS: usize, F, G, Srs> ProverIndex<FULL_ROUNDS, G, Srs>
473where
474 F: PrimeField,
475 G: KimchiCurve<FULL_ROUNDS, ScalarField = F>,
476 Srs: SRS<G>,
477{
478 pub fn verify(&self, witness: &[Vec<F>; COLUMNS], public: &[F]) -> Result<(), GateError> {
481 self.cs.verify_witness::<FULL_ROUNDS, G>(witness, public)
482 }
483}
484
485#[cfg(feature = "prover")]
486impl<F: PrimeField> ConstraintSystem<F> {
487 pub fn evaluate(&self, w: &[DP<F>; COLUMNS], z: &DP<F>) -> WitnessOverDomains<F> {
489 let (w8, z8): ([E<F, D<F>>; COLUMNS], _) = {
491 let mut res = w
492 .par_iter()
493 .chain(rayon::iter::once(z))
494 .map(|elem| elem.evaluate_over_domain_by_ref(self.domain.d8))
495 .collect::<Vec<_>>();
496 let z8 = res[COLUMNS].clone();
497 res.truncate(COLUMNS);
498 (res.try_into().unwrap(), z8)
499 };
500
501 WitnessOverDomains {
502 this: WitnessEvals {
503 w: w8,
504 z: z8.clone(),
505 },
506 z_next: z8.shift(8),
507 }
508 }
509
510 pub(crate) fn evaluated_column_coefficients(&self) -> EvaluatedColumnCoefficients<F> {
511 let shifts = Shifts::new(&self.domain.d1);
513
514 let n = self.domain.d1.size();
515
516 let mut sigmal1: [Vec<F>; PERMUTS] = array::from_fn(|_| vec![F::zero(); n]);
517
518 for (row, gate) in self.gates.iter().enumerate() {
519 for (cell, sigma) in gate.wires.iter().zip(sigmal1.iter_mut()) {
520 sigma[row] = shifts.cell_to_field(cell);
521 }
522 }
523
524 for row in n + 2 - (self.zk_rows as usize)..n - 1 {
527 for sigma in sigmal1.iter_mut() {
528 sigma[row] = F::zero();
529 }
530 }
531
532 let sigmal1: [_; PERMUTS] = {
533 let [s0, s1, s2, s3, s4, s5, s6] = sigmal1;
534 [
535 E::<F, D<F>>::from_vec_and_domain(s0, self.domain.d1),
536 E::<F, D<F>>::from_vec_and_domain(s1, self.domain.d1),
537 E::<F, D<F>>::from_vec_and_domain(s2, self.domain.d1),
538 E::<F, D<F>>::from_vec_and_domain(s3, self.domain.d1),
539 E::<F, D<F>>::from_vec_and_domain(s4, self.domain.d1),
540 E::<F, D<F>>::from_vec_and_domain(s5, self.domain.d1),
541 E::<F, D<F>>::from_vec_and_domain(s6, self.domain.d1),
542 ]
543 };
544
545 let permutation_coefficients: [DP<F>; PERMUTS] =
546 array::from_fn(|i| sigmal1[i].clone().interpolate());
547
548 let poseidon_selector = E::<F, D<F>>::from_vec_and_domain(
550 self.gates.iter().map(|gate| gate.ps()).collect(),
551 self.domain.d1,
552 )
553 .interpolate();
554
555 let generic_selector = E::<F, D<F>>::from_vec_and_domain(
557 self.gates
558 .iter()
559 .map(|gate| {
560 if matches!(gate.typ, GateType::Generic) {
561 F::one()
562 } else {
563 F::zero()
564 }
565 })
566 .collect(),
567 self.domain.d1,
568 )
569 .interpolate();
570
571 let coefficients: [_; COLUMNS] = array::from_fn(|i| {
573 let padded = self
574 .gates
575 .iter()
576 .map(|gate| gate.coeffs.get(i).cloned().unwrap_or_else(F::zero))
577 .collect();
578 let eval = E::from_vec_and_domain(padded, self.domain.d1);
579 eval.interpolate()
580 });
581
582 EvaluatedColumnCoefficients {
583 permutation_coefficients,
584 coefficients,
585 generic_selector,
586 poseidon_selector,
587 }
588 }
589
590 pub(crate) fn column_evaluations(
591 &self,
592 evaluated_column_coefficients: &EvaluatedColumnCoefficients<F>,
593 ) -> ColumnEvaluations<F> {
594 let permutation_coefficients8 = array::from_fn(|i| {
595 evaluated_column_coefficients.permutation_coefficients[i]
596 .evaluate_over_domain_by_ref(self.domain.d8)
597 });
598
599 let poseidon_selector8 = evaluated_column_coefficients
600 .poseidon_selector
601 .evaluate_over_domain_by_ref(self.domain.d8);
602
603 let complete_add_selector4 = selector_polynomial(
605 GateType::CompleteAdd,
606 &self.gates,
607 &self.domain,
608 &self.domain.d4,
609 self.disable_gates_checks,
610 );
611
612 let mul_selector8 = selector_polynomial(
613 GateType::VarBaseMul,
614 &self.gates,
615 &self.domain,
616 &self.domain.d8,
617 self.disable_gates_checks,
618 );
619
620 let emul_selector8 = selector_polynomial(
621 GateType::EndoMul,
622 &self.gates,
623 &self.domain,
624 &self.domain.d8,
625 self.disable_gates_checks,
626 );
627
628 let endomul_scalar_selector8 = selector_polynomial(
629 GateType::EndoMulScalar,
630 &self.gates,
631 &self.domain,
632 &self.domain.d8,
633 self.disable_gates_checks,
634 );
635
636 let generic_selector4 = evaluated_column_coefficients
637 .generic_selector
638 .evaluate_over_domain_by_ref(self.domain.d4);
639
640 let range_check0_selector8 = {
642 if !self.feature_flags.range_check0 {
643 None
644 } else {
645 Some(selector_polynomial(
646 GateType::RangeCheck0,
647 &self.gates,
648 &self.domain,
649 &self.domain.d8,
650 self.disable_gates_checks,
651 ))
652 }
653 };
654
655 let range_check1_selector8 = {
657 if !self.feature_flags.range_check1 {
658 None
659 } else {
660 Some(selector_polynomial(
661 GateType::RangeCheck1,
662 &self.gates,
663 &self.domain,
664 &self.domain.d8,
665 self.disable_gates_checks,
666 ))
667 }
668 };
669
670 let foreign_field_add_selector8 = {
672 if !self.feature_flags.foreign_field_add {
673 None
674 } else {
675 Some(selector_polynomial(
676 GateType::ForeignFieldAdd,
677 &self.gates,
678 &self.domain,
679 &self.domain.d8,
680 self.disable_gates_checks,
681 ))
682 }
683 };
684
685 let foreign_field_mul_selector8 = {
687 if !self.feature_flags.foreign_field_mul {
688 None
689 } else {
690 Some(selector_polynomial(
691 GateType::ForeignFieldMul,
692 &self.gates,
693 &self.domain,
694 &self.domain.d8,
695 self.disable_gates_checks,
696 ))
697 }
698 };
699
700 let xor_selector8 = {
701 if !self.feature_flags.xor {
702 None
703 } else {
704 Some(selector_polynomial(
705 GateType::Xor16,
706 &self.gates,
707 &self.domain,
708 &self.domain.d8,
709 self.disable_gates_checks,
710 ))
711 }
712 };
713
714 let rot_selector8 = {
715 if !self.feature_flags.rot {
716 None
717 } else {
718 Some(selector_polynomial(
719 GateType::Rot64,
720 &self.gates,
721 &self.domain,
722 &self.domain.d8,
723 self.disable_gates_checks,
724 ))
725 }
726 };
727
728 let coefficients8 = array::from_fn(|i| {
730 evaluated_column_coefficients.coefficients[i]
731 .evaluate_over_domain_by_ref(self.domain.d8)
732 });
733
734 ColumnEvaluations {
735 permutation_coefficients8,
736 coefficients8,
737 generic_selector4,
738 poseidon_selector8,
739 complete_add_selector4,
740 mul_selector8,
741 emul_selector8,
742 endomul_scalar_selector8,
743 range_check0_selector8,
744 range_check1_selector8,
745 foreign_field_add_selector8,
746 foreign_field_mul_selector8,
747 xor_selector8,
748 rot_selector8,
749 }
750 }
751}
752
753pub const NUM_CHUNKS_BY_DEFAULT: usize = 1;
755
756pub const ZK_ROWS_BY_DEFAULT: u64 = 3;
758
759pub fn zk_rows_strict_lower_bound(num_chunks: usize) -> usize {
770 (2 * (PERMUTS + 1) * num_chunks - 2) / PERMUTS
771}
772
773impl FeatureFlags {
774 pub fn from_gates_and_lookup_features<F: PrimeField>(
776 gates: &[CircuitGate<F>],
777 lookup_features: LookupFeatures,
778 ) -> FeatureFlags {
779 let mut feature_flags = FeatureFlags {
780 range_check0: false,
781 range_check1: false,
782 lookup_features,
783 foreign_field_add: false,
784 foreign_field_mul: false,
785 xor: false,
786 rot: false,
787 };
788
789 for gate in gates {
790 match gate.typ {
791 GateType::RangeCheck0 => feature_flags.range_check0 = true,
792 GateType::RangeCheck1 => feature_flags.range_check1 = true,
793 GateType::ForeignFieldAdd => feature_flags.foreign_field_add = true,
794 GateType::ForeignFieldMul => feature_flags.foreign_field_mul = true,
795 GateType::Xor16 => feature_flags.xor = true,
796 GateType::Rot64 => feature_flags.rot = true,
797 _ => (),
798 }
799 }
800
801 feature_flags
802 }
803
804 pub fn from_gates<F: PrimeField>(
809 gates: &[CircuitGate<F>],
810 uses_runtime_tables: bool,
811 ) -> FeatureFlags {
812 FeatureFlags::from_gates_and_lookup_features(
813 gates,
814 LookupFeatures::from_gates(gates, uses_runtime_tables),
815 )
816 }
817}
818
819impl<F: PrimeField> Builder<F> {
820 pub fn public(mut self, public: usize) -> Self {
823 self.public = public;
824 self
825 }
826
827 pub fn prev_challenges(mut self, prev_challenges: usize) -> Self {
830 self.prev_challenges = prev_challenges;
831 self
832 }
833
834 pub fn lookup(mut self, lookup_tables: Vec<LookupTable<F>>) -> Self {
843 self.lookup_tables = lookup_tables;
844 self
845 }
846
847 pub fn runtime(mut self, runtime_tables: Option<Vec<RuntimeTableCfg<F>>>) -> Self {
855 self.runtime_tables = runtime_tables;
856 self
857 }
858
859 pub fn shared_precomputations(
862 mut self,
863 shared_precomputations: Arc<DomainConstantEvaluations<F>>,
864 ) -> Self {
865 self.precomputations = Some(shared_precomputations);
866 self
867 }
868
869 pub fn disable_gates_checks(mut self, disable_gates_checks: bool) -> Self {
871 self.disable_gates_checks = disable_gates_checks;
872 self
873 }
874
875 pub fn max_poly_size(mut self, max_poly_size: Option<usize>) -> Self {
876 self.max_poly_size = max_poly_size;
877 self
878 }
879
880 pub fn lazy_mode(mut self, lazy_mode: bool) -> Self {
881 self.lazy_mode = lazy_mode;
882 self
883 }
884
885 pub fn build(self) -> Result<ConstraintSystem<F>, SetupError> {
887 let mut gates = self.gates;
888 let lookup_tables = self.lookup_tables.clone();
889 let runtime_tables = self.runtime_tables.clone();
890
891 assert!(gates.len() > 1);
894
895 let feature_flags = FeatureFlags::from_gates(&gates, runtime_tables.is_some());
896
897 let lookup_domain_size = {
898 let mut has_table_with_id_0 = false;
900 let mut lookup_domain_size: usize = lookup_tables
901 .iter()
902 .map(|LookupTable { id, data }| {
903 if *id == 0_i32 {
905 has_table_with_id_0 = true
906 }
907 if data.is_empty() {
908 0
909 } else {
910 data[0].len()
911 }
912 })
913 .sum();
914 if let Some(runtime_tables) = &runtime_tables {
916 for runtime_table in runtime_tables.iter() {
919 lookup_domain_size += runtime_table.len();
920 }
921 }
922 let LookupFeatures { patterns, .. } = &feature_flags.lookup_features;
924 let mut gate_lookup_tables = GateLookupTables {
925 xor: false,
926 range_check: false,
927 };
928 for pattern in patterns.into_iter() {
929 if let Some(gate_table) = pattern.table() {
930 gate_lookup_tables[gate_table] = true
931 }
932 }
933 for gate_table in gate_lookup_tables.into_iter() {
934 lookup_domain_size += gate_table.table_size();
935 }
936
937 if has_table_with_id_0 {
940 lookup_domain_size
941 } else {
942 lookup_domain_size + 1
943 }
944 };
945
946 let (zk_rows, domain_size_lower_bound) = {
970 let circuit_lower_bound = core::cmp::max(gates.len(), lookup_domain_size + 1);
974 let get_domain_size_lower_bound = |zk_rows: u64| circuit_lower_bound + zk_rows as usize;
975
976 let mut zk_rows = 3;
977 let mut domain_size_lower_bound = get_domain_size_lower_bound(zk_rows);
978 if let Some(max_poly_size) = self.max_poly_size {
979 while {
985 let domain_size = D::<F>::compute_size_of_domain(domain_size_lower_bound)
986 .ok_or(SetupError::DomainCreation(
987 DomainCreationError::DomainSizeFailed(domain_size_lower_bound),
988 ))?;
989 let num_chunks = if domain_size < max_poly_size {
990 1
991 } else {
992 domain_size / max_poly_size
993 };
994 zk_rows = (zk_rows_strict_lower_bound(num_chunks) + 1) as u64;
995 domain_size_lower_bound = get_domain_size_lower_bound(zk_rows);
996 domain_size < domain_size_lower_bound
997 } {}
998 }
999 (zk_rows, domain_size_lower_bound)
1000 };
1001
1002 let domain = EvaluationDomains::<F>::create(domain_size_lower_bound)
1006 .map_err(SetupError::DomainCreation)?;
1007
1008 assert!(domain.d1.size > zk_rows);
1009
1010 let d1_size = domain.d1.size();
1012 let mut padding = (gates.len()..d1_size)
1013 .map(|i| {
1014 CircuitGate::<F>::zero(array::from_fn(|j| Wire {
1015 col: WIRES[j],
1016 row: i,
1017 }))
1018 })
1019 .collect();
1020 gates.append(&mut padding);
1021
1022 let shifts = Shifts::new(&domain.d1);
1024
1025 let gates = Arc::new(gates);
1029 let gates_clone = Arc::clone(&gates);
1030 let lookup_constraint_system = {
1031 #[cfg(feature = "std")]
1032 {
1033 LazyCache::new(move || {
1034 LookupConstraintSystem::create(
1035 &gates_clone,
1036 self.lookup_tables,
1037 self.runtime_tables,
1038 &domain,
1039 zk_rows as usize,
1040 )
1041 })
1042 }
1043 #[cfg(not(feature = "std"))]
1044 {
1045 LazyCache::preinit(LookupConstraintSystem::create(
1046 &gates_clone,
1047 self.lookup_tables,
1048 self.runtime_tables,
1049 &domain,
1050 zk_rows as usize,
1051 ))
1052 }
1053 };
1054 if !self.lazy_mode {
1055 lookup_constraint_system
1057 .try_get_or_err()
1058 .map_err(SetupError::from)?;
1059 }
1060
1061 let sid = shifts.map[0].clone();
1062
1063 let endo = F::zero();
1065
1066 let precomputations = if !self.lazy_mode {
1067 match self.precomputations {
1068 Some(t) => LazyCache::preinit(t),
1069 None => LazyCache::preinit(Arc::new(
1070 DomainConstantEvaluations::create(domain, zk_rows).unwrap(),
1071 )),
1072 }
1073 } else {
1074 #[cfg(feature = "std")]
1075 {
1076 LazyCache::new(move || {
1077 Arc::new(DomainConstantEvaluations::create(domain, zk_rows).unwrap())
1078 })
1079 }
1080 #[cfg(not(feature = "std"))]
1081 {
1082 LazyCache::preinit(Arc::new(
1083 DomainConstantEvaluations::create(domain, zk_rows).unwrap(),
1084 ))
1085 }
1086 };
1087
1088 let constraints = ConstraintSystem {
1089 domain,
1090 public: self.public,
1091 prev_challenges: self.prev_challenges,
1092 sid,
1093 gates,
1094 shift: shifts.shifts,
1095 endo,
1096 zk_rows,
1097 lookup_constraint_system: Arc::new(lookup_constraint_system),
1099 feature_flags,
1100 precomputations: Arc::new(precomputations),
1101 disable_gates_checks: self.disable_gates_checks,
1102 };
1103
1104 Ok(constraints)
1105 }
1106}
1107
1108pub(crate) mod testing {
1111 use super::ConstraintSystem;
1112 use crate::circuits::{
1113 gate::CircuitGate,
1114 lookup::{runtime_tables::RuntimeTableCfg, tables::LookupTable},
1115 };
1116 use alloc::vec::Vec;
1117 use ark_ff::PrimeField;
1118
1119 #[allow(clippy::too_many_arguments)]
1120 #[allow(dead_code)]
1121 pub(crate) fn create_constraint_system<F: PrimeField>(
1122 gates: Vec<CircuitGate<F>>,
1123 public: usize,
1124 prev_challenges: usize,
1125 lookup_tables: Vec<LookupTable<F>>,
1126 runtime_tables: Option<Vec<RuntimeTableCfg<F>>>,
1127 disable_gates_checks: bool,
1128 override_srs_size: Option<usize>,
1129 lazy_mode: bool,
1130 ) -> ConstraintSystem<F> {
1131 ConstraintSystem::<F>::create(gates)
1132 .lookup(lookup_tables)
1133 .runtime(runtime_tables)
1134 .public(public)
1135 .prev_challenges(prev_challenges)
1136 .disable_gates_checks(disable_gates_checks)
1137 .max_poly_size(override_srs_size)
1138 .lazy_mode(lazy_mode)
1139 .build()
1140 .unwrap()
1141 }
1142}