1use crate::{
2 circuits::{
3 berkeley_columns,
4 berkeley_columns::BerkeleyChallengeTerm,
5 constraints::FeatureFlags,
6 domains::Domain,
7 gate::CurrOrNext,
8 lookup::lookups::{LookupPattern, LookupPatterns},
9 polynomials::{
10 foreign_field_common::KimchiForeignElement, permutation::eval_vanishes_on_last_n_rows,
11 },
12 },
13 collections::{HashMap, HashSet},
14 proof::PointEvaluations,
15};
16use alloc::{
17 boxed::Box,
18 format,
19 string::{String, ToString},
20 vec,
21 vec::Vec,
22};
23use ark_ff::{FftField, Field, One, PrimeField, Zero};
24use ark_poly::{
25 univariate::DensePolynomial, EvaluationDomain, Evaluations, Radix2EvaluationDomain as D,
26};
27use core::{
28 cmp::Ordering,
29 fmt,
30 fmt::{Debug, Display},
31 iter::FromIterator,
32 ops::{Add, AddAssign, Index, Mul, MulAssign, Neg, Sub},
33};
34use itertools::Itertools;
35use o1_utils::{field_helpers::pows, foreign_field::ForeignFieldHelpers, FieldHelpers};
36#[cfg(feature = "parallel")]
37use rayon::prelude::*;
38use serde::{Deserialize, Serialize};
39use thiserror::Error;
40use CurrOrNext::{Curr, Next};
41
42use self::constraints::ExprOps;
43
44#[derive(Debug, Error)]
45pub enum ExprError<Column> {
46 #[error("Empty stack")]
47 EmptyStack,
48
49 #[error("Lookup should not have been used")]
50 LookupShouldNotBeUsed,
51
52 #[error("Linearization failed (needed {0:?} evaluated at the {1:?} row")]
53 MissingEvaluation(Column, CurrOrNext),
54
55 #[error("Cannot get index evaluation {0:?} (should have been linearized away)")]
56 MissingIndexEvaluation(Column),
57
58 #[error("Linearization failed (too many unevaluated columns: {0:?}")]
59 FailedLinearization(Vec<Variable<Column>>),
60
61 #[error("runtime table not available")]
62 MissingRuntime,
63}
64
65pub trait AlphaChallengeTerm<'a>:
68 Copy + Clone + Debug + PartialEq + Eq + Serialize + Deserialize<'a> + Display
69{
70 const ALPHA: Self;
71}
72
73#[derive(Clone)]
75pub struct Constants<F: 'static> {
76 pub endo_coefficient: F,
78 pub mds: &'static [[F; 3]; 3],
80 pub zk_rows: u64,
82}
83
84pub trait ColumnEnvironment<
85 'a,
86 F: FftField,
87 ChallengeTerm,
88 Challenges: Index<ChallengeTerm, Output = F>,
89>
90{
91 type Column;
97
98 fn get_column(&self, col: &Self::Column) -> Option<&'a Evaluations<F, D<F>>>;
100
101 fn column_domain(&self, col: &Self::Column) -> Domain;
103
104 fn get_domain(&self, d: Domain) -> D<F>;
105
106 fn get_constants(&self) -> &Constants<F>;
110
111 fn get_challenges(&self) -> &Challenges;
113
114 fn vanishes_on_zero_knowledge_and_previous_rows(&self) -> &'a Evaluations<F, D<F>>;
115
116 fn l0_1(&self) -> F;
119}
120
121pub fn l0_1<F: FftField>(d: D<F>) -> F {
134 d.elements()
135 .skip(1)
136 .fold(F::one(), |acc, omega_j| acc * (F::one() - omega_j))
137}
138
139pub fn unnormalized_lagrange_basis<F: FftField>(domain: &D<F>, i: i32, pt: &F) -> F {
141 let omega_i = if i < 0 {
142 domain.group_gen.pow([-i as u64]).inverse().unwrap()
143 } else {
144 domain.group_gen.pow([i as u64])
145 };
146 domain.evaluate_vanishing_polynomial(*pt) / (*pt - omega_i)
147}
148
149#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
150pub struct Variable<Column> {
153 pub col: Column,
155 pub row: CurrOrNext,
157}
158
159#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
169pub enum ConstantTerm<F> {
170 EndoCoefficient,
171 Mds { row: usize, col: usize },
172 Literal(F),
173}
174
175pub trait Literal: Sized + Clone {
176 type F;
177
178 fn literal(x: Self::F) -> Self;
179
180 fn to_literal(self) -> Result<Self::F, Self>;
181
182 fn to_literal_ref(&self) -> Option<&Self::F>;
183
184 fn as_literal(&self, constants: &Constants<Self::F>) -> Self;
188}
189
190impl<F: Field> Literal for F {
191 type F = F;
192
193 fn literal(x: Self::F) -> Self {
194 x
195 }
196
197 fn to_literal(self) -> Result<Self::F, Self> {
198 Ok(self)
199 }
200
201 fn to_literal_ref(&self) -> Option<&Self::F> {
202 Some(self)
203 }
204
205 fn as_literal(&self, _constants: &Constants<Self::F>) -> Self {
206 *self
207 }
208}
209
210impl<F: Clone> Literal for ConstantTerm<F> {
211 type F = F;
212 fn literal(x: Self::F) -> Self {
213 ConstantTerm::Literal(x)
214 }
215 fn to_literal(self) -> Result<Self::F, Self> {
216 match self {
217 ConstantTerm::Literal(x) => Ok(x),
218 x => Err(x),
219 }
220 }
221 fn to_literal_ref(&self) -> Option<&Self::F> {
222 match self {
223 ConstantTerm::Literal(x) => Some(x),
224 _ => None,
225 }
226 }
227 fn as_literal(&self, constants: &Constants<Self::F>) -> Self {
228 match self {
229 ConstantTerm::EndoCoefficient => {
230 ConstantTerm::Literal(constants.endo_coefficient.clone())
231 }
232 ConstantTerm::Mds { row, col } => {
233 ConstantTerm::Literal(constants.mds[*row][*col].clone())
234 }
235 ConstantTerm::Literal(_) => self.clone(),
236 }
237 }
238}
239
240#[derive(Clone, Debug, PartialEq)]
241pub enum ConstantExprInner<F, ChallengeTerm> {
242 Challenge(ChallengeTerm),
243 Constant(ConstantTerm<F>),
244}
245
246impl<'a, F: Clone, ChallengeTerm: AlphaChallengeTerm<'a>> Literal
247 for ConstantExprInner<F, ChallengeTerm>
248{
249 type F = F;
250 fn literal(x: Self::F) -> Self {
251 Self::Constant(ConstantTerm::literal(x))
252 }
253 fn to_literal(self) -> Result<Self::F, Self> {
254 match self {
255 Self::Constant(x) => match x.to_literal() {
256 Ok(x) => Ok(x),
257 Err(x) => Err(Self::Constant(x)),
258 },
259 x => Err(x),
260 }
261 }
262 fn to_literal_ref(&self) -> Option<&Self::F> {
263 match self {
264 Self::Constant(x) => x.to_literal_ref(),
265 _ => None,
266 }
267 }
268 fn as_literal(&self, constants: &Constants<Self::F>) -> Self {
269 match self {
270 Self::Constant(x) => Self::Constant(x.as_literal(constants)),
271 Self::Challenge(_) => self.clone(),
272 }
273 }
274}
275
276impl<'a, F, ChallengeTerm: AlphaChallengeTerm<'a>> From<ChallengeTerm>
277 for ConstantExprInner<F, ChallengeTerm>
278{
279 fn from(x: ChallengeTerm) -> Self {
280 ConstantExprInner::Challenge(x)
281 }
282}
283
284impl<F, ChallengeTerm> From<ConstantTerm<F>> for ConstantExprInner<F, ChallengeTerm> {
285 fn from(x: ConstantTerm<F>) -> Self {
286 ConstantExprInner::Constant(x)
287 }
288}
289
290#[derive(Clone, Debug, PartialEq, Eq, Hash)]
291pub enum Operations<T> {
292 Atom(T),
293 Pow(Box<Self>, u64),
294 Add(Box<Self>, Box<Self>),
295 Mul(Box<Self>, Box<Self>),
296 Sub(Box<Self>, Box<Self>),
297 Double(Box<Self>),
298 Square(Box<Self>),
299 Cache(CacheId, Box<Self>),
300 IfFeature(FeatureFlag, Box<Self>, Box<Self>),
301}
302
303impl<T> From<T> for Operations<T> {
304 fn from(x: T) -> Self {
305 Operations::Atom(x)
306 }
307}
308
309impl<T: Literal + Clone> Literal for Operations<T> {
310 type F = T::F;
311
312 fn literal(x: Self::F) -> Self {
313 Self::Atom(T::literal(x))
314 }
315
316 fn to_literal(self) -> Result<Self::F, Self> {
317 match self {
318 Self::Atom(x) => match x.to_literal() {
319 Ok(x) => Ok(x),
320 Err(x) => Err(Self::Atom(x)),
321 },
322 x => Err(x),
323 }
324 }
325
326 fn to_literal_ref(&self) -> Option<&Self::F> {
327 match self {
328 Self::Atom(x) => x.to_literal_ref(),
329 _ => None,
330 }
331 }
332
333 fn as_literal(&self, constants: &Constants<Self::F>) -> Self {
334 match self {
335 Self::Atom(x) => Self::Atom(x.as_literal(constants)),
336 Self::Pow(x, n) => Self::Pow(Box::new(x.as_literal(constants)), *n),
337 Self::Add(x, y) => Self::Add(
338 Box::new(x.as_literal(constants)),
339 Box::new(y.as_literal(constants)),
340 ),
341 Self::Mul(x, y) => Self::Mul(
342 Box::new(x.as_literal(constants)),
343 Box::new(y.as_literal(constants)),
344 ),
345 Self::Sub(x, y) => Self::Sub(
346 Box::new(x.as_literal(constants)),
347 Box::new(y.as_literal(constants)),
348 ),
349 Self::Double(x) => Self::Double(Box::new(x.as_literal(constants))),
350 Self::Square(x) => Self::Square(Box::new(x.as_literal(constants))),
351 Self::Cache(id, x) => Self::Cache(*id, Box::new(x.as_literal(constants))),
352 Self::IfFeature(flag, if_true, if_false) => Self::IfFeature(
353 *flag,
354 Box::new(if_true.as_literal(constants)),
355 Box::new(if_false.as_literal(constants)),
356 ),
357 }
358 }
359}
360
361pub type ConstantExpr<F, ChallengeTerm> = Operations<ConstantExprInner<F, ChallengeTerm>>;
362
363impl<F, ChallengeTerm> From<ConstantTerm<F>> for ConstantExpr<F, ChallengeTerm> {
364 fn from(x: ConstantTerm<F>) -> Self {
365 ConstantExprInner::from(x).into()
366 }
367}
368
369impl<'a, F, ChallengeTerm: AlphaChallengeTerm<'a>> From<ChallengeTerm>
370 for ConstantExpr<F, ChallengeTerm>
371{
372 fn from(x: ChallengeTerm) -> Self {
373 ConstantExprInner::from(x).into()
374 }
375}
376
377impl<F: Copy, ChallengeTerm: Copy> ConstantExprInner<F, ChallengeTerm> {
378 fn to_polish<Column>(
379 &self,
380 _cache: &mut HashMap<CacheId, usize>,
381 res: &mut Vec<PolishToken<F, Column, ChallengeTerm>>,
382 ) {
383 match self {
384 ConstantExprInner::Challenge(chal) => res.push(PolishToken::Challenge(*chal)),
385 ConstantExprInner::Constant(c) => res.push(PolishToken::Constant(*c)),
386 }
387 }
388}
389
390impl<F: Copy, ChallengeTerm: Copy> Operations<ConstantExprInner<F, ChallengeTerm>> {
391 fn to_polish<Column>(
392 &self,
393 cache: &mut HashMap<CacheId, usize>,
394 res: &mut Vec<PolishToken<F, Column, ChallengeTerm>>,
395 ) {
396 match self {
397 Operations::Atom(atom) => atom.to_polish(cache, res),
398 Operations::Add(x, y) => {
399 x.as_ref().to_polish(cache, res);
400 y.as_ref().to_polish(cache, res);
401 res.push(PolishToken::Add)
402 }
403 Operations::Mul(x, y) => {
404 x.as_ref().to_polish(cache, res);
405 y.as_ref().to_polish(cache, res);
406 res.push(PolishToken::Mul)
407 }
408 Operations::Sub(x, y) => {
409 x.as_ref().to_polish(cache, res);
410 y.as_ref().to_polish(cache, res);
411 res.push(PolishToken::Sub)
412 }
413 Operations::Pow(x, n) => {
414 x.to_polish(cache, res);
415 res.push(PolishToken::Pow(*n))
416 }
417 Operations::Double(x) => {
418 x.to_polish(cache, res);
419 res.push(PolishToken::Dup);
420 res.push(PolishToken::Add);
421 }
422 Operations::Square(x) => {
423 x.to_polish(cache, res);
424 res.push(PolishToken::Dup);
425 res.push(PolishToken::Mul);
426 }
427 Operations::Cache(id, x) => {
428 match cache.get(id) {
429 Some(pos) =>
430 {
432 res.push(PolishToken::Load(*pos))
433 }
434 None => {
435 x.to_polish(cache, res);
437 res.push(PolishToken::Store);
438 cache.insert(*id, cache.len());
439 }
440 }
441 }
442 Operations::IfFeature(feature, if_true, if_false) => {
443 {
444 let tok = PolishToken::SkipIfNot(*feature, 0);
446 res.push(tok);
447 let len_before = res.len();
448 let mut cache = cache.clone();
451 if_true.to_polish(&mut cache, res);
452 let len_after = res.len();
453 res[len_before - 1] = PolishToken::SkipIfNot(*feature, len_after - len_before);
454 }
455
456 {
457 let tok = PolishToken::SkipIfNot(*feature, 0);
459 res.push(tok);
460 let len_before = res.len();
461 let mut cache = cache.clone();
464 if_false.to_polish(&mut cache, res);
465 let len_after = res.len();
466 res[len_before - 1] = PolishToken::SkipIfNot(*feature, len_after - len_before);
467 }
468 }
469 }
470 }
471}
472
473impl<T: Literal> Operations<T>
474where
475 T::F: Field,
476{
477 pub fn pow(self, p: u64) -> Self {
479 if p == 0 {
480 return Self::literal(T::F::one());
481 }
482 match self.to_literal() {
483 Ok(l) => Self::literal(<T::F as Field>::pow(&l, [p])),
484 Err(x) => Self::Pow(Box::new(x), p),
485 }
486 }
487}
488
489impl<F: Field, ChallengeTerm: Copy> ConstantExpr<F, ChallengeTerm> {
490 pub fn value(&self, c: &Constants<F>, chals: &dyn Index<ChallengeTerm, Output = F>) -> F {
492 use ConstantExprInner::*;
493 use Operations::*;
494 match self {
495 Atom(Challenge(challenge_term)) => chals[*challenge_term],
496 Atom(Constant(ConstantTerm::EndoCoefficient)) => c.endo_coefficient,
497 Atom(Constant(ConstantTerm::Mds { row, col })) => c.mds[*row][*col],
498 Atom(Constant(ConstantTerm::Literal(x))) => *x,
499 Pow(x, p) => x.value(c, chals).pow([*p]),
500 Mul(x, y) => x.value(c, chals) * y.value(c, chals),
501 Add(x, y) => x.value(c, chals) + y.value(c, chals),
502 Sub(x, y) => x.value(c, chals) - y.value(c, chals),
503 Double(x) => x.value(c, chals).double(),
504 Square(x) => x.value(c, chals).square(),
505 Cache(_, x) => {
506 x.value(c, chals)
508 }
509 IfFeature(_flag, _if_true, _if_false) => todo!(),
510 }
511 }
512}
513
514#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
516pub struct CacheId(usize);
517
518#[derive(Default)]
520pub struct Cache {
521 next_id: usize,
522}
523
524impl CacheId {
525 fn get_from<'b, F: FftField>(
526 &self,
527 cache: &'b HashMap<CacheId, EvalResult<'_, F>>,
528 ) -> Option<EvalResult<'b, F>> {
529 cache.get(self).map(|e| match e {
530 EvalResult::Constant(x) => EvalResult::Constant(*x),
531 EvalResult::SubEvals {
532 domain,
533 shift,
534 evals,
535 } => EvalResult::SubEvals {
536 domain: *domain,
537 shift: *shift,
538 evals,
539 },
540 EvalResult::Evals { domain, evals } => EvalResult::SubEvals {
541 domain: *domain,
542 shift: 0,
543 evals,
544 },
545 })
546 }
547
548 fn var_name(&self) -> String {
549 format!("x_{}", self.0)
550 }
551
552 fn latex_name(&self) -> String {
553 format!("x_{{{}}}", self.0)
554 }
555}
556
557impl Cache {
558 fn next_id(&mut self) -> CacheId {
559 let id = self.next_id;
560 self.next_id += 1;
561 CacheId(id)
562 }
563
564 pub fn cache<F: Field, ChallengeTerm, T: ExprOps<F, ChallengeTerm>>(&mut self, e: T) -> T {
565 e.cache(self)
566 }
567}
568
569#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Hash)]
571#[cfg_attr(
572 feature = "ocaml_types",
573 derive(ocaml::IntoValue, ocaml::FromValue, ocaml_gen::Enum)
574)]
575pub enum FeatureFlag {
576 RangeCheck0,
577 RangeCheck1,
578 ForeignFieldAdd,
579 ForeignFieldMul,
580 Xor,
581 Rot,
582 LookupTables,
583 RuntimeLookupTables,
584 LookupPattern(LookupPattern),
585 TableWidth(isize), LookupsPerRow(isize), }
590
591impl FeatureFlag {
592 fn is_enabled(&self) -> bool {
593 todo!("Handle features")
594 }
595}
596
597#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
598pub struct RowOffset {
599 pub zk_rows: bool,
600 pub offset: i32,
601}
602
603#[derive(Clone, Debug, PartialEq)]
604pub enum ExprInner<C, Column> {
605 Constant(C),
606 Cell(Variable<Column>),
607 VanishesOnZeroKnowledgeAndPreviousRows,
608 UnnormalizedLagrangeBasis(RowOffset),
611}
612
613pub type Expr<C, Column> = Operations<ExprInner<C, Column>>;
624
625impl<F, Column, ChallengeTerm> From<ConstantExpr<F, ChallengeTerm>>
626 for Expr<ConstantExpr<F, ChallengeTerm>, Column>
627{
628 fn from(x: ConstantExpr<F, ChallengeTerm>) -> Self {
629 Expr::Atom(ExprInner::Constant(x))
630 }
631}
632
633impl<'a, F, Column, ChallengeTerm: AlphaChallengeTerm<'a>> From<ConstantTerm<F>>
634 for Expr<ConstantExpr<F, ChallengeTerm>, Column>
635{
636 fn from(x: ConstantTerm<F>) -> Self {
637 ConstantExpr::from(x).into()
638 }
639}
640
641impl<'a, F, Column, ChallengeTerm: AlphaChallengeTerm<'a>> From<ChallengeTerm>
642 for Expr<ConstantExpr<F, ChallengeTerm>, Column>
643{
644 fn from(x: ChallengeTerm) -> Self {
645 ConstantExpr::from(x).into()
646 }
647}
648
649impl<T: Literal, Column: Clone> Literal for ExprInner<T, Column> {
650 type F = T::F;
651
652 fn literal(x: Self::F) -> Self {
653 ExprInner::Constant(T::literal(x))
654 }
655
656 fn to_literal(self) -> Result<Self::F, Self> {
657 match self {
658 ExprInner::Constant(x) => match x.to_literal() {
659 Ok(x) => Ok(x),
660 Err(x) => Err(ExprInner::Constant(x)),
661 },
662 x => Err(x),
663 }
664 }
665
666 fn to_literal_ref(&self) -> Option<&Self::F> {
667 match self {
668 ExprInner::Constant(x) => x.to_literal_ref(),
669 _ => None,
670 }
671 }
672
673 fn as_literal(&self, constants: &Constants<Self::F>) -> Self {
674 match self {
675 ExprInner::Constant(x) => ExprInner::Constant(x.as_literal(constants)),
676 ExprInner::Cell(_)
677 | ExprInner::VanishesOnZeroKnowledgeAndPreviousRows
678 | ExprInner::UnnormalizedLagrangeBasis(_) => self.clone(),
679 }
680 }
681}
682
683impl<T: Literal + PartialEq> Operations<T>
684where
685 T::F: Field,
686{
687 fn apply_feature_flags_inner(&self, features: &FeatureFlags) -> (Self, bool) {
688 use Operations::*;
689 match self {
690 Atom(_) => (self.clone(), false),
691 Double(c) => {
692 let (c_reduced, reduce_further) = c.apply_feature_flags_inner(features);
693 if reduce_further && c_reduced.is_zero() {
694 (Self::zero(), true)
695 } else {
696 (Double(Box::new(c_reduced)), false)
697 }
698 }
699 Square(c) => {
700 let (c_reduced, reduce_further) = c.apply_feature_flags_inner(features);
701 if reduce_further && (c_reduced.is_zero() || c_reduced.is_one()) {
702 (c_reduced, true)
703 } else {
704 (Square(Box::new(c_reduced)), false)
705 }
706 }
707 Add(c1, c2) => {
708 let (c1_reduced, reduce_further1) = c1.apply_feature_flags_inner(features);
709 let (c2_reduced, reduce_further2) = c2.apply_feature_flags_inner(features);
710 if reduce_further1 && c1_reduced.is_zero() {
711 if reduce_further2 && c2_reduced.is_zero() {
712 (Self::zero(), true)
713 } else {
714 (c2_reduced, false)
715 }
716 } else if reduce_further2 && c2_reduced.is_zero() {
717 (c1_reduced, false)
718 } else {
719 (Add(Box::new(c1_reduced), Box::new(c2_reduced)), false)
720 }
721 }
722 Sub(c1, c2) => {
723 let (c1_reduced, reduce_further1) = c1.apply_feature_flags_inner(features);
724 let (c2_reduced, reduce_further2) = c2.apply_feature_flags_inner(features);
725 if reduce_further1 && c1_reduced.is_zero() {
726 if reduce_further2 && c2_reduced.is_zero() {
727 (Self::zero(), true)
728 } else {
729 (-c2_reduced, false)
730 }
731 } else if reduce_further2 && c2_reduced.is_zero() {
732 (c1_reduced, false)
733 } else {
734 (Sub(Box::new(c1_reduced), Box::new(c2_reduced)), false)
735 }
736 }
737 Mul(c1, c2) => {
738 let (c1_reduced, reduce_further1) = c1.apply_feature_flags_inner(features);
739 let (c2_reduced, reduce_further2) = c2.apply_feature_flags_inner(features);
740 if reduce_further1 && c1_reduced.is_zero()
741 || reduce_further2 && c2_reduced.is_zero()
742 {
743 (Self::zero(), true)
744 } else if reduce_further1 && c1_reduced.is_one() {
745 if reduce_further2 && c2_reduced.is_one() {
746 (Self::one(), true)
747 } else {
748 (c2_reduced, false)
749 }
750 } else if reduce_further2 && c2_reduced.is_one() {
751 (c1_reduced, false)
752 } else {
753 (Mul(Box::new(c1_reduced), Box::new(c2_reduced)), false)
754 }
755 }
756 Pow(c, power) => {
757 let (c_reduced, reduce_further) = c.apply_feature_flags_inner(features);
758 if reduce_further && (c_reduced.is_zero() || c_reduced.is_one()) {
759 (c_reduced, true)
760 } else {
761 (Pow(Box::new(c_reduced), *power), false)
762 }
763 }
764 Cache(cache_id, c) => {
765 let (c_reduced, reduce_further) = c.apply_feature_flags_inner(features);
766 if reduce_further {
767 (c_reduced, true)
768 } else {
769 (Cache(*cache_id, Box::new(c_reduced)), false)
770 }
771 }
772 IfFeature(feature, c1, c2) => {
773 let is_enabled = {
774 use FeatureFlag::*;
775 match feature {
776 RangeCheck0 => features.range_check0,
777 RangeCheck1 => features.range_check1,
778 ForeignFieldAdd => features.foreign_field_add,
779 ForeignFieldMul => features.foreign_field_mul,
780 Xor => features.xor,
781 Rot => features.rot,
782 LookupTables => {
783 features.lookup_features.patterns != LookupPatterns::default()
784 }
785 RuntimeLookupTables => features.lookup_features.uses_runtime_tables,
786 LookupPattern(pattern) => features.lookup_features.patterns[*pattern],
787 TableWidth(width) => features
788 .lookup_features
789 .patterns
790 .into_iter()
791 .any(|feature| feature.max_joint_size() >= (*width as u32)),
792 LookupsPerRow(count) => features
793 .lookup_features
794 .patterns
795 .into_iter()
796 .any(|feature| feature.max_lookups_per_row() >= (*count as usize)),
797 }
798 };
799 if is_enabled {
800 let (c1_reduced, _) = c1.apply_feature_flags_inner(features);
801 (c1_reduced, false)
802 } else {
803 let (c2_reduced, _) = c2.apply_feature_flags_inner(features);
804 (c2_reduced, true)
805 }
806 }
807 }
808 }
809 pub fn apply_feature_flags(&self, features: &FeatureFlags) -> Self {
810 let (res, _) = self.apply_feature_flags_inner(features);
811 res
812 }
813}
814
815#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
819pub enum PolishToken<F, Column, ChallengeTerm> {
820 Constant(ConstantTerm<F>),
821 Challenge(ChallengeTerm),
822 Cell(Variable<Column>),
823 Dup,
824 Pow(u64),
825 Add,
826 Mul,
827 Sub,
828 VanishesOnZeroKnowledgeAndPreviousRows,
829 UnnormalizedLagrangeBasis(RowOffset),
830 Store,
831 Load(usize),
832 SkipIf(FeatureFlag, usize),
834 SkipIfNot(FeatureFlag, usize),
836}
837
838pub trait ColumnEvaluations<F> {
839 type Column;
840 fn evaluate(&self, col: Self::Column) -> Result<PointEvaluations<F>, ExprError<Self::Column>>;
841}
842
843impl<Column: Copy> Variable<Column> {
844 fn evaluate<F: Field, Evaluations: ColumnEvaluations<F, Column = Column>>(
845 &self,
846 evals: &Evaluations,
847 ) -> Result<F, ExprError<Column>> {
848 let point_evaluations = evals.evaluate(self.col)?;
849 match self.row {
850 CurrOrNext::Curr => Ok(point_evaluations.zeta),
851 CurrOrNext::Next => Ok(point_evaluations.zeta_omega),
852 }
853 }
854}
855
856impl<F: FftField, Column: Copy, ChallengeTerm: Copy> PolishToken<F, Column, ChallengeTerm> {
857 pub fn evaluate<Evaluations: ColumnEvaluations<F, Column = Column>>(
859 toks: &[PolishToken<F, Column, ChallengeTerm>],
860 d: D<F>,
861 pt: F,
862 evals: &Evaluations,
863 c: &Constants<F>,
864 chals: &dyn Index<ChallengeTerm, Output = F>,
865 ) -> Result<F, ExprError<Column>> {
866 let mut stack = vec![];
867 let mut cache: Vec<F> = vec![];
868
869 let mut skip_count = 0;
870
871 for t in toks.iter() {
872 if skip_count > 0 {
873 skip_count -= 1;
874 continue;
875 }
876
877 use ConstantTerm::*;
878 use PolishToken::*;
879 match t {
880 Challenge(challenge_term) => stack.push(chals[*challenge_term]),
881 Constant(EndoCoefficient) => stack.push(c.endo_coefficient),
882 Constant(Mds { row, col }) => stack.push(c.mds[*row][*col]),
883 VanishesOnZeroKnowledgeAndPreviousRows => {
884 stack.push(eval_vanishes_on_last_n_rows(d, c.zk_rows + 1, pt))
885 }
886 UnnormalizedLagrangeBasis(i) => {
887 let offset = if i.zk_rows {
888 -(c.zk_rows as i32) + i.offset
889 } else {
890 i.offset
891 };
892 stack.push(unnormalized_lagrange_basis(&d, offset, &pt))
893 }
894 Constant(Literal(x)) => stack.push(*x),
895 Dup => stack.push(stack[stack.len() - 1]),
896 Cell(v) => match v.evaluate(evals) {
897 Ok(x) => stack.push(x),
898 Err(e) => return Err(e),
899 },
900 Pow(n) => {
901 let i = stack.len() - 1;
902 stack[i] = stack[i].pow([*n]);
903 }
904 Add => {
905 let y = stack.pop().ok_or(ExprError::EmptyStack)?;
906 let x = stack.pop().ok_or(ExprError::EmptyStack)?;
907 stack.push(x + y);
908 }
909 Mul => {
910 let y = stack.pop().ok_or(ExprError::EmptyStack)?;
911 let x = stack.pop().ok_or(ExprError::EmptyStack)?;
912 stack.push(x * y);
913 }
914 Sub => {
915 let y = stack.pop().ok_or(ExprError::EmptyStack)?;
916 let x = stack.pop().ok_or(ExprError::EmptyStack)?;
917 stack.push(x - y);
918 }
919 Store => {
920 let x = stack[stack.len() - 1];
921 cache.push(x);
922 }
923 Load(i) => stack.push(cache[*i]),
924 SkipIf(feature, count) => {
925 if feature.is_enabled() {
926 skip_count = *count;
927 stack.push(F::zero());
928 }
929 }
930 SkipIfNot(feature, count) => {
931 if !feature.is_enabled() {
932 skip_count = *count;
933 stack.push(F::zero());
934 }
935 }
936 }
937 }
938
939 assert_eq!(stack.len(), 1);
940 Ok(stack[0])
941 }
942}
943
944impl<C, Column> Expr<C, Column> {
945 pub fn cell(col: Column, row: CurrOrNext) -> Expr<C, Column> {
947 Expr::Atom(ExprInner::Cell(Variable { col, row }))
948 }
949
950 pub fn double(self) -> Self {
951 Expr::Double(Box::new(self))
952 }
953
954 pub fn square(self) -> Self {
955 Expr::Square(Box::new(self))
956 }
957
958 pub fn constant(c: C) -> Expr<C, Column> {
960 Expr::Atom(ExprInner::Constant(c))
961 }
962
963 pub fn degree(&self, d1_size: u64, zk_rows: u64) -> u64 {
972 use ExprInner::*;
973 use Operations::*;
974 match self {
975 Double(x) => x.degree(d1_size, zk_rows),
976 Atom(Constant(_)) => 0,
977 Atom(VanishesOnZeroKnowledgeAndPreviousRows) => zk_rows + 1,
978 Atom(UnnormalizedLagrangeBasis(_)) => d1_size,
979 Atom(Cell(_)) => d1_size,
980 Square(x) => 2 * x.degree(d1_size, zk_rows),
981 Mul(x, y) => (*x).degree(d1_size, zk_rows) + (*y).degree(d1_size, zk_rows),
982 Add(x, y) | Sub(x, y) => {
983 core::cmp::max((*x).degree(d1_size, zk_rows), (*y).degree(d1_size, zk_rows))
984 }
985 Pow(e, d) => d * e.degree(d1_size, zk_rows),
986 Cache(_, e) => e.degree(d1_size, zk_rows),
987 IfFeature(_, e1, e2) => {
988 core::cmp::max(e1.degree(d1_size, zk_rows), e2.degree(d1_size, zk_rows))
989 }
990 }
991 }
992}
993
994impl<'a, F, Column: FormattedOutput + Debug + Clone, ChallengeTerm> fmt::Display
995 for Expr<ConstantExpr<F, ChallengeTerm>, Column>
996where
997 F: PrimeField,
998 ChallengeTerm: AlphaChallengeTerm<'a>,
999{
1000 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1001 let cache = &mut HashMap::new();
1002 write!(f, "{}", self.text(cache))
1003 }
1004}
1005
1006#[derive(Clone)]
1007enum EvalResult<'a, F: FftField> {
1008 Constant(F),
1009 Evals {
1010 domain: Domain,
1011 evals: Evaluations<F, D<F>>,
1012 },
1013 SubEvals {
1018 domain: Domain,
1019 shift: usize,
1020 evals: &'a Evaluations<F, D<F>>,
1021 },
1022}
1023
1024fn unnormalized_lagrange_evals<
1059 'a,
1060 F: FftField,
1061 ChallengeTerm,
1062 Challenge: Index<ChallengeTerm, Output = F>,
1063 Environment: ColumnEnvironment<'a, F, ChallengeTerm, Challenge>,
1064>(
1065 l0_1: F,
1066 i: i32,
1067 res_domain: Domain,
1068 env: &Environment,
1069) -> Evaluations<F, D<F>> {
1070 let k = match res_domain {
1071 Domain::D1 => 1,
1072 Domain::D2 => 2,
1073 Domain::D4 => 4,
1074 Domain::D8 => 8,
1075 };
1076 let res_domain = env.get_domain(res_domain);
1077
1078 let d1 = env.get_domain(Domain::D1);
1079 let n = d1.size;
1080 let i = if i < 0 {
1082 ((i as isize) + (n as isize)) as usize
1083 } else {
1084 i as usize
1085 };
1086 let ii = i as u64;
1087 assert!(ii < n);
1088 let omega = d1.group_gen;
1089 let omega_i = omega.pow([ii]);
1090 let omega_minus_i = omega.pow([n - ii]);
1091
1092 let omega_k_n_pows = pows(k, res_domain.group_gen.pow([n]));
1097 let omega_k_pows = pows(k, res_domain.group_gen);
1098
1099 let mut evals: Vec<F> = {
1100 let mut v = vec![F::one(); k * (n as usize)];
1101 let mut omega_q = F::one();
1102 for q in 0..(n as usize) {
1103 for r in 1..k {
1105 v[k * q + r] = omega_q * omega_k_pows[r] - omega_i;
1106 }
1107 omega_q *= omega;
1108 }
1109 ark_ff::fields::batch_inversion::<F>(&mut v[..]);
1110 v
1111 };
1112 for q in 0..(n as usize) {
1118 evals[k * q] = F::zero();
1119 }
1120 evals[k * i] = omega_minus_i * l0_1;
1121
1122 for q in 0..(n as usize) {
1124 for r in 1..k {
1125 evals[k * q + r] *= omega_k_n_pows[r] - F::one();
1126 }
1127 }
1128
1129 Evaluations::<F, D<F>>::from_vec_and_domain(evals, res_domain)
1130}
1131
1132impl<'a, F: FftField> EvalResult<'a, F> {
1135 fn init_<G: Sync + Send + Fn(usize) -> F>(
1145 res_domain: (Domain, D<F>),
1146 g: G,
1147 ) -> Evaluations<F, D<F>> {
1148 let n = res_domain.1.size();
1149 Evaluations::<F, D<F>>::from_vec_and_domain(
1150 o1_utils::cfg_into_iter!(0..n).map(g).collect(),
1151 res_domain.1,
1152 )
1153 }
1154
1155 fn init<G: Sync + Send + Fn(usize) -> F>(res_domain: (Domain, D<F>), g: G) -> Self {
1158 Self::Evals {
1159 domain: res_domain.0,
1160 evals: Self::init_(res_domain, g),
1161 }
1162 }
1163
1164 fn add<'c>(self, other: EvalResult<'_, F>, res_domain: (Domain, D<F>)) -> EvalResult<'c, F> {
1165 use EvalResult::*;
1166 match (self, other) {
1167 (Constant(x), Constant(y)) => Constant(x + y),
1168 (Evals { domain, mut evals }, Constant(x))
1169 | (Constant(x), Evals { domain, mut evals }) => {
1170 o1_utils::cfg_iter_mut!(evals.evals).for_each(|e| *e += x);
1171 Evals { domain, evals }
1172 }
1173 (
1174 SubEvals {
1175 evals,
1176 domain,
1177 shift,
1178 },
1179 Constant(x),
1180 )
1181 | (
1182 Constant(x),
1183 SubEvals {
1184 evals,
1185 domain,
1186 shift,
1187 },
1188 ) => {
1189 let n = res_domain.1.size();
1190 let scale = (domain as usize) / (res_domain.0 as usize);
1191 assert!(
1192 scale != 0,
1193 "Check that the implementation of
1194 column_domain and the evaluation domain of the
1195 witnesses are the same"
1196 );
1197 let v: Vec<_> = o1_utils::cfg_into_iter!(0..n)
1198 .map(|i| {
1199 x + evals.evals[(scale * i + (domain as usize) * shift) % evals.evals.len()]
1200 })
1201 .collect();
1202 Evals {
1203 domain: res_domain.0,
1204 evals: Evaluations::<F, D<F>>::from_vec_and_domain(v, res_domain.1),
1205 }
1206 }
1207 (
1208 Evals {
1209 domain: d1,
1210 evals: mut es1,
1211 },
1212 Evals {
1213 domain: d2,
1214 evals: es2,
1215 },
1216 ) => {
1217 assert_eq!(d1, d2);
1218 es1 += &es2;
1219 Evals {
1220 domain: d1,
1221 evals: es1,
1222 }
1223 }
1224 (
1225 SubEvals {
1226 domain: d_sub,
1227 shift: s,
1228 evals: es_sub,
1229 },
1230 Evals {
1231 domain: d,
1232 mut evals,
1233 },
1234 )
1235 | (
1236 Evals {
1237 domain: d,
1238 mut evals,
1239 },
1240 SubEvals {
1241 domain: d_sub,
1242 shift: s,
1243 evals: es_sub,
1244 },
1245 ) => {
1246 let scale = (d_sub as usize) / (d as usize);
1247 assert!(
1248 scale != 0,
1249 "Check that the implementation of
1250 column_domain and the evaluation domain of the
1251 witnesses are the same"
1252 );
1253 o1_utils::cfg_iter_mut!(evals.evals)
1254 .enumerate()
1255 .for_each(|(i, e)| {
1256 *e += es_sub.evals[(scale * i + (d_sub as usize) * s) % es_sub.evals.len()];
1257 });
1258 Evals { evals, domain: d }
1259 }
1260 (
1261 SubEvals {
1262 domain: d1,
1263 shift: s1,
1264 evals: es1,
1265 },
1266 SubEvals {
1267 domain: d2,
1268 shift: s2,
1269 evals: es2,
1270 },
1271 ) => {
1272 let scale1 = (d1 as usize) / (res_domain.0 as usize);
1273 assert!(
1274 scale1 != 0,
1275 "Check that the implementation of
1276 column_domain and the evaluation domain of the
1277 witnesses are the same"
1278 );
1279 let scale2 = (d2 as usize) / (res_domain.0 as usize);
1280 assert!(
1281 scale2 != 0,
1282 "Check that the implementation of
1283 column_domain and the evaluation domain of the
1284 witnesses are the same"
1285 );
1286 let n = res_domain.1.size();
1287 let v: Vec<_> = o1_utils::cfg_into_iter!(0..n)
1288 .map(|i| {
1289 es1.evals[(scale1 * i + (d1 as usize) * s1) % es1.evals.len()]
1290 + es2.evals[(scale2 * i + (d2 as usize) * s2) % es2.evals.len()]
1291 })
1292 .collect();
1293
1294 Evals {
1295 domain: res_domain.0,
1296 evals: Evaluations::<F, D<F>>::from_vec_and_domain(v, res_domain.1),
1297 }
1298 }
1299 }
1300 }
1301
1302 fn sub<'c>(self, other: EvalResult<'_, F>, res_domain: (Domain, D<F>)) -> EvalResult<'c, F> {
1303 use EvalResult::*;
1304 match (self, other) {
1305 (Constant(x), Constant(y)) => Constant(x - y),
1306 (Evals { domain, mut evals }, Constant(x)) => {
1307 o1_utils::cfg_iter_mut!(evals.evals).for_each(|e| *e -= x);
1308 Evals { domain, evals }
1309 }
1310 (Constant(x), Evals { domain, mut evals }) => {
1311 o1_utils::cfg_iter_mut!(evals.evals).for_each(|e| *e = x - *e);
1312 Evals { domain, evals }
1313 }
1314 (
1315 SubEvals {
1316 evals,
1317 domain: d,
1318 shift: s,
1319 },
1320 Constant(x),
1321 ) => {
1322 let scale = (d as usize) / (res_domain.0 as usize);
1323 assert!(
1324 scale != 0,
1325 "Check that the implementation of
1326 column_domain and the evaluation domain of the
1327 witnesses are the same"
1328 );
1329 EvalResult::init(res_domain, |i| {
1330 evals.evals[(scale * i + (d as usize) * s) % evals.evals.len()] - x
1331 })
1332 }
1333 (
1334 Constant(x),
1335 SubEvals {
1336 evals,
1337 domain: d,
1338 shift: s,
1339 },
1340 ) => {
1341 let scale = (d as usize) / (res_domain.0 as usize);
1342 assert!(
1343 scale != 0,
1344 "Check that the implementation of
1345 column_domain and the evaluation domain of the
1346 witnesses are the same"
1347 );
1348
1349 EvalResult::init(res_domain, |i| {
1350 x - evals.evals[(scale * i + (d as usize) * s) % evals.evals.len()]
1351 })
1352 }
1353 (
1354 Evals {
1355 domain: d1,
1356 evals: mut es1,
1357 },
1358 Evals {
1359 domain: d2,
1360 evals: es2,
1361 },
1362 ) => {
1363 assert_eq!(d1, d2);
1364 es1 -= &es2;
1365 Evals {
1366 domain: d1,
1367 evals: es1,
1368 }
1369 }
1370 (
1371 SubEvals {
1372 domain: d_sub,
1373 shift: s,
1374 evals: es_sub,
1375 },
1376 Evals {
1377 domain: d,
1378 mut evals,
1379 },
1380 ) => {
1381 let scale = (d_sub as usize) / (d as usize);
1382 assert!(
1383 scale != 0,
1384 "Check that the implementation of
1385 column_domain and the evaluation domain of the
1386 witnesses are the same"
1387 );
1388
1389 o1_utils::cfg_iter_mut!(evals.evals)
1390 .enumerate()
1391 .for_each(|(i, e)| {
1392 *e = es_sub.evals[(scale * i + (d_sub as usize) * s) % es_sub.evals.len()]
1393 - *e;
1394 });
1395 Evals { evals, domain: d }
1396 }
1397 (
1398 Evals {
1399 domain: d,
1400 mut evals,
1401 },
1402 SubEvals {
1403 domain: d_sub,
1404 shift: s,
1405 evals: es_sub,
1406 },
1407 ) => {
1408 let scale = (d_sub as usize) / (d as usize);
1409 assert!(
1410 scale != 0,
1411 "Check that the implementation of
1412 column_domain and the evaluation domain of the
1413 witnesses are the same"
1414 );
1415 o1_utils::cfg_iter_mut!(evals.evals)
1416 .enumerate()
1417 .for_each(|(i, e)| {
1418 *e -= es_sub.evals[(scale * i + (d_sub as usize) * s) % es_sub.evals.len()];
1419 });
1420 Evals { evals, domain: d }
1421 }
1422 (
1423 SubEvals {
1424 domain: d1,
1425 shift: s1,
1426 evals: es1,
1427 },
1428 SubEvals {
1429 domain: d2,
1430 shift: s2,
1431 evals: es2,
1432 },
1433 ) => {
1434 let scale1 = (d1 as usize) / (res_domain.0 as usize);
1435 assert!(
1436 scale1 != 0,
1437 "Check that the implementation of
1438 column_domain and the evaluation domain of the
1439 witnesses are the same"
1440 );
1441 let scale2 = (d2 as usize) / (res_domain.0 as usize);
1442 assert!(
1443 scale2 != 0,
1444 "Check that the implementation of
1445 column_domain and the evaluation domain of the
1446 witnesses are the same"
1447 );
1448
1449 EvalResult::init(res_domain, |i| {
1450 es1.evals[(scale1 * i + (d1 as usize) * s1) % es1.evals.len()]
1451 - es2.evals[(scale2 * i + (d2 as usize) * s2) % es2.evals.len()]
1452 })
1453 }
1454 }
1455 }
1456
1457 fn pow<'b>(self, d: u64, res_domain: (Domain, D<F>)) -> EvalResult<'b, F> {
1458 let mut acc = EvalResult::Constant(F::one());
1459 for i in (0..u64::BITS).rev() {
1460 acc = acc.square(res_domain);
1461
1462 if (d >> i) & 1 == 1 {
1463 acc = acc.mul(self.clone(), res_domain)
1465 }
1466 }
1467 acc
1468 }
1469
1470 fn square<'b>(self, res_domain: (Domain, D<F>)) -> EvalResult<'b, F> {
1471 use EvalResult::*;
1472 match self {
1473 Constant(x) => Constant(x.square()),
1474 Evals { domain, mut evals } => {
1475 o1_utils::cfg_iter_mut!(evals.evals).for_each(|e| {
1476 e.square_in_place();
1477 });
1478 Evals { domain, evals }
1479 }
1480 SubEvals {
1481 evals,
1482 domain: d,
1483 shift: s,
1484 } => {
1485 let scale = (d as usize) / (res_domain.0 as usize);
1486 assert!(
1487 scale != 0,
1488 "Check that the implementation of
1489 column_domain and the evaluation domain of the
1490 witnesses are the same"
1491 );
1492 EvalResult::init(res_domain, |i| {
1493 evals.evals[(scale * i + (d as usize) * s) % evals.evals.len()].square()
1494 })
1495 }
1496 }
1497 }
1498
1499 fn mul<'c>(self, other: EvalResult<'_, F>, res_domain: (Domain, D<F>)) -> EvalResult<'c, F> {
1500 use EvalResult::*;
1501 match (self, other) {
1502 (Constant(x), Constant(y)) => Constant(x * y),
1503 (Evals { domain, mut evals }, Constant(x))
1504 | (Constant(x), Evals { domain, mut evals }) => {
1505 o1_utils::cfg_iter_mut!(evals.evals).for_each(|e| *e *= x);
1506 Evals { domain, evals }
1507 }
1508 (
1509 SubEvals {
1510 evals,
1511 domain: d,
1512 shift: s,
1513 },
1514 Constant(x),
1515 )
1516 | (
1517 Constant(x),
1518 SubEvals {
1519 evals,
1520 domain: d,
1521 shift: s,
1522 },
1523 ) => {
1524 let scale = (d as usize) / (res_domain.0 as usize);
1525 assert!(
1526 scale != 0,
1527 "Check that the implementation of
1528 column_domain and the evaluation domain of the
1529 witnesses are the same"
1530 );
1531 EvalResult::init(res_domain, |i| {
1532 x * evals.evals[(scale * i + (d as usize) * s) % evals.evals.len()]
1533 })
1534 }
1535 (
1536 Evals {
1537 domain: d1,
1538 evals: mut es1,
1539 },
1540 Evals {
1541 domain: d2,
1542 evals: es2,
1543 },
1544 ) => {
1545 assert_eq!(d1, d2);
1546 es1 *= &es2;
1547 Evals {
1548 domain: d1,
1549 evals: es1,
1550 }
1551 }
1552 (
1553 SubEvals {
1554 domain: d_sub,
1555 shift: s,
1556 evals: es_sub,
1557 },
1558 Evals {
1559 domain: d,
1560 mut evals,
1561 },
1562 )
1563 | (
1564 Evals {
1565 domain: d,
1566 mut evals,
1567 },
1568 SubEvals {
1569 domain: d_sub,
1570 shift: s,
1571 evals: es_sub,
1572 },
1573 ) => {
1574 let scale = (d_sub as usize) / (d as usize);
1575 assert!(
1576 scale != 0,
1577 "Check that the implementation of
1578 column_domainand the evaluation domain of the
1579 witnesses are the same"
1580 );
1581
1582 o1_utils::cfg_iter_mut!(evals.evals)
1583 .enumerate()
1584 .for_each(|(i, e)| {
1585 *e *= es_sub.evals[(scale * i + (d_sub as usize) * s) % es_sub.evals.len()];
1586 });
1587 Evals { evals, domain: d }
1588 }
1589 (
1590 SubEvals {
1591 domain: d1,
1592 shift: s1,
1593 evals: es1,
1594 },
1595 SubEvals {
1596 domain: d2,
1597 shift: s2,
1598 evals: es2,
1599 },
1600 ) => {
1601 let scale1 = (d1 as usize) / (res_domain.0 as usize);
1602 assert!(
1603 scale1 != 0,
1604 "Check that the implementation of
1605 column_domain and the evaluation domain of the
1606 witnesses are the same"
1607 );
1608 let scale2 = (d2 as usize) / (res_domain.0 as usize);
1609
1610 assert!(
1611 scale2 != 0,
1612 "Check that the implementation of
1613 column_domain and the evaluation domain of the
1614 witnesses are the same"
1615 );
1616 EvalResult::init(res_domain, |i| {
1617 es1.evals[(scale1 * i + (d1 as usize) * s1) % es1.evals.len()]
1618 * es2.evals[(scale2 * i + (d2 as usize) * s2) % es2.evals.len()]
1619 })
1620 }
1621 }
1622 }
1623}
1624
1625impl<'a, F: Field, Column: PartialEq + Copy, ChallengeTerm: AlphaChallengeTerm<'a>>
1626 Expr<ConstantExpr<F, ChallengeTerm>, Column>
1627{
1628 pub fn literal(x: F) -> Self {
1631 ConstantTerm::Literal(x).into()
1632 }
1633
1634 pub fn combine_constraints(alphas: impl Iterator<Item = u32>, cs: Vec<Self>) -> Self {
1637 let zero = Expr::<ConstantExpr<F, ChallengeTerm>, Column>::zero();
1638 cs.into_iter()
1639 .zip_eq(alphas)
1640 .map(|(c, i)| Expr::from(ConstantExpr::pow(ChallengeTerm::ALPHA.into(), i as u64)) * c)
1641 .fold(zero, |acc, x| acc + x)
1642 }
1643}
1644
1645impl<F: FftField, Column: Copy, ChallengeTerm: Copy> Expr<ConstantExpr<F, ChallengeTerm>, Column> {
1646 pub fn to_polish(&self) -> Vec<PolishToken<F, Column, ChallengeTerm>> {
1648 let mut res = vec![];
1649 let mut cache = HashMap::new();
1650 self.to_polish_(&mut cache, &mut res);
1651 res
1652 }
1653
1654 fn to_polish_(
1655 &self,
1656 cache: &mut HashMap<CacheId, usize>,
1657 res: &mut Vec<PolishToken<F, Column, ChallengeTerm>>,
1658 ) {
1659 match self {
1660 Expr::Double(x) => {
1661 x.to_polish_(cache, res);
1662 res.push(PolishToken::Dup);
1663 res.push(PolishToken::Add);
1664 }
1665 Expr::Square(x) => {
1666 x.to_polish_(cache, res);
1667 res.push(PolishToken::Dup);
1668 res.push(PolishToken::Mul);
1669 }
1670 Expr::Pow(x, d) => {
1671 x.to_polish_(cache, res);
1672 res.push(PolishToken::Pow(*d))
1673 }
1674 Expr::Atom(ExprInner::Constant(c)) => {
1675 c.to_polish(cache, res);
1676 }
1677 Expr::Atom(ExprInner::Cell(v)) => res.push(PolishToken::Cell(*v)),
1678 Expr::Atom(ExprInner::VanishesOnZeroKnowledgeAndPreviousRows) => {
1679 res.push(PolishToken::VanishesOnZeroKnowledgeAndPreviousRows);
1680 }
1681 Expr::Atom(ExprInner::UnnormalizedLagrangeBasis(i)) => {
1682 res.push(PolishToken::UnnormalizedLagrangeBasis(*i));
1683 }
1684 Expr::Add(x, y) => {
1685 x.to_polish_(cache, res);
1686 y.to_polish_(cache, res);
1687 res.push(PolishToken::Add);
1688 }
1689 Expr::Sub(x, y) => {
1690 x.to_polish_(cache, res);
1691 y.to_polish_(cache, res);
1692 res.push(PolishToken::Sub);
1693 }
1694 Expr::Mul(x, y) => {
1695 x.to_polish_(cache, res);
1696 y.to_polish_(cache, res);
1697 res.push(PolishToken::Mul);
1698 }
1699 Expr::Cache(id, e) => {
1700 match cache.get(id) {
1701 Some(pos) =>
1702 {
1704 res.push(PolishToken::Load(*pos))
1705 }
1706 None => {
1707 e.to_polish_(cache, res);
1709 res.push(PolishToken::Store);
1710 cache.insert(*id, cache.len());
1711 }
1712 }
1713 }
1714 Expr::IfFeature(feature, e1, e2) => {
1715 {
1716 let tok = PolishToken::SkipIfNot(*feature, 0);
1718 res.push(tok);
1719 let len_before = res.len();
1720 let mut cache = cache.clone();
1723 e1.to_polish_(&mut cache, res);
1724 let len_after = res.len();
1725 res[len_before - 1] = PolishToken::SkipIfNot(*feature, len_after - len_before);
1726 }
1727
1728 {
1729 let tok = PolishToken::SkipIfNot(*feature, 0);
1731 res.push(tok);
1732 let len_before = res.len();
1733 let mut cache = cache.clone();
1736 e2.to_polish_(&mut cache, res);
1737 let len_after = res.len();
1738 res[len_before - 1] = PolishToken::SkipIfNot(*feature, len_after - len_before);
1739 }
1740 }
1741 }
1742 }
1743}
1744
1745impl<F: FftField, Column: PartialEq + Copy, ChallengeTerm: Copy>
1746 Expr<ConstantExpr<F, ChallengeTerm>, Column>
1747{
1748 fn evaluate_constants_(
1749 &self,
1750 c: &Constants<F>,
1751 chals: &dyn Index<ChallengeTerm, Output = F>,
1752 ) -> Expr<F, Column> {
1753 use ExprInner::*;
1754 use Operations::*;
1755 match self {
1757 Double(x) => x.evaluate_constants_(c, chals).double(),
1758 Pow(x, d) => x.evaluate_constants_(c, chals).pow(*d),
1759 Square(x) => x.evaluate_constants_(c, chals).square(),
1760 Atom(Constant(x)) => Atom(Constant(x.value(c, chals))),
1761 Atom(Cell(v)) => Atom(Cell(*v)),
1762 Atom(VanishesOnZeroKnowledgeAndPreviousRows) => {
1763 Atom(VanishesOnZeroKnowledgeAndPreviousRows)
1764 }
1765 Atom(UnnormalizedLagrangeBasis(i)) => Atom(UnnormalizedLagrangeBasis(*i)),
1766 Add(x, y) => x.evaluate_constants_(c, chals) + y.evaluate_constants_(c, chals),
1767 Mul(x, y) => x.evaluate_constants_(c, chals) * y.evaluate_constants_(c, chals),
1768 Sub(x, y) => x.evaluate_constants_(c, chals) - y.evaluate_constants_(c, chals),
1769 Cache(id, e) => Cache(*id, Box::new(e.evaluate_constants_(c, chals))),
1770 IfFeature(feature, e1, e2) => IfFeature(
1771 *feature,
1772 Box::new(e1.evaluate_constants_(c, chals)),
1773 Box::new(e2.evaluate_constants_(c, chals)),
1774 ),
1775 }
1776 }
1777
1778 pub fn evaluate<
1780 'a,
1781 Evaluations: ColumnEvaluations<F, Column = Column>,
1782 Challenge: Index<ChallengeTerm, Output = F>,
1783 Environment: ColumnEnvironment<'a, F, ChallengeTerm, Challenge, Column = Column>,
1784 >(
1785 &self,
1786 d: D<F>,
1787 pt: F,
1788 evals: &Evaluations,
1789 env: &Environment,
1790 ) -> Result<F, ExprError<Column>> {
1791 self.evaluate_(d, pt, evals, env.get_constants(), env.get_challenges())
1792 }
1793
1794 pub fn evaluate_<Evaluations: ColumnEvaluations<F, Column = Column>>(
1796 &self,
1797 d: D<F>,
1798 pt: F,
1799 evals: &Evaluations,
1800 c: &Constants<F>,
1801 chals: &dyn Index<ChallengeTerm, Output = F>,
1802 ) -> Result<F, ExprError<Column>> {
1803 use ExprInner::*;
1804 use Operations::*;
1805 match self {
1806 Double(x) => x.evaluate_(d, pt, evals, c, chals).map(|x| x.double()),
1807 Atom(Constant(x)) => Ok(x.value(c, chals)),
1808 Pow(x, p) => Ok(x.evaluate_(d, pt, evals, c, chals)?.pow([*p])),
1809 Mul(x, y) => {
1810 let x = (*x).evaluate_(d, pt, evals, c, chals)?;
1811 let y = (*y).evaluate_(d, pt, evals, c, chals)?;
1812 Ok(x * y)
1813 }
1814 Square(x) => Ok(x.evaluate_(d, pt, evals, c, chals)?.square()),
1815 Add(x, y) => {
1816 let x = (*x).evaluate_(d, pt, evals, c, chals)?;
1817 let y = (*y).evaluate_(d, pt, evals, c, chals)?;
1818 Ok(x + y)
1819 }
1820 Sub(x, y) => {
1821 let x = (*x).evaluate_(d, pt, evals, c, chals)?;
1822 let y = (*y).evaluate_(d, pt, evals, c, chals)?;
1823 Ok(x - y)
1824 }
1825 Atom(VanishesOnZeroKnowledgeAndPreviousRows) => {
1826 Ok(eval_vanishes_on_last_n_rows(d, c.zk_rows + 1, pt))
1827 }
1828 Atom(UnnormalizedLagrangeBasis(i)) => {
1829 let offset = if i.zk_rows {
1830 -(c.zk_rows as i32) + i.offset
1831 } else {
1832 i.offset
1833 };
1834 Ok(unnormalized_lagrange_basis(&d, offset, &pt))
1835 }
1836 Atom(Cell(v)) => v.evaluate(evals),
1837 Cache(_, e) => e.evaluate_(d, pt, evals, c, chals),
1838 IfFeature(feature, e1, e2) => {
1839 if feature.is_enabled() {
1840 e1.evaluate_(d, pt, evals, c, chals)
1841 } else {
1842 e2.evaluate_(d, pt, evals, c, chals)
1843 }
1844 }
1845 }
1846 }
1847
1848 pub fn evaluate_constants<
1850 'a,
1851 Challenge: Index<ChallengeTerm, Output = F>,
1852 Environment: ColumnEnvironment<'a, F, ChallengeTerm, Challenge, Column = Column>,
1853 >(
1854 &self,
1855 env: &Environment,
1856 ) -> Expr<F, Column> {
1857 self.evaluate_constants_(env.get_constants(), env.get_challenges())
1858 }
1859
1860 pub fn evaluations<
1867 'a,
1868 Challenge: Index<ChallengeTerm, Output = F>,
1869 Environment: ColumnEnvironment<'a, F, ChallengeTerm, Challenge, Column = Column>,
1870 >(
1871 &self,
1872 env: &Environment,
1873 ) -> Evaluations<F, D<F>> {
1874 self.evaluate_constants(env).evaluations(env)
1875 }
1876}
1877
1878enum Either<A, B> {
1882 Left(A),
1883 Right(B),
1884}
1885
1886impl<F: FftField, Column: Copy> Expr<F, Column> {
1887 pub fn evaluate<Evaluations: ColumnEvaluations<F, Column = Column>>(
1889 &self,
1890 d: D<F>,
1891 pt: F,
1892 zk_rows: u64,
1893 evals: &Evaluations,
1894 ) -> Result<F, ExprError<Column>> {
1895 use ExprInner::*;
1896 use Operations::*;
1897 match self {
1898 Atom(Constant(x)) => Ok(*x),
1899 Pow(x, p) => Ok(x.evaluate(d, pt, zk_rows, evals)?.pow([*p])),
1900 Double(x) => x.evaluate(d, pt, zk_rows, evals).map(|x| x.double()),
1901 Square(x) => x.evaluate(d, pt, zk_rows, evals).map(|x| x.square()),
1902 Mul(x, y) => {
1903 let x = (*x).evaluate(d, pt, zk_rows, evals)?;
1904 let y = (*y).evaluate(d, pt, zk_rows, evals)?;
1905 Ok(x * y)
1906 }
1907 Add(x, y) => {
1908 let x = (*x).evaluate(d, pt, zk_rows, evals)?;
1909 let y = (*y).evaluate(d, pt, zk_rows, evals)?;
1910 Ok(x + y)
1911 }
1912 Sub(x, y) => {
1913 let x = (*x).evaluate(d, pt, zk_rows, evals)?;
1914 let y = (*y).evaluate(d, pt, zk_rows, evals)?;
1915 Ok(x - y)
1916 }
1917 Atom(VanishesOnZeroKnowledgeAndPreviousRows) => {
1918 Ok(eval_vanishes_on_last_n_rows(d, zk_rows + 1, pt))
1919 }
1920 Atom(UnnormalizedLagrangeBasis(i)) => {
1921 let offset = if i.zk_rows {
1922 -(zk_rows as i32) + i.offset
1923 } else {
1924 i.offset
1925 };
1926 Ok(unnormalized_lagrange_basis(&d, offset, &pt))
1927 }
1928 Atom(Cell(v)) => v.evaluate(evals),
1929 Cache(_, e) => e.evaluate(d, pt, zk_rows, evals),
1930 IfFeature(feature, e1, e2) => {
1931 if feature.is_enabled() {
1932 e1.evaluate(d, pt, zk_rows, evals)
1933 } else {
1934 e2.evaluate(d, pt, zk_rows, evals)
1935 }
1936 }
1937 }
1938 }
1939
1940 pub fn evaluations<
1942 'a,
1943 ChallengeTerm,
1944 Challenge: Index<ChallengeTerm, Output = F>,
1945 Environment: ColumnEnvironment<'a, F, ChallengeTerm, Challenge, Column = Column>,
1946 >(
1947 &self,
1948 env: &Environment,
1949 ) -> Evaluations<F, D<F>> {
1950 let d1_size = env.get_domain(Domain::D1).size;
1951 let deg = self.degree(d1_size, env.get_constants().zk_rows);
1952 let d = if deg <= d1_size {
1953 Domain::D1
1954 } else if deg <= 4 * d1_size {
1955 Domain::D4
1956 } else if deg <= 8 * d1_size {
1957 Domain::D8
1958 } else {
1959 panic!("constraint had degree {deg} > d8 ({})", 8 * d1_size);
1960 };
1961
1962 let mut cache = HashMap::new();
1963
1964 let evals = match self.evaluations_helper(&mut cache, d, env) {
1965 Either::Left(x) => x,
1966 Either::Right(id) => cache.get(&id).unwrap().clone(),
1967 };
1968
1969 match evals {
1970 EvalResult::Evals { evals, domain } => {
1971 assert_eq!(domain, d);
1972 evals
1973 }
1974 EvalResult::Constant(x) => EvalResult::init_((d, env.get_domain(d)), |_| x),
1975 EvalResult::SubEvals {
1976 evals,
1977 domain: d_sub,
1978 shift: s,
1979 } => {
1980 let res_domain = env.get_domain(d);
1981 let scale = (d_sub as usize) / (d as usize);
1982 assert!(
1983 scale != 0,
1984 "Check that the implementation of
1985 column_domain and the evaluation domain of the
1986 witnesses are the same"
1987 );
1988 EvalResult::init_((d, res_domain), |i| {
1989 evals.evals[(scale * i + (d_sub as usize) * s) % evals.evals.len()]
1990 })
1991 }
1992 }
1993 }
1994
1995 fn evaluations_helper<
1996 'a,
1997 'b,
1998 ChallengeTerm,
1999 Challenge: Index<ChallengeTerm, Output = F>,
2000 Environment: ColumnEnvironment<'a, F, ChallengeTerm, Challenge, Column = Column>,
2001 >(
2002 &self,
2003 cache: &'b mut HashMap<CacheId, EvalResult<'a, F>>,
2004 d: Domain,
2005 env: &Environment,
2006 ) -> Either<EvalResult<'a, F>, CacheId>
2007 where
2008 'a: 'b,
2009 {
2010 let dom = (d, env.get_domain(d));
2011
2012 let res: EvalResult<'a, F> = match self {
2013 Expr::Square(x) => match x.evaluations_helper(cache, d, env) {
2014 Either::Left(x) => x.square(dom),
2015 Either::Right(id) => id.get_from(cache).unwrap().square(dom),
2016 },
2017 Expr::Double(x) => {
2018 let x = x.evaluations_helper(cache, d, env);
2019 let res = match x {
2020 Either::Left(x) => {
2021 let x = match x {
2022 EvalResult::Evals { domain, mut evals } => {
2023 o1_utils::cfg_iter_mut!(evals.evals).for_each(|x| {
2024 x.double_in_place();
2025 });
2026 return Either::Left(EvalResult::Evals { domain, evals });
2027 }
2028 x => x,
2029 };
2030 let xx = || match &x {
2031 EvalResult::Constant(x) => EvalResult::Constant(*x),
2032 EvalResult::SubEvals {
2033 domain,
2034 shift,
2035 evals,
2036 } => EvalResult::SubEvals {
2037 domain: *domain,
2038 shift: *shift,
2039 evals,
2040 },
2041 EvalResult::Evals { domain, evals } => EvalResult::SubEvals {
2042 domain: *domain,
2043 shift: 0,
2044 evals,
2045 },
2046 };
2047 xx().add(xx(), dom)
2048 }
2049 Either::Right(id) => {
2050 let x1 = id.get_from(cache).unwrap();
2051 let x2 = id.get_from(cache).unwrap();
2052 x1.add(x2, dom)
2053 }
2054 };
2055 return Either::Left(res);
2056 }
2057 Expr::Cache(id, e) => match cache.get(id) {
2058 Some(_) => return Either::Right(*id),
2059 None => {
2060 match e.evaluations_helper(cache, d, env) {
2061 Either::Left(es) => {
2062 cache.insert(*id, es);
2063 }
2064 Either::Right(_) => {}
2065 };
2066 return Either::Right(*id);
2067 }
2068 },
2069 Expr::Pow(x, p) => {
2070 let x = x.evaluations_helper(cache, d, env);
2071 match x {
2072 Either::Left(x) => x.pow(*p, (d, env.get_domain(d))),
2073 Either::Right(id) => {
2074 id.get_from(cache).unwrap().pow(*p, (d, env.get_domain(d)))
2075 }
2076 }
2077 }
2078 Expr::Atom(ExprInner::VanishesOnZeroKnowledgeAndPreviousRows) => EvalResult::SubEvals {
2079 domain: Domain::D8,
2080 shift: 0,
2081 evals: env.vanishes_on_zero_knowledge_and_previous_rows(),
2082 },
2083 Expr::Atom(ExprInner::Constant(x)) => EvalResult::Constant(*x),
2084 Expr::Atom(ExprInner::UnnormalizedLagrangeBasis(i)) => {
2085 let offset = if i.zk_rows {
2086 -(env.get_constants().zk_rows as i32) + i.offset
2087 } else {
2088 i.offset
2089 };
2090 EvalResult::Evals {
2091 domain: d,
2092 evals: unnormalized_lagrange_evals(env.l0_1(), offset, d, env),
2093 }
2094 }
2095 Expr::Atom(ExprInner::Cell(Variable { col, row })) => {
2096 let evals: &'a Evaluations<F, D<F>> = {
2097 match env.get_column(col) {
2098 None => return Either::Left(EvalResult::Constant(F::zero())),
2099 Some(e) => e,
2100 }
2101 };
2102 EvalResult::SubEvals {
2103 domain: env.column_domain(col),
2104 shift: row.shift(),
2105 evals,
2106 }
2107 }
2108 Expr::Add(e1, e2) => {
2109 let dom = (d, env.get_domain(d));
2110 let f = |x: EvalResult<F>, y: EvalResult<F>| x.add(y, dom);
2111 let e1 = e1.evaluations_helper(cache, d, env);
2112 let e2 = e2.evaluations_helper(cache, d, env);
2113 use Either::*;
2114 match (e1, e2) {
2115 (Left(e1), Left(e2)) => f(e1, e2),
2116 (Right(id1), Left(e2)) => f(id1.get_from(cache).unwrap(), e2),
2117 (Left(e1), Right(id2)) => f(e1, id2.get_from(cache).unwrap()),
2118 (Right(id1), Right(id2)) => {
2119 f(id1.get_from(cache).unwrap(), id2.get_from(cache).unwrap())
2120 }
2121 }
2122 }
2123 Expr::Sub(e1, e2) => {
2124 let dom = (d, env.get_domain(d));
2125 let f = |x: EvalResult<F>, y: EvalResult<F>| x.sub(y, dom);
2126 let e1 = e1.evaluations_helper(cache, d, env);
2127 let e2 = e2.evaluations_helper(cache, d, env);
2128 use Either::*;
2129 match (e1, e2) {
2130 (Left(e1), Left(e2)) => f(e1, e2),
2131 (Right(id1), Left(e2)) => f(id1.get_from(cache).unwrap(), e2),
2132 (Left(e1), Right(id2)) => f(e1, id2.get_from(cache).unwrap()),
2133 (Right(id1), Right(id2)) => {
2134 f(id1.get_from(cache).unwrap(), id2.get_from(cache).unwrap())
2135 }
2136 }
2137 }
2138 Expr::Mul(e1, e2) => {
2139 let dom = (d, env.get_domain(d));
2140 let f = |x: EvalResult<F>, y: EvalResult<F>| x.mul(y, dom);
2141 let e1 = e1.evaluations_helper(cache, d, env);
2142 let e2 = e2.evaluations_helper(cache, d, env);
2143 use Either::*;
2144 match (e1, e2) {
2145 (Left(e1), Left(e2)) => f(e1, e2),
2146 (Right(id1), Left(e2)) => f(id1.get_from(cache).unwrap(), e2),
2147 (Left(e1), Right(id2)) => f(e1, id2.get_from(cache).unwrap()),
2148 (Right(id1), Right(id2)) => {
2149 f(id1.get_from(cache).unwrap(), id2.get_from(cache).unwrap())
2150 }
2151 }
2152 }
2153 Expr::IfFeature(feature, e1, e2) => {
2154 let mut cache = cache.clone();
2157 if feature.is_enabled() {
2158 return e1.evaluations_helper(&mut cache, d, env);
2159 } else {
2160 return e2.evaluations_helper(&mut cache, d, env);
2161 }
2162 }
2163 };
2164 Either::Left(res)
2165 }
2166}
2167
2168#[derive(Clone, Debug, Serialize, Deserialize)]
2169pub struct Linearization<E, Column> {
2172 pub constant_term: E,
2173 pub index_terms: Vec<(Column, E)>,
2174}
2175
2176impl<E: Default, Column> Default for Linearization<E, Column> {
2177 fn default() -> Self {
2178 Linearization {
2179 constant_term: E::default(),
2180 index_terms: vec![],
2181 }
2182 }
2183}
2184
2185impl<A, Column: Copy> Linearization<A, Column> {
2186 pub fn map<B, F: Fn(&A) -> B>(&self, f: F) -> Linearization<B, Column> {
2188 Linearization {
2189 constant_term: f(&self.constant_term),
2190 index_terms: self.index_terms.iter().map(|(c, x)| (*c, f(x))).collect(),
2191 }
2192 }
2193}
2194
2195impl<F: FftField, Column: PartialEq + Copy, ChallengeTerm: Copy>
2196 Linearization<Expr<ConstantExpr<F, ChallengeTerm>, Column>, Column>
2197{
2198 pub fn evaluate_constants<
2201 'a,
2202 Challenge: Index<ChallengeTerm, Output = F>,
2203 Environment: ColumnEnvironment<'a, F, ChallengeTerm, Challenge, Column = Column>,
2204 >(
2205 &self,
2206 env: &Environment,
2207 ) -> Linearization<Expr<F, Column>, Column> {
2208 self.map(|e| e.evaluate_constants(env))
2209 }
2210}
2211
2212impl<F: FftField, Column: Copy + Debug, ChallengeTerm: Copy>
2213 Linearization<Vec<PolishToken<F, Column, ChallengeTerm>>, Column>
2214{
2215 pub fn to_polynomial<
2218 'a,
2219 Challenge: Index<ChallengeTerm, Output = F>,
2220 ColEvaluations: ColumnEvaluations<F, Column = Column>,
2221 Environment: ColumnEnvironment<'a, F, ChallengeTerm, Challenge, Column = Column>,
2222 >(
2223 &self,
2224 env: &Environment,
2225 pt: F,
2226 evals: &ColEvaluations,
2227 ) -> (F, Evaluations<F, D<F>>) {
2228 let cs = env.get_constants();
2229 let chals = env.get_challenges();
2230 let d1 = env.get_domain(Domain::D1);
2231 let n = d1.size();
2232 let mut res = vec![F::zero(); n];
2233 self.index_terms.iter().for_each(|(idx, c)| {
2234 let c = PolishToken::evaluate(c, d1, pt, evals, cs, chals).unwrap();
2235 let e = env
2236 .get_column(idx)
2237 .unwrap_or_else(|| panic!("Index polynomial {idx:?} not found"));
2238 let scale = e.evals.len() / n;
2239 o1_utils::cfg_iter_mut!(res)
2240 .enumerate()
2241 .for_each(|(i, r)| *r += c * e.evals[scale * i]);
2242 });
2243 let p = Evaluations::<F, D<F>>::from_vec_and_domain(res, d1);
2244 (
2245 PolishToken::evaluate(&self.constant_term, d1, pt, evals, cs, chals).unwrap(),
2246 p,
2247 )
2248 }
2249}
2250
2251impl<F: FftField, Column: Debug + PartialEq + Copy, ChallengeTerm: Copy>
2252 Linearization<Expr<ConstantExpr<F, ChallengeTerm>, Column>, Column>
2253{
2254 pub fn to_polynomial<
2257 'a,
2258 Challenge: Index<ChallengeTerm, Output = F>,
2259 ColEvaluations: ColumnEvaluations<F, Column = Column>,
2260 Environment: ColumnEnvironment<'a, F, ChallengeTerm, Challenge, Column = Column>,
2261 >(
2262 &self,
2263 env: &Environment,
2264 pt: F,
2265 evals: &ColEvaluations,
2266 ) -> (F, DensePolynomial<F>) {
2267 let cs = env.get_constants();
2268 let chals = env.get_challenges();
2269 let d1 = env.get_domain(Domain::D1);
2270 let n = d1.size();
2271 let mut res = vec![F::zero(); n];
2272 self.index_terms.iter().for_each(|(idx, c)| {
2273 let c = c.evaluate_(d1, pt, evals, cs, chals).unwrap();
2274 let e = env
2275 .get_column(idx)
2276 .unwrap_or_else(|| panic!("Index polynomial {idx:?} not found"));
2277 let scale = e.evals.len() / n;
2278 o1_utils::cfg_iter_mut!(res)
2279 .enumerate()
2280 .for_each(|(i, r)| *r += c * e.evals[scale * i])
2281 });
2282 let p = Evaluations::<F, D<F>>::from_vec_and_domain(res, d1).interpolate();
2283 (
2284 self.constant_term
2285 .evaluate_(d1, pt, evals, cs, chals)
2286 .unwrap(),
2287 p,
2288 )
2289 }
2290}
2291
2292type Monomials<F, Column> = HashMap<Vec<Variable<Column>>, Expr<F, Column>>;
2293
2294fn mul_monomials<
2295 F: Neg<Output = F> + Clone + One + Zero + PartialEq,
2296 Column: Ord + Copy + core::hash::Hash,
2297>(
2298 e1: &Monomials<F, Column>,
2299 e2: &Monomials<F, Column>,
2300) -> Monomials<F, Column>
2301where
2302 ExprInner<F, Column>: Literal,
2303 <ExprInner<F, Column> as Literal>::F: Field,
2304{
2305 let mut res: HashMap<_, Expr<F, Column>> = HashMap::new();
2306 for (m1, c1) in e1.iter() {
2307 for (m2, c2) in e2.iter() {
2308 let mut m = m1.clone();
2309 m.extend(m2);
2310 m.sort();
2311 let c1c2 = c1.clone() * c2.clone();
2312 let v = res.entry(m).or_insert_with(Expr::<F, Column>::zero);
2313 *v = v.clone() + c1c2;
2314 }
2315 }
2316 res
2317}
2318
2319impl<
2320 F: Neg<Output = F> + Clone + One + Zero + PartialEq,
2321 Column: Ord + Copy + core::hash::Hash,
2322 > Expr<F, Column>
2323where
2324 ExprInner<F, Column>: Literal,
2325 <ExprInner<F, Column> as Literal>::F: Field,
2326{
2327 fn is_constant(&self, evaluated: &HashSet<Column>) -> bool {
2332 use ExprInner::*;
2333 use Operations::*;
2334 match self {
2335 Pow(x, _) => x.is_constant(evaluated),
2336 Square(x) => x.is_constant(evaluated),
2337 Atom(Constant(_)) => true,
2338 Atom(Cell(v)) => evaluated.contains(&v.col),
2339 Double(x) => x.is_constant(evaluated),
2340 Add(x, y) | Sub(x, y) | Mul(x, y) => {
2341 x.is_constant(evaluated) && y.is_constant(evaluated)
2342 }
2343 Atom(VanishesOnZeroKnowledgeAndPreviousRows) => true,
2344 Atom(UnnormalizedLagrangeBasis(_)) => true,
2345 Cache(_, x) => x.is_constant(evaluated),
2346 IfFeature(_, e1, e2) => e1.is_constant(evaluated) && e2.is_constant(evaluated),
2347 }
2348 }
2349
2350 fn monomials(&self, ev: &HashSet<Column>) -> HashMap<Vec<Variable<Column>>, Expr<F, Column>> {
2351 let sing = |v: Vec<Variable<Column>>, c: Expr<F, Column>| {
2352 let mut h = HashMap::new();
2353 h.insert(v, c);
2354 h
2355 };
2356 let constant = |e: Expr<F, Column>| sing(vec![], e);
2357 use ExprInner::*;
2358 use Operations::*;
2359
2360 if self.is_constant(ev) {
2361 return constant(self.clone());
2362 }
2363
2364 match self {
2365 Pow(x, d) => {
2366 let mut acc = sing(vec![], Expr::<F, Column>::one());
2368 let mut acc_is_one = true;
2369 let x = x.monomials(ev);
2370
2371 for i in (0..u64::BITS).rev() {
2372 if !acc_is_one {
2373 let acc2 = mul_monomials(&acc, &acc);
2374 acc = acc2;
2375 }
2376
2377 if (d >> i) & 1 == 1 {
2378 let res = mul_monomials(&acc, &x);
2379 acc = res;
2380 acc_is_one = false;
2381 }
2382 }
2383 acc
2384 }
2385 Double(e) => {
2386 HashMap::from_iter(e.monomials(ev).into_iter().map(|(m, c)| (m, c.double())))
2387 }
2388 Cache(_, e) => e.monomials(ev),
2389 Atom(UnnormalizedLagrangeBasis(i)) => constant(Atom(UnnormalizedLagrangeBasis(*i))),
2390 Atom(VanishesOnZeroKnowledgeAndPreviousRows) => {
2391 constant(Atom(VanishesOnZeroKnowledgeAndPreviousRows))
2392 }
2393 Atom(Constant(c)) => constant(Atom(Constant(c.clone()))),
2394 Atom(Cell(var)) => sing(vec![*var], Atom(Constant(F::one()))),
2395 Add(e1, e2) => {
2396 let mut res = e1.monomials(ev);
2397 for (m, c) in e2.monomials(ev) {
2398 let v = match res.remove(&m) {
2399 None => c,
2400 Some(v) => v + c,
2401 };
2402 res.insert(m, v);
2403 }
2404 res
2405 }
2406 Sub(e1, e2) => {
2407 let mut res = e1.monomials(ev);
2408 for (m, c) in e2.monomials(ev) {
2409 let v = match res.remove(&m) {
2410 None => -c, Some(v) => v - c,
2412 };
2413 res.insert(m, v);
2414 }
2415 res
2416 }
2417 Mul(e1, e2) => {
2418 let e1 = e1.monomials(ev);
2419 let e2 = e2.monomials(ev);
2420 mul_monomials(&e1, &e2)
2421 }
2422 Square(x) => {
2423 let x = x.monomials(ev);
2424 mul_monomials(&x, &x)
2425 }
2426 IfFeature(feature, e1, e2) => {
2427 let mut res = HashMap::new();
2428 let e1_monomials = e1.monomials(ev);
2429 let mut e2_monomials = e2.monomials(ev);
2430 for (m, c) in e1_monomials.into_iter() {
2431 let else_branch = match e2_monomials.remove(&m) {
2432 None => Expr::zero(),
2433 Some(c) => c,
2434 };
2435 let expr = Expr::IfFeature(*feature, Box::new(c), Box::new(else_branch));
2436 res.insert(m, expr);
2437 }
2438 for (m, c) in e2_monomials.into_iter() {
2439 let expr = Expr::IfFeature(*feature, Box::new(Expr::zero()), Box::new(c));
2440 res.insert(m, expr);
2441 }
2442 res
2443 }
2444 }
2445 }
2446
2447 pub fn linearize(
2474 &self,
2475 evaluated: HashSet<Column>,
2476 ) -> Result<Linearization<Expr<F, Column>, Column>, ExprError<Column>> {
2477 let mut res: HashMap<Column, Expr<F, Column>> = HashMap::new();
2478 let mut constant_term: Expr<F, Column> = Self::zero();
2479 let monomials = self.monomials(&evaluated);
2480
2481 for (m, c) in monomials {
2482 let (evaluated, mut unevaluated): (Vec<_>, _) =
2483 m.into_iter().partition(|v| evaluated.contains(&v.col));
2484 let c = evaluated
2485 .into_iter()
2486 .fold(c, |acc, v| acc * Expr::Atom(ExprInner::Cell(v)));
2487 if unevaluated.is_empty() {
2488 constant_term += c;
2489 } else if unevaluated.len() == 1 {
2490 let var = unevaluated.remove(0);
2491 match var.row {
2492 Next => {
2493 return Err(ExprError::MissingEvaluation(var.col, var.row));
2494 }
2495 Curr => {
2496 let e = match res.remove(&var.col) {
2497 Some(v) => v + c,
2498 None => c,
2499 };
2500 res.insert(var.col, e);
2501 }
2513 }
2514 } else {
2515 return Err(ExprError::FailedLinearization(unevaluated));
2516 }
2517 }
2518 Ok(Linearization {
2519 constant_term,
2520 index_terms: res.into_iter().collect(),
2521 })
2522 }
2523}
2524
2525impl<T: Literal> Zero for Operations<T>
2528where
2529 T::F: Field,
2530{
2531 fn zero() -> Self {
2532 Self::literal(T::F::zero())
2533 }
2534
2535 fn is_zero(&self) -> bool {
2536 if let Some(x) = self.to_literal_ref() {
2537 x.is_zero()
2538 } else {
2539 false
2540 }
2541 }
2542}
2543
2544impl<T: Literal + PartialEq> One for Operations<T>
2545where
2546 T::F: Field,
2547{
2548 fn one() -> Self {
2549 Self::literal(T::F::one())
2550 }
2551
2552 fn is_one(&self) -> bool {
2553 if let Some(x) = self.to_literal_ref() {
2554 x.is_one()
2555 } else {
2556 false
2557 }
2558 }
2559}
2560
2561impl<T: Literal> Neg for Operations<T>
2562where
2563 T::F: One + Neg<Output = T::F> + Copy,
2564{
2565 type Output = Self;
2566
2567 fn neg(self) -> Self {
2568 match self.to_literal() {
2569 Ok(x) => Self::literal(x.neg()),
2570 Err(x) => Operations::Mul(Box::new(Self::literal(T::F::one().neg())), Box::new(x)),
2571 }
2572 }
2573}
2574
2575impl<T: Literal> Add<Self> for Operations<T>
2576where
2577 T::F: Field,
2578{
2579 type Output = Self;
2580 fn add(self, other: Self) -> Self {
2581 if self.is_zero() {
2582 return other;
2583 }
2584 if other.is_zero() {
2585 return self;
2586 }
2587 let (x, y) = {
2588 match (self.to_literal(), other.to_literal()) {
2589 (Ok(x), Ok(y)) => return Self::literal(x + y),
2590 (Ok(x), Err(y)) => (Self::literal(x), y),
2591 (Err(x), Ok(y)) => (x, Self::literal(y)),
2592 (Err(x), Err(y)) => (x, y),
2593 }
2594 };
2595 Operations::Add(Box::new(x), Box::new(y))
2596 }
2597}
2598
2599impl<T: Literal> Sub<Self> for Operations<T>
2600where
2601 T::F: Field,
2602{
2603 type Output = Self;
2604 fn sub(self, other: Self) -> Self {
2605 if other.is_zero() {
2606 return self;
2607 }
2608 let (x, y) = {
2609 match (self.to_literal(), other.to_literal()) {
2610 (Ok(x), Ok(y)) => return Self::literal(x - y),
2611 (Ok(x), Err(y)) => (Self::literal(x), y),
2612 (Err(x), Ok(y)) => (x, Self::literal(y)),
2613 (Err(x), Err(y)) => (x, y),
2614 }
2615 };
2616 Operations::Sub(Box::new(x), Box::new(y))
2617 }
2618}
2619
2620impl<T: Literal + PartialEq> Mul<Self> for Operations<T>
2621where
2622 T::F: Field,
2623{
2624 type Output = Self;
2625 fn mul(self, other: Self) -> Self {
2626 if self.is_zero() || other.is_zero() {
2627 return Self::zero();
2628 }
2629
2630 if self.is_one() {
2631 return other;
2632 }
2633 if other.is_one() {
2634 return self;
2635 }
2636 let (x, y) = {
2637 match (self.to_literal(), other.to_literal()) {
2638 (Ok(x), Ok(y)) => return Self::literal(x * y),
2639 (Ok(x), Err(y)) => (Self::literal(x), y),
2640 (Err(x), Ok(y)) => (x, Self::literal(y)),
2641 (Err(x), Err(y)) => (x, y),
2642 }
2643 };
2644 Operations::Mul(Box::new(x), Box::new(y))
2645 }
2646}
2647
2648impl<F: Zero + Clone, Column: Clone> AddAssign<Expr<F, Column>> for Expr<F, Column>
2649where
2650 ExprInner<F, Column>: Literal,
2651 <ExprInner<F, Column> as Literal>::F: Field,
2652{
2653 fn add_assign(&mut self, other: Self) {
2654 if self.is_zero() {
2655 *self = other;
2656 } else if !other.is_zero() {
2657 *self = Expr::Add(Box::new(self.clone()), Box::new(other));
2658 }
2659 }
2660}
2661
2662impl<F, Column> MulAssign<Expr<F, Column>> for Expr<F, Column>
2663where
2664 F: Zero + One + PartialEq + Clone,
2665 Column: PartialEq + Clone,
2666 ExprInner<F, Column>: Literal,
2667 <ExprInner<F, Column> as Literal>::F: Field,
2668{
2669 fn mul_assign(&mut self, other: Self) {
2670 if self.is_zero() || other.is_zero() {
2671 *self = Self::zero();
2672 } else if self.is_one() {
2673 *self = other;
2674 } else if !other.is_one() {
2675 *self = Expr::Mul(Box::new(self.clone()), Box::new(other));
2676 }
2677 }
2678}
2679
2680impl<F: Field, Column> From<u64> for Expr<F, Column> {
2681 fn from(x: u64) -> Self {
2682 Expr::Atom(ExprInner::Constant(F::from(x)))
2683 }
2684}
2685
2686impl<'a, F: Field, Column, ChallengeTerm: AlphaChallengeTerm<'a>> From<u64>
2687 for Expr<ConstantExpr<F, ChallengeTerm>, Column>
2688{
2689 fn from(x: u64) -> Self {
2690 ConstantTerm::Literal(F::from(x)).into()
2691 }
2692}
2693
2694impl<F: Field, ChallengeTerm> From<u64> for ConstantExpr<F, ChallengeTerm> {
2695 fn from(x: u64) -> Self {
2696 ConstantTerm::Literal(F::from(x)).into()
2697 }
2698}
2699
2700impl<'a, F: Field, Column: PartialEq + Copy, ChallengeTerm: AlphaChallengeTerm<'a>> Mul<F>
2701 for Expr<ConstantExpr<F, ChallengeTerm>, Column>
2702{
2703 type Output = Expr<ConstantExpr<F, ChallengeTerm>, Column>;
2704
2705 fn mul(self, y: F) -> Self::Output {
2706 Expr::from(ConstantTerm::Literal(y)) * self
2707 }
2708}
2709
2710pub trait FormattedOutput: Sized {
2715 fn is_alpha(&self) -> bool;
2716 fn ocaml(&self, cache: &mut HashMap<CacheId, Self>) -> String;
2717 fn latex(&self, cache: &mut HashMap<CacheId, Self>) -> String;
2718 fn text(&self, cache: &mut HashMap<CacheId, Self>) -> String;
2719}
2720
2721impl<'a, ChallengeTerm> FormattedOutput for ChallengeTerm
2722where
2723 ChallengeTerm: AlphaChallengeTerm<'a>,
2724{
2725 fn is_alpha(&self) -> bool {
2726 self.eq(&ChallengeTerm::ALPHA)
2727 }
2728 fn ocaml(&self, _cache: &mut HashMap<CacheId, Self>) -> String {
2729 self.to_string()
2730 }
2731
2732 fn latex(&self, _cache: &mut HashMap<CacheId, Self>) -> String {
2733 "\\".to_string() + &self.to_string()
2734 }
2735
2736 fn text(&self, _cache: &mut HashMap<CacheId, Self>) -> String {
2737 self.to_string()
2738 }
2739}
2740
2741impl<F: PrimeField> FormattedOutput for ConstantTerm<F> {
2742 fn is_alpha(&self) -> bool {
2743 false
2744 }
2745 fn ocaml(&self, _cache: &mut HashMap<CacheId, Self>) -> String {
2746 use ConstantTerm::*;
2747 match self {
2748 EndoCoefficient => "endo_coefficient".to_string(),
2749 Mds { row, col } => format!("mds({row}, {col})"),
2750 Literal(x) => format!(
2751 "field(\"{:#066X}\")",
2752 Into::<num_bigint::BigUint>::into(x.into_bigint())
2753 ),
2754 }
2755 }
2756
2757 fn latex(&self, _cache: &mut HashMap<CacheId, Self>) -> String {
2758 use ConstantTerm::*;
2759 match self {
2760 EndoCoefficient => "endo\\_coefficient".to_string(),
2761 Mds { row, col } => format!("mds({row}, {col})"),
2762 Literal(x) => format!("\\mathbb{{F}}({})", x.into_bigint().into()),
2763 }
2764 }
2765
2766 fn text(&self, _cache: &mut HashMap<CacheId, Self>) -> String {
2767 use ConstantTerm::*;
2768 match self {
2769 EndoCoefficient => "endo_coefficient".to_string(),
2770 Mds { row, col } => format!("mds({row}, {col})"),
2771 Literal(x) => format!("0x{}", x.to_hex()),
2772 }
2773 }
2774}
2775
2776impl<'a, F: PrimeField, ChallengeTerm> FormattedOutput for ConstantExprInner<F, ChallengeTerm>
2777where
2778 ChallengeTerm: AlphaChallengeTerm<'a>,
2779{
2780 fn is_alpha(&self) -> bool {
2781 use ConstantExprInner::*;
2782 match self {
2783 Challenge(x) => x.is_alpha(),
2784 Constant(x) => x.is_alpha(),
2785 }
2786 }
2787 fn ocaml(&self, cache: &mut HashMap<CacheId, Self>) -> String {
2788 use ConstantExprInner::*;
2789 match self {
2790 Challenge(x) => {
2791 let mut inner_cache = HashMap::new();
2792 let res = x.ocaml(&mut inner_cache);
2793 inner_cache.into_iter().for_each(|(k, v)| {
2794 let _ = cache.insert(k, Challenge(v));
2795 });
2796 res
2797 }
2798 Constant(x) => {
2799 let mut inner_cache = HashMap::new();
2800 let res = x.ocaml(&mut inner_cache);
2801 inner_cache.into_iter().for_each(|(k, v)| {
2802 let _ = cache.insert(k, Constant(v));
2803 });
2804 res
2805 }
2806 }
2807 }
2808 fn latex(&self, cache: &mut HashMap<CacheId, Self>) -> String {
2809 use ConstantExprInner::*;
2810 match self {
2811 Challenge(x) => {
2812 let mut inner_cache = HashMap::new();
2813 let res = x.latex(&mut inner_cache);
2814 inner_cache.into_iter().for_each(|(k, v)| {
2815 let _ = cache.insert(k, Challenge(v));
2816 });
2817 res
2818 }
2819 Constant(x) => {
2820 let mut inner_cache = HashMap::new();
2821 let res = x.latex(&mut inner_cache);
2822 inner_cache.into_iter().for_each(|(k, v)| {
2823 let _ = cache.insert(k, Constant(v));
2824 });
2825 res
2826 }
2827 }
2828 }
2829 fn text(&self, cache: &mut HashMap<CacheId, Self>) -> String {
2830 use ConstantExprInner::*;
2831 match self {
2832 Challenge(x) => {
2833 let mut inner_cache = HashMap::new();
2834 let res = x.text(&mut inner_cache);
2835 inner_cache.into_iter().for_each(|(k, v)| {
2836 let _ = cache.insert(k, Challenge(v));
2837 });
2838 res
2839 }
2840 Constant(x) => {
2841 let mut inner_cache = HashMap::new();
2842 let res = x.text(&mut inner_cache);
2843 inner_cache.into_iter().for_each(|(k, v)| {
2844 let _ = cache.insert(k, Constant(v));
2845 });
2846 res
2847 }
2848 }
2849 }
2850}
2851
2852impl<Column: FormattedOutput + Debug> FormattedOutput for Variable<Column> {
2853 fn is_alpha(&self) -> bool {
2854 false
2855 }
2856
2857 fn ocaml(&self, _cache: &mut HashMap<CacheId, Self>) -> String {
2858 format!("var({:?}, {:?})", self.col, self.row)
2859 }
2860
2861 fn latex(&self, _cache: &mut HashMap<CacheId, Self>) -> String {
2862 let col = self.col.latex(&mut HashMap::new());
2863 match self.row {
2864 Curr => col,
2865 Next => format!("\\tilde{{{col}}}"),
2866 }
2867 }
2868
2869 fn text(&self, _cache: &mut HashMap<CacheId, Self>) -> String {
2870 let col = self.col.text(&mut HashMap::new());
2871 match self.row {
2872 Curr => format!("Curr({col})"),
2873 Next => format!("Next({col})"),
2874 }
2875 }
2876}
2877
2878impl<T: FormattedOutput + Clone> FormattedOutput for Operations<T> {
2879 fn is_alpha(&self) -> bool {
2880 match self {
2881 Operations::Atom(x) => x.is_alpha(),
2882 _ => false,
2883 }
2884 }
2885 fn ocaml(&self, cache: &mut HashMap<CacheId, Self>) -> String {
2886 use Operations::*;
2887 match self {
2888 Atom(x) => {
2889 let mut inner_cache = HashMap::new();
2890 let res = x.ocaml(&mut inner_cache);
2891 inner_cache.into_iter().for_each(|(k, v)| {
2892 let _ = cache.insert(k, Atom(v));
2893 });
2894 res
2895 }
2896 Pow(x, n) => {
2897 if x.is_alpha() {
2898 format!("alpha_pow({n})")
2899 } else {
2900 format!("pow({}, {n})", x.ocaml(cache))
2901 }
2902 }
2903 Add(x, y) => format!("({} + {})", x.ocaml(cache), y.ocaml(cache)),
2904 Mul(x, y) => format!("({} * {})", x.ocaml(cache), y.ocaml(cache)),
2905 Sub(x, y) => format!("({} - {})", x.ocaml(cache), y.ocaml(cache)),
2906 Double(x) => format!("double({})", x.ocaml(cache)),
2907 Square(x) => format!("square({})", x.ocaml(cache)),
2908 Cache(id, e) => {
2909 cache.insert(*id, e.as_ref().clone());
2910 id.var_name()
2911 }
2912 IfFeature(feature, e1, e2) => {
2913 format!(
2914 "if_feature({:?}, (fun () -> {}), (fun () -> {}))",
2915 feature,
2916 e1.ocaml(cache),
2917 e2.ocaml(cache)
2918 )
2919 }
2920 }
2921 }
2922
2923 fn latex(&self, cache: &mut HashMap<CacheId, Self>) -> String {
2924 use Operations::*;
2925 match self {
2926 Atom(x) => {
2927 let mut inner_cache = HashMap::new();
2928 let res = x.latex(&mut inner_cache);
2929 inner_cache.into_iter().for_each(|(k, v)| {
2930 let _ = cache.insert(k, Atom(v));
2931 });
2932 res
2933 }
2934 Pow(x, n) => format!("{}^{{{n}}}", x.latex(cache)),
2935 Add(x, y) => format!("({} + {})", x.latex(cache), y.latex(cache)),
2936 Mul(x, y) => format!("({} \\cdot {})", x.latex(cache), y.latex(cache)),
2937 Sub(x, y) => format!("({} - {})", x.latex(cache), y.latex(cache)),
2938 Double(x) => format!("2 ({})", x.latex(cache)),
2939 Square(x) => format!("({})^2", x.latex(cache)),
2940 Cache(id, e) => {
2941 cache.insert(*id, e.as_ref().clone());
2942 id.var_name()
2943 }
2944 IfFeature(feature, _, _) => format!("{feature:?}"),
2945 }
2946 }
2947
2948 fn text(&self, cache: &mut HashMap<CacheId, Self>) -> String {
2949 use Operations::*;
2950 match self {
2951 Atom(x) => {
2952 let mut inner_cache = HashMap::new();
2953 let res = x.text(&mut inner_cache);
2954 inner_cache.into_iter().for_each(|(k, v)| {
2955 let _ = cache.insert(k, Atom(v));
2956 });
2957 res
2958 }
2959 Pow(x, n) => format!("{}^{n}", x.text(cache)),
2960 Add(x, y) => format!("({} + {})", x.text(cache), y.text(cache)),
2961 Mul(x, y) => format!("({} * {})", x.text(cache), y.text(cache)),
2962 Sub(x, y) => format!("({} - {})", x.text(cache), y.text(cache)),
2963 Double(x) => format!("double({})", x.text(cache)),
2964 Square(x) => format!("square({})", x.text(cache)),
2965 Cache(id, e) => {
2966 cache.insert(*id, e.as_ref().clone());
2967 id.var_name()
2968 }
2969 IfFeature(feature, _, _) => format!("{feature:?}"),
2970 }
2971 }
2972}
2973
2974impl<'a, F, Column: FormattedOutput + Debug + Clone, ChallengeTerm> FormattedOutput
2975 for Expr<ConstantExpr<F, ChallengeTerm>, Column>
2976where
2977 F: PrimeField,
2978 ChallengeTerm: AlphaChallengeTerm<'a>,
2979{
2980 fn is_alpha(&self) -> bool {
2981 use ExprInner::*;
2982 use Operations::*;
2983 match self {
2984 Atom(Constant(x)) => x.is_alpha(),
2985 _ => false,
2986 }
2987 }
2988 fn ocaml(
2992 &self,
2993 cache: &mut HashMap<CacheId, Expr<ConstantExpr<F, ChallengeTerm>, Column>>,
2994 ) -> String {
2995 use ExprInner::*;
2996 use Operations::*;
2997 match self {
2998 Double(x) => format!("double({})", x.ocaml(cache)),
2999 Atom(Constant(x)) => {
3000 let mut inner_cache = HashMap::new();
3001 let res = x.ocaml(&mut inner_cache);
3002 inner_cache.into_iter().for_each(|(k, v)| {
3003 let _ = cache.insert(k, Atom(Constant(v)));
3004 });
3005 res
3006 }
3007 Atom(Cell(v)) => format!("cell({})", v.ocaml(&mut HashMap::new())),
3008 Atom(UnnormalizedLagrangeBasis(i)) => {
3009 format!("unnormalized_lagrange_basis({}, {})", i.zk_rows, i.offset)
3010 }
3011 Atom(VanishesOnZeroKnowledgeAndPreviousRows) => {
3012 "vanishes_on_zero_knowledge_and_previous_rows".to_string()
3013 }
3014 Add(x, y) => format!("({} + {})", x.ocaml(cache), y.ocaml(cache)),
3015 Mul(x, y) => format!("({} * {})", x.ocaml(cache), y.ocaml(cache)),
3016 Sub(x, y) => format!("({} - {})", x.ocaml(cache), y.ocaml(cache)),
3017 Pow(x, d) => format!("pow({}, {d})", x.ocaml(cache)),
3018 Square(x) => format!("square({})", x.ocaml(cache)),
3019 Cache(id, e) => {
3020 cache.insert(*id, e.as_ref().clone());
3021 id.var_name()
3022 }
3023 IfFeature(feature, e1, e2) => {
3024 format!(
3025 "if_feature({:?}, (fun () -> {}), (fun () -> {}))",
3026 feature,
3027 e1.ocaml(cache),
3028 e2.ocaml(cache)
3029 )
3030 }
3031 }
3032 }
3033
3034 fn latex(
3035 &self,
3036 cache: &mut HashMap<CacheId, Expr<ConstantExpr<F, ChallengeTerm>, Column>>,
3037 ) -> String {
3038 use ExprInner::*;
3039 use Operations::*;
3040 match self {
3041 Double(x) => format!("2 ({})", x.latex(cache)),
3042 Atom(Constant(x)) => {
3043 let mut inner_cache = HashMap::new();
3044 let res = x.latex(&mut inner_cache);
3045 inner_cache.into_iter().for_each(|(k, v)| {
3046 let _ = cache.insert(k, Atom(Constant(v)));
3047 });
3048 res
3049 }
3050 Atom(Cell(v)) => v.latex(&mut HashMap::new()),
3051 Atom(UnnormalizedLagrangeBasis(RowOffset {
3052 zk_rows: true,
3053 offset: i,
3054 })) => {
3055 format!("unnormalized\\_lagrange\\_basis(zk\\_rows + {})", *i)
3056 }
3057 Atom(UnnormalizedLagrangeBasis(RowOffset {
3058 zk_rows: false,
3059 offset: i,
3060 })) => {
3061 format!("unnormalized\\_lagrange\\_basis({})", *i)
3062 }
3063 Atom(VanishesOnZeroKnowledgeAndPreviousRows) => {
3064 "vanishes\\_on\\_zero\\_knowledge\\_and\\_previous\\_row".to_string()
3065 }
3066 Add(x, y) => format!("({} + {})", x.latex(cache), y.latex(cache)),
3067 Mul(x, y) => format!("({} \\cdot {})", x.latex(cache), y.latex(cache)),
3068 Sub(x, y) => format!("({} - {})", x.latex(cache), y.latex(cache)),
3069 Pow(x, d) => format!("{}^{{{d}}}", x.latex(cache)),
3070 Square(x) => format!("({})^2", x.latex(cache)),
3071 Cache(id, e) => {
3072 cache.insert(*id, e.as_ref().clone());
3073 id.latex_name()
3074 }
3075 IfFeature(feature, _, _) => format!("{feature:?}"),
3076 }
3077 }
3078
3079 fn text(
3082 &self,
3083 cache: &mut HashMap<CacheId, Expr<ConstantExpr<F, ChallengeTerm>, Column>>,
3084 ) -> String {
3085 use ExprInner::*;
3086 use Operations::*;
3087 match self {
3088 Double(x) => format!("double({})", x.text(cache)),
3089 Atom(Constant(x)) => {
3090 let mut inner_cache = HashMap::new();
3091 let res = x.text(&mut inner_cache);
3092 inner_cache.into_iter().for_each(|(k, v)| {
3093 let _ = cache.insert(k, Atom(Constant(v)));
3094 });
3095 res
3096 }
3097 Atom(Cell(v)) => v.text(&mut HashMap::new()),
3098 Atom(UnnormalizedLagrangeBasis(RowOffset {
3099 zk_rows: true,
3100 offset: i,
3101 })) => match i.cmp(&0) {
3102 Ordering::Greater => format!("unnormalized_lagrange_basis(zk_rows + {})", *i),
3103 Ordering::Equal => "unnormalized_lagrange_basis(zk_rows)".to_string(),
3104 Ordering::Less => format!("unnormalized_lagrange_basis(zk_rows - {})", (-*i)),
3105 },
3106 Atom(UnnormalizedLagrangeBasis(RowOffset {
3107 zk_rows: false,
3108 offset: i,
3109 })) => {
3110 format!("unnormalized_lagrange_basis({})", *i)
3111 }
3112 Atom(VanishesOnZeroKnowledgeAndPreviousRows) => {
3113 "vanishes_on_zero_knowledge_and_previous_rows".to_string()
3114 }
3115 Add(x, y) => format!("({} + {})", x.text(cache), y.text(cache)),
3116 Mul(x, y) => format!("({} * {})", x.text(cache), y.text(cache)),
3117 Sub(x, y) => format!("({} - {})", x.text(cache), y.text(cache)),
3118 Pow(x, d) => format!("pow({}, {d})", x.text(cache)),
3119 Square(x) => format!("square({})", x.text(cache)),
3120 Cache(id, e) => {
3121 cache.insert(*id, e.as_ref().clone());
3122 id.var_name()
3123 }
3124 IfFeature(feature, _, _) => format!("{feature:?}"),
3125 }
3126 }
3127}
3128
3129impl<'a, F, Column: FormattedOutput + Debug + Clone, ChallengeTerm>
3130 Expr<ConstantExpr<F, ChallengeTerm>, Column>
3131where
3132 F: PrimeField,
3133 ChallengeTerm: AlphaChallengeTerm<'a>,
3134{
3135 pub fn latex_str(&self) -> Vec<String> {
3138 let mut env = HashMap::new();
3139 let e = self.latex(&mut env);
3140
3141 let mut env: Vec<_> = env.into_iter().collect();
3142 env.sort_by_key(|(x, _)| *x);
3145
3146 let mut res = vec![];
3147 for (k, v) in env {
3148 let mut rhs = v.latex_str();
3149 let last = rhs.pop().expect("returned an empty expression");
3150 res.push(format!("{} = {last}", k.latex_name()));
3151 res.extend(rhs);
3152 }
3153 res.push(e);
3154 res
3155 }
3156
3157 pub fn ocaml_str(&self) -> String {
3159 let mut env = HashMap::new();
3160 let e = self.ocaml(&mut env);
3161
3162 let mut env: Vec<_> = env.into_iter().collect();
3163 env.sort_by_key(|(x, _)| *x);
3166
3167 let mut res = String::new();
3168 for (k, v) in env {
3169 let rhs = v.ocaml_str();
3170 let cached = format!("let {} = {rhs} in ", k.var_name());
3171 res.push_str(&cached);
3172 }
3173
3174 res.push_str(&e);
3175 res
3176 }
3177}
3178
3179pub mod constraints {
3185 use o1_utils::Two;
3186
3187 use crate::circuits::argument::ArgumentData;
3188 use core::fmt;
3189
3190 use super::*;
3191 use crate::circuits::berkeley_columns::{coeff, witness};
3192
3193 pub trait ExprOps<F, ChallengeTerm>:
3197 Add<Output = Self>
3198 + Sub<Output = Self>
3199 + Neg<Output = Self>
3200 + Mul<Output = Self>
3201 + AddAssign<Self>
3202 + MulAssign<Self>
3203 + Clone
3204 + Zero
3205 + One
3206 + From<u64>
3207 + fmt::Debug
3208 + fmt::Display
3209 where
3211 Self: core::marker::Sized,
3212 {
3213 fn two_pow(pow: u64) -> Self;
3215
3216 fn two_to_limb() -> Self;
3218
3219 fn two_to_2limb() -> Self;
3221
3222 fn two_to_3limb() -> Self;
3224
3225 fn double(&self) -> Self;
3227
3228 fn square(&self) -> Self;
3230
3231 fn pow(&self, p: u64) -> Self;
3233
3234 fn boolean(&self) -> Self;
3236
3237 fn crumb(&self) -> Self;
3239
3240 fn literal(x: F) -> Self;
3242
3243 fn witness(row: CurrOrNext, col: usize, env: Option<&ArgumentData<F>>) -> Self;
3245
3246 fn coeff(col: usize, env: Option<&ArgumentData<F>>) -> Self;
3248
3249 fn constant(expr: ConstantExpr<F, ChallengeTerm>, env: Option<&ArgumentData<F>>) -> Self;
3251
3252 fn cache(&self, cache: &mut Cache) -> Self;
3254 }
3255 impl<F> ExprOps<F, BerkeleyChallengeTerm>
3258 for Expr<ConstantExpr<F, BerkeleyChallengeTerm>, berkeley_columns::Column>
3259 where
3260 F: PrimeField,
3261 Expr<ConstantExpr<F, BerkeleyChallengeTerm>, berkeley_columns::Column>: core::fmt::Display,
3263 {
3264 fn two_pow(pow: u64) -> Self {
3265 Expr::<ConstantExpr<F, BerkeleyChallengeTerm>, berkeley_columns::Column>::literal(
3266 <F as Two<F>>::two_pow(pow),
3267 )
3268 }
3269
3270 fn two_to_limb() -> Self {
3271 Expr::<ConstantExpr<F, BerkeleyChallengeTerm>, berkeley_columns::Column>::literal(
3272 KimchiForeignElement::<F>::two_to_limb(),
3273 )
3274 }
3275
3276 fn two_to_2limb() -> Self {
3277 Expr::<ConstantExpr<F, BerkeleyChallengeTerm>, berkeley_columns::Column>::literal(
3278 KimchiForeignElement::<F>::two_to_2limb(),
3279 )
3280 }
3281
3282 fn two_to_3limb() -> Self {
3283 Expr::<ConstantExpr<F, BerkeleyChallengeTerm>, berkeley_columns::Column>::literal(
3284 KimchiForeignElement::<F>::two_to_3limb(),
3285 )
3286 }
3287
3288 fn double(&self) -> Self {
3289 Expr::double(self.clone())
3290 }
3291
3292 fn square(&self) -> Self {
3293 Expr::square(self.clone())
3294 }
3295
3296 fn pow(&self, p: u64) -> Self {
3297 Expr::pow(self.clone(), p)
3298 }
3299
3300 fn boolean(&self) -> Self {
3301 constraints::boolean(self)
3302 }
3303
3304 fn crumb(&self) -> Self {
3305 constraints::crumb(self)
3306 }
3307
3308 fn literal(x: F) -> Self {
3309 ConstantTerm::Literal(x).into()
3310 }
3311
3312 fn witness(row: CurrOrNext, col: usize, _: Option<&ArgumentData<F>>) -> Self {
3313 witness(col, row)
3314 }
3315
3316 fn coeff(col: usize, _: Option<&ArgumentData<F>>) -> Self {
3317 coeff(col)
3318 }
3319
3320 fn constant(
3321 expr: ConstantExpr<F, BerkeleyChallengeTerm>,
3322 _: Option<&ArgumentData<F>>,
3323 ) -> Self {
3324 Expr::from(expr)
3325 }
3326
3327 fn cache(&self, cache: &mut Cache) -> Self {
3328 Expr::Cache(cache.next_id(), Box::new(self.clone()))
3329 }
3330 }
3331 impl<F: Field> ExprOps<F, BerkeleyChallengeTerm> for F {
3334 fn two_pow(pow: u64) -> Self {
3335 <F as Two<F>>::two_pow(pow)
3336 }
3337
3338 fn two_to_limb() -> Self {
3339 KimchiForeignElement::<F>::two_to_limb()
3340 }
3341
3342 fn two_to_2limb() -> Self {
3343 KimchiForeignElement::<F>::two_to_2limb()
3344 }
3345
3346 fn two_to_3limb() -> Self {
3347 KimchiForeignElement::<F>::two_to_3limb()
3348 }
3349
3350 fn double(&self) -> Self {
3351 *self * F::from(2u64)
3352 }
3353
3354 fn square(&self) -> Self {
3355 *self * *self
3356 }
3357
3358 fn pow(&self, p: u64) -> Self {
3359 self.pow([p])
3360 }
3361
3362 fn boolean(&self) -> Self {
3363 constraints::boolean(self)
3364 }
3365
3366 fn crumb(&self) -> Self {
3367 constraints::crumb(self)
3368 }
3369
3370 fn literal(x: F) -> Self {
3371 x
3372 }
3373
3374 fn witness(row: CurrOrNext, col: usize, env: Option<&ArgumentData<F>>) -> Self {
3375 match env {
3376 Some(data) => data.witness[(row, col)],
3377 None => panic!("Missing witness"),
3378 }
3379 }
3380
3381 fn coeff(col: usize, env: Option<&ArgumentData<F>>) -> Self {
3382 match env {
3383 Some(data) => data.coeffs[col],
3384 None => panic!("Missing coefficients"),
3385 }
3386 }
3387
3388 fn constant(
3389 expr: ConstantExpr<F, BerkeleyChallengeTerm>,
3390 env: Option<&ArgumentData<F>>,
3391 ) -> Self {
3392 match env {
3393 Some(data) => expr.value(&data.constants, &data.challenges),
3394 None => panic!("Missing constants"),
3395 }
3396 }
3397
3398 fn cache(&self, _: &mut Cache) -> Self {
3399 *self
3400 }
3401 }
3402
3403 pub fn boolean<F: Field, ChallengeTerm, T: ExprOps<F, ChallengeTerm>>(b: &T) -> T {
3405 b.square() - b.clone()
3406 }
3407
3408 pub fn crumb<F: Field, ChallengeTerm, T: ExprOps<F, ChallengeTerm>>(x: &T) -> T {
3410 x.clone()
3412 * (x.clone() - 1u64.into())
3413 * (x.clone() - 2u64.into())
3414 * (x.clone() - 3u64.into())
3415 }
3416
3417 pub fn compact_limb<F: Field, ChallengeTerm, T: ExprOps<F, ChallengeTerm>>(
3419 lo: &T,
3420 mi: &T,
3421 ) -> T {
3422 lo.clone() + mi.clone() * T::two_to_limb()
3423 }
3424}
3425
3426#[macro_export]
3429macro_rules! auto_clone {
3430 ($var:ident, $expr:expr) => {
3431 let $var = $expr;
3432 let $var = || $var.clone();
3433 };
3434 ($var:ident) => {
3435 let $var = || $var.clone();
3436 };
3437}
3438#[macro_export]
3439macro_rules! auto_clone_array {
3440 ($var:ident, $expr:expr) => {
3441 let $var = $expr;
3442 let $var = |i: usize| $var[i].clone();
3443 };
3444 ($var:ident) => {
3445 let $var = |i: usize| $var[i].clone();
3446 };
3447}
3448
3449pub use auto_clone;
3450pub use auto_clone_array;
3451
3452pub mod prologue {
3454 pub use super::{
3455 berkeley_columns::{coeff, constant, index, witness, witness_curr, witness_next, E},
3456 FeatureFlag,
3457 };
3458}