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, WitnessShifts},
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 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 let w4: [E<F, D<F>>; COLUMNS] = (0..COLUMNS)
502 .into_par_iter()
503 .map(|i| {
504 E::<F, D<F>>::from_vec_and_domain(
505 (0..self.domain.d4.size)
506 .map(|j| w8[i].evals[2 * j as usize])
507 .collect(),
508 self.domain.d4,
509 )
510 })
511 .collect::<Vec<_>>()
512 .try_into()
513 .unwrap();
514
515 let z4 = DP::<F>::zero().evaluate_over_domain_by_ref(D::<F>::new(1).unwrap());
516 let z8_shift8 = z8.shift(8);
517
518 let d4_next_w: [_; COLUMNS] = w4
519 .par_iter()
520 .map(|w4_i| w4_i.shift(4))
521 .collect::<Vec<_>>()
522 .try_into()
523 .unwrap();
524
525 let d8_next_w: [_; COLUMNS] = w8
526 .par_iter()
527 .map(|w8_i| w8_i.shift(8))
528 .collect::<Vec<_>>()
529 .try_into()
530 .unwrap();
531
532 WitnessOverDomains {
533 d4: WitnessShifts {
534 next: WitnessEvals {
535 w: d4_next_w,
536 z: z4.clone(), },
539 this: WitnessEvals {
540 w: w4,
541 z: z4, },
543 },
544 d8: WitnessShifts {
545 next: WitnessEvals {
546 w: d8_next_w,
547 z: z8_shift8,
548 },
549 this: WitnessEvals { w: w8, z: z8 },
550 },
551 }
552 }
553
554 pub(crate) fn evaluated_column_coefficients(&self) -> EvaluatedColumnCoefficients<F> {
555 let shifts = Shifts::new(&self.domain.d1);
557
558 let n = self.domain.d1.size();
559
560 let mut sigmal1: [Vec<F>; PERMUTS] = array::from_fn(|_| vec![F::zero(); n]);
561
562 for (row, gate) in self.gates.iter().enumerate() {
563 for (cell, sigma) in gate.wires.iter().zip(sigmal1.iter_mut()) {
564 sigma[row] = shifts.cell_to_field(cell);
565 }
566 }
567
568 for row in n + 2 - (self.zk_rows as usize)..n - 1 {
571 for sigma in sigmal1.iter_mut() {
572 sigma[row] = F::zero();
573 }
574 }
575
576 let sigmal1: [_; PERMUTS] = {
577 let [s0, s1, s2, s3, s4, s5, s6] = sigmal1;
578 [
579 E::<F, D<F>>::from_vec_and_domain(s0, self.domain.d1),
580 E::<F, D<F>>::from_vec_and_domain(s1, self.domain.d1),
581 E::<F, D<F>>::from_vec_and_domain(s2, self.domain.d1),
582 E::<F, D<F>>::from_vec_and_domain(s3, self.domain.d1),
583 E::<F, D<F>>::from_vec_and_domain(s4, self.domain.d1),
584 E::<F, D<F>>::from_vec_and_domain(s5, self.domain.d1),
585 E::<F, D<F>>::from_vec_and_domain(s6, self.domain.d1),
586 ]
587 };
588
589 let permutation_coefficients: [DP<F>; PERMUTS] =
590 array::from_fn(|i| sigmal1[i].clone().interpolate());
591
592 let poseidon_selector = E::<F, D<F>>::from_vec_and_domain(
594 self.gates.iter().map(|gate| gate.ps()).collect(),
595 self.domain.d1,
596 )
597 .interpolate();
598
599 let generic_selector = E::<F, D<F>>::from_vec_and_domain(
601 self.gates
602 .iter()
603 .map(|gate| {
604 if matches!(gate.typ, GateType::Generic) {
605 F::one()
606 } else {
607 F::zero()
608 }
609 })
610 .collect(),
611 self.domain.d1,
612 )
613 .interpolate();
614
615 let coefficients: [_; COLUMNS] = array::from_fn(|i| {
617 let padded = self
618 .gates
619 .iter()
620 .map(|gate| gate.coeffs.get(i).cloned().unwrap_or_else(F::zero))
621 .collect();
622 let eval = E::from_vec_and_domain(padded, self.domain.d1);
623 eval.interpolate()
624 });
625
626 EvaluatedColumnCoefficients {
627 permutation_coefficients,
628 coefficients,
629 generic_selector,
630 poseidon_selector,
631 }
632 }
633
634 pub(crate) fn column_evaluations(
635 &self,
636 evaluated_column_coefficients: &EvaluatedColumnCoefficients<F>,
637 ) -> ColumnEvaluations<F> {
638 let permutation_coefficients8 = array::from_fn(|i| {
639 evaluated_column_coefficients.permutation_coefficients[i]
640 .evaluate_over_domain_by_ref(self.domain.d8)
641 });
642
643 let poseidon_selector8 = evaluated_column_coefficients
644 .poseidon_selector
645 .evaluate_over_domain_by_ref(self.domain.d8);
646
647 let complete_add_selector4 = selector_polynomial(
649 GateType::CompleteAdd,
650 &self.gates,
651 &self.domain,
652 &self.domain.d4,
653 self.disable_gates_checks,
654 );
655
656 let mul_selector8 = selector_polynomial(
657 GateType::VarBaseMul,
658 &self.gates,
659 &self.domain,
660 &self.domain.d8,
661 self.disable_gates_checks,
662 );
663
664 let emul_selector8 = selector_polynomial(
665 GateType::EndoMul,
666 &self.gates,
667 &self.domain,
668 &self.domain.d8,
669 self.disable_gates_checks,
670 );
671
672 let endomul_scalar_selector8 = selector_polynomial(
673 GateType::EndoMulScalar,
674 &self.gates,
675 &self.domain,
676 &self.domain.d8,
677 self.disable_gates_checks,
678 );
679
680 let generic_selector4 = evaluated_column_coefficients
681 .generic_selector
682 .evaluate_over_domain_by_ref(self.domain.d4);
683
684 let range_check0_selector8 = {
686 if !self.feature_flags.range_check0 {
687 None
688 } else {
689 Some(selector_polynomial(
690 GateType::RangeCheck0,
691 &self.gates,
692 &self.domain,
693 &self.domain.d8,
694 self.disable_gates_checks,
695 ))
696 }
697 };
698
699 let range_check1_selector8 = {
701 if !self.feature_flags.range_check1 {
702 None
703 } else {
704 Some(selector_polynomial(
705 GateType::RangeCheck1,
706 &self.gates,
707 &self.domain,
708 &self.domain.d8,
709 self.disable_gates_checks,
710 ))
711 }
712 };
713
714 let foreign_field_add_selector8 = {
716 if !self.feature_flags.foreign_field_add {
717 None
718 } else {
719 Some(selector_polynomial(
720 GateType::ForeignFieldAdd,
721 &self.gates,
722 &self.domain,
723 &self.domain.d8,
724 self.disable_gates_checks,
725 ))
726 }
727 };
728
729 let foreign_field_mul_selector8 = {
731 if !self.feature_flags.foreign_field_mul {
732 None
733 } else {
734 Some(selector_polynomial(
735 GateType::ForeignFieldMul,
736 &self.gates,
737 &self.domain,
738 &self.domain.d8,
739 self.disable_gates_checks,
740 ))
741 }
742 };
743
744 let xor_selector8 = {
745 if !self.feature_flags.xor {
746 None
747 } else {
748 Some(selector_polynomial(
749 GateType::Xor16,
750 &self.gates,
751 &self.domain,
752 &self.domain.d8,
753 self.disable_gates_checks,
754 ))
755 }
756 };
757
758 let rot_selector8 = {
759 if !self.feature_flags.rot {
760 None
761 } else {
762 Some(selector_polynomial(
763 GateType::Rot64,
764 &self.gates,
765 &self.domain,
766 &self.domain.d8,
767 self.disable_gates_checks,
768 ))
769 }
770 };
771
772 let coefficients8 = array::from_fn(|i| {
774 evaluated_column_coefficients.coefficients[i]
775 .evaluate_over_domain_by_ref(self.domain.d8)
776 });
777
778 ColumnEvaluations {
779 permutation_coefficients8,
780 coefficients8,
781 generic_selector4,
782 poseidon_selector8,
783 complete_add_selector4,
784 mul_selector8,
785 emul_selector8,
786 endomul_scalar_selector8,
787 range_check0_selector8,
788 range_check1_selector8,
789 foreign_field_add_selector8,
790 foreign_field_mul_selector8,
791 xor_selector8,
792 rot_selector8,
793 }
794 }
795}
796
797pub const NUM_CHUNKS_BY_DEFAULT: usize = 1;
799
800pub const ZK_ROWS_BY_DEFAULT: u64 = 3;
802
803pub fn zk_rows_strict_lower_bound(num_chunks: usize) -> usize {
814 (2 * (PERMUTS + 1) * num_chunks - 2) / PERMUTS
815}
816
817impl FeatureFlags {
818 pub fn from_gates_and_lookup_features<F: PrimeField>(
820 gates: &[CircuitGate<F>],
821 lookup_features: LookupFeatures,
822 ) -> FeatureFlags {
823 let mut feature_flags = FeatureFlags {
824 range_check0: false,
825 range_check1: false,
826 lookup_features,
827 foreign_field_add: false,
828 foreign_field_mul: false,
829 xor: false,
830 rot: false,
831 };
832
833 for gate in gates {
834 match gate.typ {
835 GateType::RangeCheck0 => feature_flags.range_check0 = true,
836 GateType::RangeCheck1 => feature_flags.range_check1 = true,
837 GateType::ForeignFieldAdd => feature_flags.foreign_field_add = true,
838 GateType::ForeignFieldMul => feature_flags.foreign_field_mul = true,
839 GateType::Xor16 => feature_flags.xor = true,
840 GateType::Rot64 => feature_flags.rot = true,
841 _ => (),
842 }
843 }
844
845 feature_flags
846 }
847
848 pub fn from_gates<F: PrimeField>(
853 gates: &[CircuitGate<F>],
854 uses_runtime_tables: bool,
855 ) -> FeatureFlags {
856 FeatureFlags::from_gates_and_lookup_features(
857 gates,
858 LookupFeatures::from_gates(gates, uses_runtime_tables),
859 )
860 }
861}
862
863impl<F: PrimeField> Builder<F> {
864 pub fn public(mut self, public: usize) -> Self {
867 self.public = public;
868 self
869 }
870
871 pub fn prev_challenges(mut self, prev_challenges: usize) -> Self {
874 self.prev_challenges = prev_challenges;
875 self
876 }
877
878 pub fn lookup(mut self, lookup_tables: Vec<LookupTable<F>>) -> Self {
887 self.lookup_tables = lookup_tables;
888 self
889 }
890
891 pub fn runtime(mut self, runtime_tables: Option<Vec<RuntimeTableCfg<F>>>) -> Self {
899 self.runtime_tables = runtime_tables;
900 self
901 }
902
903 pub fn shared_precomputations(
906 mut self,
907 shared_precomputations: Arc<DomainConstantEvaluations<F>>,
908 ) -> Self {
909 self.precomputations = Some(shared_precomputations);
910 self
911 }
912
913 pub fn disable_gates_checks(mut self, disable_gates_checks: bool) -> Self {
915 self.disable_gates_checks = disable_gates_checks;
916 self
917 }
918
919 pub fn max_poly_size(mut self, max_poly_size: Option<usize>) -> Self {
920 self.max_poly_size = max_poly_size;
921 self
922 }
923
924 pub fn lazy_mode(mut self, lazy_mode: bool) -> Self {
925 self.lazy_mode = lazy_mode;
926 self
927 }
928
929 pub fn build(self) -> Result<ConstraintSystem<F>, SetupError> {
931 let mut gates = self.gates;
932 let lookup_tables = self.lookup_tables.clone();
933 let runtime_tables = self.runtime_tables.clone();
934
935 assert!(gates.len() > 1);
938
939 let feature_flags = FeatureFlags::from_gates(&gates, runtime_tables.is_some());
940
941 let lookup_domain_size = {
942 let mut has_table_with_id_0 = false;
944 let mut lookup_domain_size: usize = lookup_tables
945 .iter()
946 .map(|LookupTable { id, data }| {
947 if *id == 0_i32 {
949 has_table_with_id_0 = true
950 }
951 if data.is_empty() {
952 0
953 } else {
954 data[0].len()
955 }
956 })
957 .sum();
958 if let Some(runtime_tables) = &runtime_tables {
960 for runtime_table in runtime_tables.iter() {
963 lookup_domain_size += runtime_table.len();
964 }
965 }
966 let LookupFeatures { patterns, .. } = &feature_flags.lookup_features;
968 let mut gate_lookup_tables = GateLookupTables {
969 xor: false,
970 range_check: false,
971 };
972 for pattern in patterns.into_iter() {
973 if let Some(gate_table) = pattern.table() {
974 gate_lookup_tables[gate_table] = true
975 }
976 }
977 for gate_table in gate_lookup_tables.into_iter() {
978 lookup_domain_size += gate_table.table_size();
979 }
980
981 if has_table_with_id_0 {
984 lookup_domain_size
985 } else {
986 lookup_domain_size + 1
987 }
988 };
989
990 let (zk_rows, domain_size_lower_bound) = {
1014 let circuit_lower_bound = core::cmp::max(gates.len(), lookup_domain_size + 1);
1018 let get_domain_size_lower_bound = |zk_rows: u64| circuit_lower_bound + zk_rows as usize;
1019
1020 let mut zk_rows = 3;
1021 let mut domain_size_lower_bound = get_domain_size_lower_bound(zk_rows);
1022 if let Some(max_poly_size) = self.max_poly_size {
1023 while {
1029 let domain_size = D::<F>::compute_size_of_domain(domain_size_lower_bound)
1030 .ok_or(SetupError::DomainCreation(
1031 DomainCreationError::DomainSizeFailed(domain_size_lower_bound),
1032 ))?;
1033 let num_chunks = if domain_size < max_poly_size {
1034 1
1035 } else {
1036 domain_size / max_poly_size
1037 };
1038 zk_rows = (zk_rows_strict_lower_bound(num_chunks) + 1) as u64;
1039 domain_size_lower_bound = get_domain_size_lower_bound(zk_rows);
1040 domain_size < domain_size_lower_bound
1041 } {}
1042 }
1043 (zk_rows, domain_size_lower_bound)
1044 };
1045
1046 let domain = EvaluationDomains::<F>::create(domain_size_lower_bound)
1050 .map_err(SetupError::DomainCreation)?;
1051
1052 assert!(domain.d1.size > zk_rows);
1053
1054 let d1_size = domain.d1.size();
1056 let mut padding = (gates.len()..d1_size)
1057 .map(|i| {
1058 CircuitGate::<F>::zero(array::from_fn(|j| Wire {
1059 col: WIRES[j],
1060 row: i,
1061 }))
1062 })
1063 .collect();
1064 gates.append(&mut padding);
1065
1066 let shifts = Shifts::new(&domain.d1);
1068
1069 let gates = Arc::new(gates);
1073 let gates_clone = Arc::clone(&gates);
1074 let lookup_constraint_system = {
1075 #[cfg(feature = "std")]
1076 {
1077 LazyCache::new(move || {
1078 LookupConstraintSystem::create(
1079 &gates_clone,
1080 self.lookup_tables,
1081 self.runtime_tables,
1082 &domain,
1083 zk_rows as usize,
1084 )
1085 })
1086 }
1087 #[cfg(not(feature = "std"))]
1088 {
1089 LazyCache::preinit(LookupConstraintSystem::create(
1090 &gates_clone,
1091 self.lookup_tables,
1092 self.runtime_tables,
1093 &domain,
1094 zk_rows as usize,
1095 ))
1096 }
1097 };
1098 if !self.lazy_mode {
1099 lookup_constraint_system
1101 .try_get_or_err()
1102 .map_err(SetupError::from)?;
1103 }
1104
1105 let sid = shifts.map[0].clone();
1106
1107 let endo = F::zero();
1109
1110 let precomputations = if !self.lazy_mode {
1111 match self.precomputations {
1112 Some(t) => LazyCache::preinit(t),
1113 None => LazyCache::preinit(Arc::new(
1114 DomainConstantEvaluations::create(domain, zk_rows).unwrap(),
1115 )),
1116 }
1117 } else {
1118 #[cfg(feature = "std")]
1119 {
1120 LazyCache::new(move || {
1121 Arc::new(DomainConstantEvaluations::create(domain, zk_rows).unwrap())
1122 })
1123 }
1124 #[cfg(not(feature = "std"))]
1125 {
1126 LazyCache::preinit(Arc::new(
1127 DomainConstantEvaluations::create(domain, zk_rows).unwrap(),
1128 ))
1129 }
1130 };
1131
1132 let constraints = ConstraintSystem {
1133 domain,
1134 public: self.public,
1135 prev_challenges: self.prev_challenges,
1136 sid,
1137 gates,
1138 shift: shifts.shifts,
1139 endo,
1140 zk_rows,
1141 lookup_constraint_system: Arc::new(lookup_constraint_system),
1143 feature_flags,
1144 precomputations: Arc::new(precomputations),
1145 disable_gates_checks: self.disable_gates_checks,
1146 };
1147
1148 Ok(constraints)
1149 }
1150}
1151
1152pub(crate) mod testing {
1155 use super::ConstraintSystem;
1156 use crate::circuits::{
1157 gate::CircuitGate,
1158 lookup::{runtime_tables::RuntimeTableCfg, tables::LookupTable},
1159 };
1160 use alloc::vec::Vec;
1161 use ark_ff::PrimeField;
1162
1163 #[allow(clippy::too_many_arguments)]
1164 #[allow(dead_code)]
1165 pub(crate) fn create_constraint_system<F: PrimeField>(
1166 gates: Vec<CircuitGate<F>>,
1167 public: usize,
1168 prev_challenges: usize,
1169 lookup_tables: Vec<LookupTable<F>>,
1170 runtime_tables: Option<Vec<RuntimeTableCfg<F>>>,
1171 disable_gates_checks: bool,
1172 override_srs_size: Option<usize>,
1173 lazy_mode: bool,
1174 ) -> ConstraintSystem<F> {
1175 ConstraintSystem::<F>::create(gates)
1176 .lookup(lookup_tables)
1177 .runtime(runtime_tables)
1178 .public(public)
1179 .prev_challenges(prev_challenges)
1180 .disable_gates_checks(disable_gates_checks)
1181 .max_poly_size(override_srs_size)
1182 .lazy_mode(lazy_mode)
1183 .build()
1184 .unwrap()
1185 }
1186}