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) => stack.push(v.evaluate(evals)?),
897 Pow(n) => {
898 let i = stack.len() - 1;
899 stack[i] = stack[i].pow([*n]);
900 }
901 Add => {
902 let y = stack.pop().ok_or(ExprError::EmptyStack)?;
903 let x = stack.pop().ok_or(ExprError::EmptyStack)?;
904 stack.push(x + y);
905 }
906 Mul => {
907 let y = stack.pop().ok_or(ExprError::EmptyStack)?;
908 let x = stack.pop().ok_or(ExprError::EmptyStack)?;
909 stack.push(x * y);
910 }
911 Sub => {
912 let y = stack.pop().ok_or(ExprError::EmptyStack)?;
913 let x = stack.pop().ok_or(ExprError::EmptyStack)?;
914 stack.push(x - y);
915 }
916 Store => {
917 let x = stack[stack.len() - 1];
918 cache.push(x);
919 }
920 Load(i) => stack.push(cache[*i]),
921 SkipIf(feature, count) => {
922 if feature.is_enabled() {
923 skip_count = *count;
924 stack.push(F::zero());
925 }
926 }
927 SkipIfNot(feature, count) => {
928 if !feature.is_enabled() {
929 skip_count = *count;
930 stack.push(F::zero());
931 }
932 }
933 }
934 }
935
936 assert_eq!(stack.len(), 1);
937 Ok(stack[0])
938 }
939}
940
941impl<C, Column> Expr<C, Column> {
942 pub fn cell(col: Column, row: CurrOrNext) -> Expr<C, Column> {
944 Expr::Atom(ExprInner::Cell(Variable { col, row }))
945 }
946
947 pub fn double(self) -> Self {
948 Expr::Double(Box::new(self))
949 }
950
951 pub fn square(self) -> Self {
952 Expr::Square(Box::new(self))
953 }
954
955 pub fn constant(c: C) -> Expr<C, Column> {
957 Expr::Atom(ExprInner::Constant(c))
958 }
959
960 pub fn degree(&self, d1_size: u64, zk_rows: u64) -> u64 {
969 use ExprInner::*;
970 use Operations::*;
971 match self {
972 Double(x) => x.degree(d1_size, zk_rows),
973 Atom(Constant(_)) => 0,
974 Atom(VanishesOnZeroKnowledgeAndPreviousRows) => zk_rows + 1,
975 Atom(UnnormalizedLagrangeBasis(_)) => d1_size,
976 Atom(Cell(_)) => d1_size,
977 Square(x) => 2 * x.degree(d1_size, zk_rows),
978 Mul(x, y) => (*x).degree(d1_size, zk_rows) + (*y).degree(d1_size, zk_rows),
979 Add(x, y) | Sub(x, y) => {
980 core::cmp::max((*x).degree(d1_size, zk_rows), (*y).degree(d1_size, zk_rows))
981 }
982 Pow(e, d) => d * e.degree(d1_size, zk_rows),
983 Cache(_, e) => e.degree(d1_size, zk_rows),
984 IfFeature(_, e1, e2) => {
985 core::cmp::max(e1.degree(d1_size, zk_rows), e2.degree(d1_size, zk_rows))
986 }
987 }
988 }
989}
990
991impl<'a, F, Column: FormattedOutput + Debug + Clone, ChallengeTerm> fmt::Display
992 for Expr<ConstantExpr<F, ChallengeTerm>, Column>
993where
994 F: PrimeField,
995 ChallengeTerm: AlphaChallengeTerm<'a>,
996{
997 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
998 let cache = &mut HashMap::new();
999 write!(f, "{}", self.text(cache))
1000 }
1001}
1002
1003#[derive(Clone)]
1004enum EvalResult<'a, F: FftField> {
1005 Constant(F),
1006 Evals {
1007 domain: Domain,
1008 evals: Evaluations<F, D<F>>,
1009 },
1010 SubEvals {
1015 domain: Domain,
1016 shift: usize,
1017 evals: &'a Evaluations<F, D<F>>,
1018 },
1019}
1020
1021fn unnormalized_lagrange_evals<
1056 'a,
1057 F: FftField,
1058 ChallengeTerm,
1059 Challenge: Index<ChallengeTerm, Output = F>,
1060 Environment: ColumnEnvironment<'a, F, ChallengeTerm, Challenge>,
1061>(
1062 l0_1: F,
1063 i: i32,
1064 res_domain: Domain,
1065 env: &Environment,
1066) -> Evaluations<F, D<F>> {
1067 let k = match res_domain {
1068 Domain::D1 => 1,
1069 Domain::D2 => 2,
1070 Domain::D4 => 4,
1071 Domain::D8 => 8,
1072 };
1073 let res_domain = env.get_domain(res_domain);
1074
1075 let d1 = env.get_domain(Domain::D1);
1076 let n = d1.size;
1077 let i = if i < 0 {
1079 ((i as isize) + (n as isize)) as usize
1080 } else {
1081 i as usize
1082 };
1083 let ii = i as u64;
1084 assert!(ii < n);
1085 let omega = d1.group_gen;
1086 let omega_i = omega.pow([ii]);
1087 let omega_minus_i = omega.pow([n - ii]);
1088
1089 let omega_k_n_pows = pows(k, res_domain.group_gen.pow([n]));
1094 let omega_k_pows = pows(k, res_domain.group_gen);
1095
1096 let mut evals: Vec<F> = {
1097 let mut v = vec![F::one(); k * (n as usize)];
1098 let mut omega_q = F::one();
1099 for q in 0..(n as usize) {
1100 for r in 1..k {
1102 v[k * q + r] = omega_q * omega_k_pows[r] - omega_i;
1103 }
1104 omega_q *= omega;
1105 }
1106 ark_ff::fields::batch_inversion::<F>(&mut v[..]);
1107 v
1108 };
1109 for q in 0..(n as usize) {
1115 evals[k * q] = F::zero();
1116 }
1117 evals[k * i] = omega_minus_i * l0_1;
1118
1119 for q in 0..(n as usize) {
1121 for r in 1..k {
1122 evals[k * q + r] *= omega_k_n_pows[r] - F::one();
1123 }
1124 }
1125
1126 Evaluations::<F, D<F>>::from_vec_and_domain(evals, res_domain)
1127}
1128
1129impl<'a, F: FftField> EvalResult<'a, F> {
1132 fn init_<G: Sync + Send + Fn(usize) -> F>(
1142 res_domain: (Domain, D<F>),
1143 g: G,
1144 ) -> Evaluations<F, D<F>> {
1145 let n = res_domain.1.size();
1146 Evaluations::<F, D<F>>::from_vec_and_domain(
1147 o1_utils::cfg_into_iter!(0..n).map(g).collect(),
1148 res_domain.1,
1149 )
1150 }
1151
1152 fn init<G: Sync + Send + Fn(usize) -> F>(res_domain: (Domain, D<F>), g: G) -> Self {
1155 Self::Evals {
1156 domain: res_domain.0,
1157 evals: Self::init_(res_domain, g),
1158 }
1159 }
1160
1161 fn add<'c>(self, other: EvalResult<'_, F>, res_domain: (Domain, D<F>)) -> EvalResult<'c, F> {
1162 use EvalResult::*;
1163 match (self, other) {
1164 (Constant(x), Constant(y)) => Constant(x + y),
1165 (Evals { domain, mut evals }, Constant(x))
1166 | (Constant(x), Evals { domain, mut evals }) => {
1167 o1_utils::cfg_iter_mut!(evals.evals).for_each(|e| *e += x);
1168 Evals { domain, evals }
1169 }
1170 (
1171 SubEvals {
1172 evals,
1173 domain,
1174 shift,
1175 },
1176 Constant(x),
1177 )
1178 | (
1179 Constant(x),
1180 SubEvals {
1181 evals,
1182 domain,
1183 shift,
1184 },
1185 ) => {
1186 let n = res_domain.1.size();
1187 let scale = (domain as usize) / (res_domain.0 as usize);
1188 assert!(
1189 scale != 0,
1190 "Check that the implementation of
1191 column_domain and the evaluation domain of the
1192 witnesses are the same"
1193 );
1194 let v: Vec<_> = o1_utils::cfg_into_iter!(0..n)
1195 .map(|i| {
1196 x + evals.evals[(scale * i + (domain as usize) * shift) % evals.evals.len()]
1197 })
1198 .collect();
1199 Evals {
1200 domain: res_domain.0,
1201 evals: Evaluations::<F, D<F>>::from_vec_and_domain(v, res_domain.1),
1202 }
1203 }
1204 (
1205 Evals {
1206 domain: d1,
1207 evals: mut es1,
1208 },
1209 Evals {
1210 domain: d2,
1211 evals: es2,
1212 },
1213 ) => {
1214 assert_eq!(d1, d2);
1215 es1 += &es2;
1216 Evals {
1217 domain: d1,
1218 evals: es1,
1219 }
1220 }
1221 (
1222 SubEvals {
1223 domain: d_sub,
1224 shift: s,
1225 evals: es_sub,
1226 },
1227 Evals {
1228 domain: d,
1229 mut evals,
1230 },
1231 )
1232 | (
1233 Evals {
1234 domain: d,
1235 mut evals,
1236 },
1237 SubEvals {
1238 domain: d_sub,
1239 shift: s,
1240 evals: es_sub,
1241 },
1242 ) => {
1243 let scale = (d_sub as usize) / (d as usize);
1244 assert!(
1245 scale != 0,
1246 "Check that the implementation of
1247 column_domain and the evaluation domain of the
1248 witnesses are the same"
1249 );
1250 o1_utils::cfg_iter_mut!(evals.evals)
1251 .enumerate()
1252 .for_each(|(i, e)| {
1253 *e += es_sub.evals[(scale * i + (d_sub as usize) * s) % es_sub.evals.len()];
1254 });
1255 Evals { evals, domain: d }
1256 }
1257 (
1258 SubEvals {
1259 domain: d1,
1260 shift: s1,
1261 evals: es1,
1262 },
1263 SubEvals {
1264 domain: d2,
1265 shift: s2,
1266 evals: es2,
1267 },
1268 ) => {
1269 let scale1 = (d1 as usize) / (res_domain.0 as usize);
1270 assert!(
1271 scale1 != 0,
1272 "Check that the implementation of
1273 column_domain and the evaluation domain of the
1274 witnesses are the same"
1275 );
1276 let scale2 = (d2 as usize) / (res_domain.0 as usize);
1277 assert!(
1278 scale2 != 0,
1279 "Check that the implementation of
1280 column_domain and the evaluation domain of the
1281 witnesses are the same"
1282 );
1283 let n = res_domain.1.size();
1284 let v: Vec<_> = o1_utils::cfg_into_iter!(0..n)
1285 .map(|i| {
1286 es1.evals[(scale1 * i + (d1 as usize) * s1) % es1.evals.len()]
1287 + es2.evals[(scale2 * i + (d2 as usize) * s2) % es2.evals.len()]
1288 })
1289 .collect();
1290
1291 Evals {
1292 domain: res_domain.0,
1293 evals: Evaluations::<F, D<F>>::from_vec_and_domain(v, res_domain.1),
1294 }
1295 }
1296 }
1297 }
1298
1299 fn sub<'c>(self, other: EvalResult<'_, F>, res_domain: (Domain, D<F>)) -> EvalResult<'c, F> {
1300 use EvalResult::*;
1301 match (self, other) {
1302 (Constant(x), Constant(y)) => Constant(x - y),
1303 (Evals { domain, mut evals }, Constant(x)) => {
1304 o1_utils::cfg_iter_mut!(evals.evals).for_each(|e| *e -= x);
1305 Evals { domain, evals }
1306 }
1307 (Constant(x), Evals { domain, mut evals }) => {
1308 o1_utils::cfg_iter_mut!(evals.evals).for_each(|e| *e = x - *e);
1309 Evals { domain, evals }
1310 }
1311 (
1312 SubEvals {
1313 evals,
1314 domain: d,
1315 shift: s,
1316 },
1317 Constant(x),
1318 ) => {
1319 let scale = (d as usize) / (res_domain.0 as usize);
1320 assert!(
1321 scale != 0,
1322 "Check that the implementation of
1323 column_domain and the evaluation domain of the
1324 witnesses are the same"
1325 );
1326 EvalResult::init(res_domain, |i| {
1327 evals.evals[(scale * i + (d as usize) * s) % evals.evals.len()] - x
1328 })
1329 }
1330 (
1331 Constant(x),
1332 SubEvals {
1333 evals,
1334 domain: d,
1335 shift: s,
1336 },
1337 ) => {
1338 let scale = (d as usize) / (res_domain.0 as usize);
1339 assert!(
1340 scale != 0,
1341 "Check that the implementation of
1342 column_domain and the evaluation domain of the
1343 witnesses are the same"
1344 );
1345
1346 EvalResult::init(res_domain, |i| {
1347 x - evals.evals[(scale * i + (d as usize) * s) % evals.evals.len()]
1348 })
1349 }
1350 (
1351 Evals {
1352 domain: d1,
1353 evals: mut es1,
1354 },
1355 Evals {
1356 domain: d2,
1357 evals: es2,
1358 },
1359 ) => {
1360 assert_eq!(d1, d2);
1361 es1 -= &es2;
1362 Evals {
1363 domain: d1,
1364 evals: es1,
1365 }
1366 }
1367 (
1368 SubEvals {
1369 domain: d_sub,
1370 shift: s,
1371 evals: es_sub,
1372 },
1373 Evals {
1374 domain: d,
1375 mut evals,
1376 },
1377 ) => {
1378 let scale = (d_sub as usize) / (d as usize);
1379 assert!(
1380 scale != 0,
1381 "Check that the implementation of
1382 column_domain and the evaluation domain of the
1383 witnesses are the same"
1384 );
1385
1386 o1_utils::cfg_iter_mut!(evals.evals)
1387 .enumerate()
1388 .for_each(|(i, e)| {
1389 *e = es_sub.evals[(scale * i + (d_sub as usize) * s) % es_sub.evals.len()]
1390 - *e;
1391 });
1392 Evals { evals, domain: d }
1393 }
1394 (
1395 Evals {
1396 domain: d,
1397 mut evals,
1398 },
1399 SubEvals {
1400 domain: d_sub,
1401 shift: s,
1402 evals: es_sub,
1403 },
1404 ) => {
1405 let scale = (d_sub as usize) / (d as usize);
1406 assert!(
1407 scale != 0,
1408 "Check that the implementation of
1409 column_domain and the evaluation domain of the
1410 witnesses are the same"
1411 );
1412 o1_utils::cfg_iter_mut!(evals.evals)
1413 .enumerate()
1414 .for_each(|(i, e)| {
1415 *e -= es_sub.evals[(scale * i + (d_sub as usize) * s) % es_sub.evals.len()];
1416 });
1417 Evals { evals, domain: d }
1418 }
1419 (
1420 SubEvals {
1421 domain: d1,
1422 shift: s1,
1423 evals: es1,
1424 },
1425 SubEvals {
1426 domain: d2,
1427 shift: s2,
1428 evals: es2,
1429 },
1430 ) => {
1431 let scale1 = (d1 as usize) / (res_domain.0 as usize);
1432 assert!(
1433 scale1 != 0,
1434 "Check that the implementation of
1435 column_domain and the evaluation domain of the
1436 witnesses are the same"
1437 );
1438 let scale2 = (d2 as usize) / (res_domain.0 as usize);
1439 assert!(
1440 scale2 != 0,
1441 "Check that the implementation of
1442 column_domain and the evaluation domain of the
1443 witnesses are the same"
1444 );
1445
1446 EvalResult::init(res_domain, |i| {
1447 es1.evals[(scale1 * i + (d1 as usize) * s1) % es1.evals.len()]
1448 - es2.evals[(scale2 * i + (d2 as usize) * s2) % es2.evals.len()]
1449 })
1450 }
1451 }
1452 }
1453
1454 fn pow<'b>(self, d: u64, res_domain: (Domain, D<F>)) -> EvalResult<'b, F> {
1455 let mut acc = EvalResult::Constant(F::one());
1456 for i in (0..u64::BITS).rev() {
1457 acc = acc.square(res_domain);
1458
1459 if (d >> i) & 1 == 1 {
1460 acc = acc.mul(self.clone(), res_domain)
1462 }
1463 }
1464 acc
1465 }
1466
1467 fn square<'b>(self, res_domain: (Domain, D<F>)) -> EvalResult<'b, F> {
1468 use EvalResult::*;
1469 match self {
1470 Constant(x) => Constant(x.square()),
1471 Evals { domain, mut evals } => {
1472 o1_utils::cfg_iter_mut!(evals.evals).for_each(|e| {
1473 e.square_in_place();
1474 });
1475 Evals { domain, evals }
1476 }
1477 SubEvals {
1478 evals,
1479 domain: d,
1480 shift: s,
1481 } => {
1482 let scale = (d as usize) / (res_domain.0 as usize);
1483 assert!(
1484 scale != 0,
1485 "Check that the implementation of
1486 column_domain and the evaluation domain of the
1487 witnesses are the same"
1488 );
1489 EvalResult::init(res_domain, |i| {
1490 evals.evals[(scale * i + (d as usize) * s) % evals.evals.len()].square()
1491 })
1492 }
1493 }
1494 }
1495
1496 fn mul<'c>(self, other: EvalResult<'_, F>, res_domain: (Domain, D<F>)) -> EvalResult<'c, F> {
1497 use EvalResult::*;
1498 match (self, other) {
1499 (Constant(x), Constant(y)) => Constant(x * y),
1500 (Evals { domain, mut evals }, Constant(x))
1501 | (Constant(x), Evals { domain, mut evals }) => {
1502 o1_utils::cfg_iter_mut!(evals.evals).for_each(|e| *e *= x);
1503 Evals { domain, evals }
1504 }
1505 (
1506 SubEvals {
1507 evals,
1508 domain: d,
1509 shift: s,
1510 },
1511 Constant(x),
1512 )
1513 | (
1514 Constant(x),
1515 SubEvals {
1516 evals,
1517 domain: d,
1518 shift: s,
1519 },
1520 ) => {
1521 let scale = (d as usize) / (res_domain.0 as usize);
1522 assert!(
1523 scale != 0,
1524 "Check that the implementation of
1525 column_domain and the evaluation domain of the
1526 witnesses are the same"
1527 );
1528 EvalResult::init(res_domain, |i| {
1529 x * evals.evals[(scale * i + (d as usize) * s) % evals.evals.len()]
1530 })
1531 }
1532 (
1533 Evals {
1534 domain: d1,
1535 evals: mut es1,
1536 },
1537 Evals {
1538 domain: d2,
1539 evals: es2,
1540 },
1541 ) => {
1542 assert_eq!(d1, d2);
1543 es1 *= &es2;
1544 Evals {
1545 domain: d1,
1546 evals: es1,
1547 }
1548 }
1549 (
1550 SubEvals {
1551 domain: d_sub,
1552 shift: s,
1553 evals: es_sub,
1554 },
1555 Evals {
1556 domain: d,
1557 mut evals,
1558 },
1559 )
1560 | (
1561 Evals {
1562 domain: d,
1563 mut evals,
1564 },
1565 SubEvals {
1566 domain: d_sub,
1567 shift: s,
1568 evals: es_sub,
1569 },
1570 ) => {
1571 let scale = (d_sub as usize) / (d as usize);
1572 assert!(
1573 scale != 0,
1574 "Check that the implementation of
1575 column_domainand the evaluation domain of the
1576 witnesses are the same"
1577 );
1578
1579 o1_utils::cfg_iter_mut!(evals.evals)
1580 .enumerate()
1581 .for_each(|(i, e)| {
1582 *e *= es_sub.evals[(scale * i + (d_sub as usize) * s) % es_sub.evals.len()];
1583 });
1584 Evals { evals, domain: d }
1585 }
1586 (
1587 SubEvals {
1588 domain: d1,
1589 shift: s1,
1590 evals: es1,
1591 },
1592 SubEvals {
1593 domain: d2,
1594 shift: s2,
1595 evals: es2,
1596 },
1597 ) => {
1598 let scale1 = (d1 as usize) / (res_domain.0 as usize);
1599 assert!(
1600 scale1 != 0,
1601 "Check that the implementation of
1602 column_domain and the evaluation domain of the
1603 witnesses are the same"
1604 );
1605 let scale2 = (d2 as usize) / (res_domain.0 as usize);
1606
1607 assert!(
1608 scale2 != 0,
1609 "Check that the implementation of
1610 column_domain and the evaluation domain of the
1611 witnesses are the same"
1612 );
1613 EvalResult::init(res_domain, |i| {
1614 es1.evals[(scale1 * i + (d1 as usize) * s1) % es1.evals.len()]
1615 * es2.evals[(scale2 * i + (d2 as usize) * s2) % es2.evals.len()]
1616 })
1617 }
1618 }
1619 }
1620}
1621
1622impl<'a, F: Field, Column: PartialEq + Copy, ChallengeTerm: AlphaChallengeTerm<'a>>
1623 Expr<ConstantExpr<F, ChallengeTerm>, Column>
1624{
1625 pub fn literal(x: F) -> Self {
1628 ConstantTerm::Literal(x).into()
1629 }
1630
1631 pub fn combine_constraints(alphas: impl Iterator<Item = u32>, cs: Vec<Self>) -> Self {
1634 let zero = Expr::<ConstantExpr<F, ChallengeTerm>, Column>::zero();
1635 cs.into_iter()
1636 .zip_eq(alphas)
1637 .map(|(c, i)| Expr::from(ConstantExpr::pow(ChallengeTerm::ALPHA.into(), i as u64)) * c)
1638 .fold(zero, |acc, x| acc + x)
1639 }
1640}
1641
1642impl<F: FftField, Column: Copy, ChallengeTerm: Copy> Expr<ConstantExpr<F, ChallengeTerm>, Column> {
1643 pub fn to_polish(&self) -> Vec<PolishToken<F, Column, ChallengeTerm>> {
1645 let mut res = vec![];
1646 let mut cache = HashMap::new();
1647 self.to_polish_(&mut cache, &mut res);
1648 res
1649 }
1650
1651 fn to_polish_(
1652 &self,
1653 cache: &mut HashMap<CacheId, usize>,
1654 res: &mut Vec<PolishToken<F, Column, ChallengeTerm>>,
1655 ) {
1656 match self {
1657 Expr::Double(x) => {
1658 x.to_polish_(cache, res);
1659 res.push(PolishToken::Dup);
1660 res.push(PolishToken::Add);
1661 }
1662 Expr::Square(x) => {
1663 x.to_polish_(cache, res);
1664 res.push(PolishToken::Dup);
1665 res.push(PolishToken::Mul);
1666 }
1667 Expr::Pow(x, d) => {
1668 x.to_polish_(cache, res);
1669 res.push(PolishToken::Pow(*d))
1670 }
1671 Expr::Atom(ExprInner::Constant(c)) => {
1672 c.to_polish(cache, res);
1673 }
1674 Expr::Atom(ExprInner::Cell(v)) => res.push(PolishToken::Cell(*v)),
1675 Expr::Atom(ExprInner::VanishesOnZeroKnowledgeAndPreviousRows) => {
1676 res.push(PolishToken::VanishesOnZeroKnowledgeAndPreviousRows);
1677 }
1678 Expr::Atom(ExprInner::UnnormalizedLagrangeBasis(i)) => {
1679 res.push(PolishToken::UnnormalizedLagrangeBasis(*i));
1680 }
1681 Expr::Add(x, y) => {
1682 x.to_polish_(cache, res);
1683 y.to_polish_(cache, res);
1684 res.push(PolishToken::Add);
1685 }
1686 Expr::Sub(x, y) => {
1687 x.to_polish_(cache, res);
1688 y.to_polish_(cache, res);
1689 res.push(PolishToken::Sub);
1690 }
1691 Expr::Mul(x, y) => {
1692 x.to_polish_(cache, res);
1693 y.to_polish_(cache, res);
1694 res.push(PolishToken::Mul);
1695 }
1696 Expr::Cache(id, e) => {
1697 match cache.get(id) {
1698 Some(pos) =>
1699 {
1701 res.push(PolishToken::Load(*pos))
1702 }
1703 None => {
1704 e.to_polish_(cache, res);
1706 res.push(PolishToken::Store);
1707 cache.insert(*id, cache.len());
1708 }
1709 }
1710 }
1711 Expr::IfFeature(feature, e1, e2) => {
1712 {
1713 let tok = PolishToken::SkipIfNot(*feature, 0);
1715 res.push(tok);
1716 let len_before = res.len();
1717 let mut cache = cache.clone();
1720 e1.to_polish_(&mut cache, res);
1721 let len_after = res.len();
1722 res[len_before - 1] = PolishToken::SkipIfNot(*feature, len_after - len_before);
1723 }
1724
1725 {
1726 let tok = PolishToken::SkipIfNot(*feature, 0);
1728 res.push(tok);
1729 let len_before = res.len();
1730 let mut cache = cache.clone();
1733 e2.to_polish_(&mut cache, res);
1734 let len_after = res.len();
1735 res[len_before - 1] = PolishToken::SkipIfNot(*feature, len_after - len_before);
1736 }
1737 }
1738 }
1739 }
1740}
1741
1742impl<F: FftField, Column: PartialEq + Copy, ChallengeTerm: Copy>
1743 Expr<ConstantExpr<F, ChallengeTerm>, Column>
1744{
1745 fn evaluate_constants_(
1746 &self,
1747 c: &Constants<F>,
1748 chals: &dyn Index<ChallengeTerm, Output = F>,
1749 ) -> Expr<F, Column> {
1750 use ExprInner::*;
1751 use Operations::*;
1752 match self {
1754 Double(x) => x.evaluate_constants_(c, chals).double(),
1755 Pow(x, d) => x.evaluate_constants_(c, chals).pow(*d),
1756 Square(x) => x.evaluate_constants_(c, chals).square(),
1757 Atom(Constant(x)) => Atom(Constant(x.value(c, chals))),
1758 Atom(Cell(v)) => Atom(Cell(*v)),
1759 Atom(VanishesOnZeroKnowledgeAndPreviousRows) => {
1760 Atom(VanishesOnZeroKnowledgeAndPreviousRows)
1761 }
1762 Atom(UnnormalizedLagrangeBasis(i)) => Atom(UnnormalizedLagrangeBasis(*i)),
1763 Add(x, y) => x.evaluate_constants_(c, chals) + y.evaluate_constants_(c, chals),
1764 Mul(x, y) => x.evaluate_constants_(c, chals) * y.evaluate_constants_(c, chals),
1765 Sub(x, y) => x.evaluate_constants_(c, chals) - y.evaluate_constants_(c, chals),
1766 Cache(id, e) => Cache(*id, Box::new(e.evaluate_constants_(c, chals))),
1767 IfFeature(feature, e1, e2) => IfFeature(
1768 *feature,
1769 Box::new(e1.evaluate_constants_(c, chals)),
1770 Box::new(e2.evaluate_constants_(c, chals)),
1771 ),
1772 }
1773 }
1774
1775 pub fn evaluate<
1777 'a,
1778 Evaluations: ColumnEvaluations<F, Column = Column>,
1779 Challenge: Index<ChallengeTerm, Output = F>,
1780 Environment: ColumnEnvironment<'a, F, ChallengeTerm, Challenge, Column = Column>,
1781 >(
1782 &self,
1783 d: D<F>,
1784 pt: F,
1785 evals: &Evaluations,
1786 env: &Environment,
1787 ) -> Result<F, ExprError<Column>> {
1788 self.evaluate_(d, pt, evals, env.get_constants(), env.get_challenges())
1789 }
1790
1791 pub fn evaluate_<Evaluations: ColumnEvaluations<F, Column = Column>>(
1793 &self,
1794 d: D<F>,
1795 pt: F,
1796 evals: &Evaluations,
1797 c: &Constants<F>,
1798 chals: &dyn Index<ChallengeTerm, Output = F>,
1799 ) -> Result<F, ExprError<Column>> {
1800 use ExprInner::*;
1801 use Operations::*;
1802 match self {
1803 Double(x) => x.evaluate_(d, pt, evals, c, chals).map(|x| x.double()),
1804 Atom(Constant(x)) => Ok(x.value(c, chals)),
1805 Pow(x, p) => Ok(x.evaluate_(d, pt, evals, c, chals)?.pow([*p])),
1806 Mul(x, y) => {
1807 let x = (*x).evaluate_(d, pt, evals, c, chals)?;
1808 let y = (*y).evaluate_(d, pt, evals, c, chals)?;
1809 Ok(x * y)
1810 }
1811 Square(x) => Ok(x.evaluate_(d, pt, evals, c, chals)?.square()),
1812 Add(x, y) => {
1813 let x = (*x).evaluate_(d, pt, evals, c, chals)?;
1814 let y = (*y).evaluate_(d, pt, evals, c, chals)?;
1815 Ok(x + y)
1816 }
1817 Sub(x, y) => {
1818 let x = (*x).evaluate_(d, pt, evals, c, chals)?;
1819 let y = (*y).evaluate_(d, pt, evals, c, chals)?;
1820 Ok(x - y)
1821 }
1822 Atom(VanishesOnZeroKnowledgeAndPreviousRows) => {
1823 Ok(eval_vanishes_on_last_n_rows(d, c.zk_rows + 1, pt))
1824 }
1825 Atom(UnnormalizedLagrangeBasis(i)) => {
1826 let offset = if i.zk_rows {
1827 -(c.zk_rows as i32) + i.offset
1828 } else {
1829 i.offset
1830 };
1831 Ok(unnormalized_lagrange_basis(&d, offset, &pt))
1832 }
1833 Atom(Cell(v)) => v.evaluate(evals),
1834 Cache(_, e) => e.evaluate_(d, pt, evals, c, chals),
1835 IfFeature(feature, e1, e2) => {
1836 if feature.is_enabled() {
1837 e1.evaluate_(d, pt, evals, c, chals)
1838 } else {
1839 e2.evaluate_(d, pt, evals, c, chals)
1840 }
1841 }
1842 }
1843 }
1844
1845 pub fn evaluate_constants<
1847 'a,
1848 Challenge: Index<ChallengeTerm, Output = F>,
1849 Environment: ColumnEnvironment<'a, F, ChallengeTerm, Challenge, Column = Column>,
1850 >(
1851 &self,
1852 env: &Environment,
1853 ) -> Expr<F, Column> {
1854 self.evaluate_constants_(env.get_constants(), env.get_challenges())
1855 }
1856
1857 pub fn evaluations<
1864 'a,
1865 Challenge: Index<ChallengeTerm, Output = F>,
1866 Environment: ColumnEnvironment<'a, F, ChallengeTerm, Challenge, Column = Column>,
1867 >(
1868 &self,
1869 env: &Environment,
1870 ) -> Evaluations<F, D<F>> {
1871 self.evaluate_constants(env).evaluations(env)
1872 }
1873}
1874
1875enum Either<A, B> {
1879 Left(A),
1880 Right(B),
1881}
1882
1883impl<F: FftField, Column: Copy> Expr<F, Column> {
1884 pub fn evaluate<Evaluations: ColumnEvaluations<F, Column = Column>>(
1886 &self,
1887 d: D<F>,
1888 pt: F,
1889 zk_rows: u64,
1890 evals: &Evaluations,
1891 ) -> Result<F, ExprError<Column>> {
1892 use ExprInner::*;
1893 use Operations::*;
1894 match self {
1895 Atom(Constant(x)) => Ok(*x),
1896 Pow(x, p) => Ok(x.evaluate(d, pt, zk_rows, evals)?.pow([*p])),
1897 Double(x) => x.evaluate(d, pt, zk_rows, evals).map(|x| x.double()),
1898 Square(x) => x.evaluate(d, pt, zk_rows, evals).map(|x| x.square()),
1899 Mul(x, y) => {
1900 let x = (*x).evaluate(d, pt, zk_rows, evals)?;
1901 let y = (*y).evaluate(d, pt, zk_rows, evals)?;
1902 Ok(x * y)
1903 }
1904 Add(x, y) => {
1905 let x = (*x).evaluate(d, pt, zk_rows, evals)?;
1906 let y = (*y).evaluate(d, pt, zk_rows, evals)?;
1907 Ok(x + y)
1908 }
1909 Sub(x, y) => {
1910 let x = (*x).evaluate(d, pt, zk_rows, evals)?;
1911 let y = (*y).evaluate(d, pt, zk_rows, evals)?;
1912 Ok(x - y)
1913 }
1914 Atom(VanishesOnZeroKnowledgeAndPreviousRows) => {
1915 Ok(eval_vanishes_on_last_n_rows(d, zk_rows + 1, pt))
1916 }
1917 Atom(UnnormalizedLagrangeBasis(i)) => {
1918 let offset = if i.zk_rows {
1919 -(zk_rows as i32) + i.offset
1920 } else {
1921 i.offset
1922 };
1923 Ok(unnormalized_lagrange_basis(&d, offset, &pt))
1924 }
1925 Atom(Cell(v)) => v.evaluate(evals),
1926 Cache(_, e) => e.evaluate(d, pt, zk_rows, evals),
1927 IfFeature(feature, e1, e2) => {
1928 if feature.is_enabled() {
1929 e1.evaluate(d, pt, zk_rows, evals)
1930 } else {
1931 e2.evaluate(d, pt, zk_rows, evals)
1932 }
1933 }
1934 }
1935 }
1936
1937 pub fn evaluations<
1939 'a,
1940 ChallengeTerm,
1941 Challenge: Index<ChallengeTerm, Output = F>,
1942 Environment: ColumnEnvironment<'a, F, ChallengeTerm, Challenge, Column = Column>,
1943 >(
1944 &self,
1945 env: &Environment,
1946 ) -> Evaluations<F, D<F>> {
1947 let d1_size = env.get_domain(Domain::D1).size;
1948 let deg = self.degree(d1_size, env.get_constants().zk_rows);
1949 let d = if deg <= d1_size {
1950 Domain::D1
1951 } else if deg <= 4 * d1_size {
1952 Domain::D4
1953 } else if deg <= 8 * d1_size {
1954 Domain::D8
1955 } else {
1956 panic!("constraint had degree {deg} > d8 ({})", 8 * d1_size);
1957 };
1958
1959 let mut cache = HashMap::new();
1960
1961 let evals = match self.evaluations_helper(&mut cache, d, env) {
1962 Either::Left(x) => x,
1963 Either::Right(id) => cache.get(&id).unwrap().clone(),
1964 };
1965
1966 match evals {
1967 EvalResult::Evals { evals, domain } => {
1968 assert_eq!(domain, d);
1969 evals
1970 }
1971 EvalResult::Constant(x) => EvalResult::init_((d, env.get_domain(d)), |_| x),
1972 EvalResult::SubEvals {
1973 evals,
1974 domain: d_sub,
1975 shift: s,
1976 } => {
1977 let res_domain = env.get_domain(d);
1978 let scale = (d_sub as usize) / (d as usize);
1979 assert!(
1980 scale != 0,
1981 "Check that the implementation of
1982 column_domain and the evaluation domain of the
1983 witnesses are the same"
1984 );
1985 EvalResult::init_((d, res_domain), |i| {
1986 evals.evals[(scale * i + (d_sub as usize) * s) % evals.evals.len()]
1987 })
1988 }
1989 }
1990 }
1991
1992 fn evaluations_helper<
1993 'a,
1994 'b,
1995 ChallengeTerm,
1996 Challenge: Index<ChallengeTerm, Output = F>,
1997 Environment: ColumnEnvironment<'a, F, ChallengeTerm, Challenge, Column = Column>,
1998 >(
1999 &self,
2000 cache: &'b mut HashMap<CacheId, EvalResult<'a, F>>,
2001 d: Domain,
2002 env: &Environment,
2003 ) -> Either<EvalResult<'a, F>, CacheId>
2004 where
2005 'a: 'b,
2006 {
2007 let dom = (d, env.get_domain(d));
2008
2009 let res: EvalResult<'a, F> = match self {
2010 Expr::Square(x) => match x.evaluations_helper(cache, d, env) {
2011 Either::Left(x) => x.square(dom),
2012 Either::Right(id) => id.get_from(cache).unwrap().square(dom),
2013 },
2014 Expr::Double(x) => {
2015 let x = x.evaluations_helper(cache, d, env);
2016 let res = match x {
2017 Either::Left(x) => {
2018 let x = match x {
2019 EvalResult::Evals { domain, mut evals } => {
2020 o1_utils::cfg_iter_mut!(evals.evals).for_each(|x| {
2021 x.double_in_place();
2022 });
2023 return Either::Left(EvalResult::Evals { domain, evals });
2024 }
2025 x => x,
2026 };
2027 let xx = || match &x {
2028 EvalResult::Constant(x) => EvalResult::Constant(*x),
2029 EvalResult::SubEvals {
2030 domain,
2031 shift,
2032 evals,
2033 } => EvalResult::SubEvals {
2034 domain: *domain,
2035 shift: *shift,
2036 evals,
2037 },
2038 EvalResult::Evals { domain, evals } => EvalResult::SubEvals {
2039 domain: *domain,
2040 shift: 0,
2041 evals,
2042 },
2043 };
2044 xx().add(xx(), dom)
2045 }
2046 Either::Right(id) => {
2047 let x1 = id.get_from(cache).unwrap();
2048 let x2 = id.get_from(cache).unwrap();
2049 x1.add(x2, dom)
2050 }
2051 };
2052 return Either::Left(res);
2053 }
2054 Expr::Cache(id, e) => match cache.get(id) {
2055 Some(_) => return Either::Right(*id),
2056 None => {
2057 match e.evaluations_helper(cache, d, env) {
2058 Either::Left(es) => {
2059 cache.insert(*id, es);
2060 }
2061 Either::Right(_) => {}
2062 };
2063 return Either::Right(*id);
2064 }
2065 },
2066 Expr::Pow(x, p) => {
2067 let x = x.evaluations_helper(cache, d, env);
2068 match x {
2069 Either::Left(x) => x.pow(*p, (d, env.get_domain(d))),
2070 Either::Right(id) => {
2071 id.get_from(cache).unwrap().pow(*p, (d, env.get_domain(d)))
2072 }
2073 }
2074 }
2075 Expr::Atom(ExprInner::VanishesOnZeroKnowledgeAndPreviousRows) => EvalResult::SubEvals {
2076 domain: Domain::D8,
2077 shift: 0,
2078 evals: env.vanishes_on_zero_knowledge_and_previous_rows(),
2079 },
2080 Expr::Atom(ExprInner::Constant(x)) => EvalResult::Constant(*x),
2081 Expr::Atom(ExprInner::UnnormalizedLagrangeBasis(i)) => {
2082 let offset = if i.zk_rows {
2083 -(env.get_constants().zk_rows as i32) + i.offset
2084 } else {
2085 i.offset
2086 };
2087 EvalResult::Evals {
2088 domain: d,
2089 evals: unnormalized_lagrange_evals(env.l0_1(), offset, d, env),
2090 }
2091 }
2092 Expr::Atom(ExprInner::Cell(Variable { col, row })) => {
2093 let evals: &'a Evaluations<F, D<F>> = {
2094 match env.get_column(col) {
2095 None => return Either::Left(EvalResult::Constant(F::zero())),
2096 Some(e) => e,
2097 }
2098 };
2099 EvalResult::SubEvals {
2100 domain: env.column_domain(col),
2101 shift: row.shift(),
2102 evals,
2103 }
2104 }
2105 Expr::Add(e1, e2) => {
2106 let dom = (d, env.get_domain(d));
2107 let f = |x: EvalResult<F>, y: EvalResult<F>| x.add(y, dom);
2108 let e1 = e1.evaluations_helper(cache, d, env);
2109 let e2 = e2.evaluations_helper(cache, d, env);
2110 use Either::*;
2111 match (e1, e2) {
2112 (Left(e1), Left(e2)) => f(e1, e2),
2113 (Right(id1), Left(e2)) => f(id1.get_from(cache).unwrap(), e2),
2114 (Left(e1), Right(id2)) => f(e1, id2.get_from(cache).unwrap()),
2115 (Right(id1), Right(id2)) => {
2116 f(id1.get_from(cache).unwrap(), id2.get_from(cache).unwrap())
2117 }
2118 }
2119 }
2120 Expr::Sub(e1, e2) => {
2121 let dom = (d, env.get_domain(d));
2122 let f = |x: EvalResult<F>, y: EvalResult<F>| x.sub(y, dom);
2123 let e1 = e1.evaluations_helper(cache, d, env);
2124 let e2 = e2.evaluations_helper(cache, d, env);
2125 use Either::*;
2126 match (e1, e2) {
2127 (Left(e1), Left(e2)) => f(e1, e2),
2128 (Right(id1), Left(e2)) => f(id1.get_from(cache).unwrap(), e2),
2129 (Left(e1), Right(id2)) => f(e1, id2.get_from(cache).unwrap()),
2130 (Right(id1), Right(id2)) => {
2131 f(id1.get_from(cache).unwrap(), id2.get_from(cache).unwrap())
2132 }
2133 }
2134 }
2135 Expr::Mul(e1, e2) => {
2136 let dom = (d, env.get_domain(d));
2137 let f = |x: EvalResult<F>, y: EvalResult<F>| x.mul(y, dom);
2138 let e1 = e1.evaluations_helper(cache, d, env);
2139 let e2 = e2.evaluations_helper(cache, d, env);
2140 use Either::*;
2141 match (e1, e2) {
2142 (Left(e1), Left(e2)) => f(e1, e2),
2143 (Right(id1), Left(e2)) => f(id1.get_from(cache).unwrap(), e2),
2144 (Left(e1), Right(id2)) => f(e1, id2.get_from(cache).unwrap()),
2145 (Right(id1), Right(id2)) => {
2146 f(id1.get_from(cache).unwrap(), id2.get_from(cache).unwrap())
2147 }
2148 }
2149 }
2150 Expr::IfFeature(feature, e1, e2) => {
2151 let mut cache = cache.clone();
2154 if feature.is_enabled() {
2155 return e1.evaluations_helper(&mut cache, d, env);
2156 } else {
2157 return e2.evaluations_helper(&mut cache, d, env);
2158 }
2159 }
2160 };
2161 Either::Left(res)
2162 }
2163}
2164
2165#[derive(Clone, Debug, Serialize, Deserialize)]
2166pub struct Linearization<E, Column> {
2169 pub constant_term: E,
2170 pub index_terms: Vec<(Column, E)>,
2171}
2172
2173impl<E: Default, Column> Default for Linearization<E, Column> {
2174 fn default() -> Self {
2175 Linearization {
2176 constant_term: E::default(),
2177 index_terms: vec![],
2178 }
2179 }
2180}
2181
2182impl<A, Column: Copy> Linearization<A, Column> {
2183 pub fn map<B, F: Fn(&A) -> B>(&self, f: F) -> Linearization<B, Column> {
2185 Linearization {
2186 constant_term: f(&self.constant_term),
2187 index_terms: self.index_terms.iter().map(|(c, x)| (*c, f(x))).collect(),
2188 }
2189 }
2190}
2191
2192impl<F: FftField, Column: PartialEq + Copy, ChallengeTerm: Copy>
2193 Linearization<Expr<ConstantExpr<F, ChallengeTerm>, Column>, Column>
2194{
2195 pub fn evaluate_constants<
2198 'a,
2199 Challenge: Index<ChallengeTerm, Output = F>,
2200 Environment: ColumnEnvironment<'a, F, ChallengeTerm, Challenge, Column = Column>,
2201 >(
2202 &self,
2203 env: &Environment,
2204 ) -> Linearization<Expr<F, Column>, Column> {
2205 self.map(|e| e.evaluate_constants(env))
2206 }
2207}
2208
2209impl<F: FftField, Column: Copy + Debug, ChallengeTerm: Copy>
2210 Linearization<Vec<PolishToken<F, Column, ChallengeTerm>>, Column>
2211{
2212 pub fn to_polynomial<
2215 'a,
2216 Challenge: Index<ChallengeTerm, Output = F>,
2217 ColEvaluations: ColumnEvaluations<F, Column = Column>,
2218 Environment: ColumnEnvironment<'a, F, ChallengeTerm, Challenge, Column = Column>,
2219 >(
2220 &self,
2221 env: &Environment,
2222 pt: F,
2223 evals: &ColEvaluations,
2224 ) -> (F, Evaluations<F, D<F>>) {
2225 let cs = env.get_constants();
2226 let chals = env.get_challenges();
2227 let d1 = env.get_domain(Domain::D1);
2228 let n = d1.size();
2229 let mut res = vec![F::zero(); n];
2230 self.index_terms.iter().for_each(|(idx, c)| {
2231 let c = PolishToken::evaluate(c, d1, pt, evals, cs, chals).unwrap();
2232 let e = env
2233 .get_column(idx)
2234 .unwrap_or_else(|| panic!("Index polynomial {idx:?} not found"));
2235 let scale = e.evals.len() / n;
2236 o1_utils::cfg_iter_mut!(res)
2237 .enumerate()
2238 .for_each(|(i, r)| *r += c * e.evals[scale * i]);
2239 });
2240 let p = Evaluations::<F, D<F>>::from_vec_and_domain(res, d1);
2241 (
2242 PolishToken::evaluate(&self.constant_term, d1, pt, evals, cs, chals).unwrap(),
2243 p,
2244 )
2245 }
2246}
2247
2248impl<F: FftField, Column: Debug + PartialEq + Copy, ChallengeTerm: Copy>
2249 Linearization<Expr<ConstantExpr<F, ChallengeTerm>, Column>, Column>
2250{
2251 pub fn to_polynomial<
2254 'a,
2255 Challenge: Index<ChallengeTerm, Output = F>,
2256 ColEvaluations: ColumnEvaluations<F, Column = Column>,
2257 Environment: ColumnEnvironment<'a, F, ChallengeTerm, Challenge, Column = Column>,
2258 >(
2259 &self,
2260 env: &Environment,
2261 pt: F,
2262 evals: &ColEvaluations,
2263 ) -> (F, DensePolynomial<F>) {
2264 let cs = env.get_constants();
2265 let chals = env.get_challenges();
2266 let d1 = env.get_domain(Domain::D1);
2267 let n = d1.size();
2268 let mut res = vec![F::zero(); n];
2269 self.index_terms.iter().for_each(|(idx, c)| {
2270 let c = c.evaluate_(d1, pt, evals, cs, chals).unwrap();
2271 let e = env
2272 .get_column(idx)
2273 .unwrap_or_else(|| panic!("Index polynomial {idx:?} not found"));
2274 let scale = e.evals.len() / n;
2275 o1_utils::cfg_iter_mut!(res)
2276 .enumerate()
2277 .for_each(|(i, r)| *r += c * e.evals[scale * i])
2278 });
2279 let p = Evaluations::<F, D<F>>::from_vec_and_domain(res, d1).interpolate();
2280 (
2281 self.constant_term
2282 .evaluate_(d1, pt, evals, cs, chals)
2283 .unwrap(),
2284 p,
2285 )
2286 }
2287}
2288
2289type Monomials<F, Column> = HashMap<Vec<Variable<Column>>, Expr<F, Column>>;
2290
2291fn mul_monomials<
2292 F: Neg<Output = F> + Clone + One + Zero + PartialEq,
2293 Column: Ord + Copy + core::hash::Hash,
2294>(
2295 e1: &Monomials<F, Column>,
2296 e2: &Monomials<F, Column>,
2297) -> Monomials<F, Column>
2298where
2299 ExprInner<F, Column>: Literal,
2300 <ExprInner<F, Column> as Literal>::F: Field,
2301{
2302 let mut res: HashMap<_, Expr<F, Column>> = HashMap::new();
2303 for (m1, c1) in e1.iter() {
2304 for (m2, c2) in e2.iter() {
2305 let mut m = m1.clone();
2306 m.extend(m2);
2307 m.sort();
2308 let c1c2 = c1.clone() * c2.clone();
2309 let v = res.entry(m).or_insert_with(Expr::<F, Column>::zero);
2310 *v = v.clone() + c1c2;
2311 }
2312 }
2313 res
2314}
2315
2316impl<
2317 F: Neg<Output = F> + Clone + One + Zero + PartialEq,
2318 Column: Ord + Copy + core::hash::Hash,
2319 > Expr<F, Column>
2320where
2321 ExprInner<F, Column>: Literal,
2322 <ExprInner<F, Column> as Literal>::F: Field,
2323{
2324 fn is_constant(&self, evaluated: &HashSet<Column>) -> bool {
2329 use ExprInner::*;
2330 use Operations::*;
2331 match self {
2332 Pow(x, _) => x.is_constant(evaluated),
2333 Square(x) => x.is_constant(evaluated),
2334 Atom(Constant(_)) => true,
2335 Atom(Cell(v)) => evaluated.contains(&v.col),
2336 Double(x) => x.is_constant(evaluated),
2337 Add(x, y) | Sub(x, y) | Mul(x, y) => {
2338 x.is_constant(evaluated) && y.is_constant(evaluated)
2339 }
2340 Atom(VanishesOnZeroKnowledgeAndPreviousRows) => true,
2341 Atom(UnnormalizedLagrangeBasis(_)) => true,
2342 Cache(_, x) => x.is_constant(evaluated),
2343 IfFeature(_, e1, e2) => e1.is_constant(evaluated) && e2.is_constant(evaluated),
2344 }
2345 }
2346
2347 fn monomials(&self, ev: &HashSet<Column>) -> HashMap<Vec<Variable<Column>>, Expr<F, Column>> {
2348 let sing = |v: Vec<Variable<Column>>, c: Expr<F, Column>| {
2349 let mut h = HashMap::new();
2350 h.insert(v, c);
2351 h
2352 };
2353 let constant = |e: Expr<F, Column>| sing(vec![], e);
2354 use ExprInner::*;
2355 use Operations::*;
2356
2357 if self.is_constant(ev) {
2358 return constant(self.clone());
2359 }
2360
2361 match self {
2362 Pow(x, d) => {
2363 let mut acc = sing(vec![], Expr::<F, Column>::one());
2365 let mut acc_is_one = true;
2366 let x = x.monomials(ev);
2367
2368 for i in (0..u64::BITS).rev() {
2369 if !acc_is_one {
2370 let acc2 = mul_monomials(&acc, &acc);
2371 acc = acc2;
2372 }
2373
2374 if (d >> i) & 1 == 1 {
2375 let res = mul_monomials(&acc, &x);
2376 acc = res;
2377 acc_is_one = false;
2378 }
2379 }
2380 acc
2381 }
2382 Double(e) => {
2383 HashMap::from_iter(e.monomials(ev).into_iter().map(|(m, c)| (m, c.double())))
2384 }
2385 Cache(_, e) => e.monomials(ev),
2386 Atom(UnnormalizedLagrangeBasis(i)) => constant(Atom(UnnormalizedLagrangeBasis(*i))),
2387 Atom(VanishesOnZeroKnowledgeAndPreviousRows) => {
2388 constant(Atom(VanishesOnZeroKnowledgeAndPreviousRows))
2389 }
2390 Atom(Constant(c)) => constant(Atom(Constant(c.clone()))),
2391 Atom(Cell(var)) => sing(vec![*var], Atom(Constant(F::one()))),
2392 Add(e1, e2) => {
2393 let mut res = e1.monomials(ev);
2394 for (m, c) in e2.monomials(ev) {
2395 let v = match res.remove(&m) {
2396 None => c,
2397 Some(v) => v + c,
2398 };
2399 res.insert(m, v);
2400 }
2401 res
2402 }
2403 Sub(e1, e2) => {
2404 let mut res = e1.monomials(ev);
2405 for (m, c) in e2.monomials(ev) {
2406 let v = match res.remove(&m) {
2407 None => -c, Some(v) => v - c,
2409 };
2410 res.insert(m, v);
2411 }
2412 res
2413 }
2414 Mul(e1, e2) => {
2415 let e1 = e1.monomials(ev);
2416 let e2 = e2.monomials(ev);
2417 mul_monomials(&e1, &e2)
2418 }
2419 Square(x) => {
2420 let x = x.monomials(ev);
2421 mul_monomials(&x, &x)
2422 }
2423 IfFeature(feature, e1, e2) => {
2424 let mut res = HashMap::new();
2425 let e1_monomials = e1.monomials(ev);
2426 let mut e2_monomials = e2.monomials(ev);
2427 for (m, c) in e1_monomials.into_iter() {
2428 let else_branch = match e2_monomials.remove(&m) {
2429 None => Expr::zero(),
2430 Some(c) => c,
2431 };
2432 let expr = Expr::IfFeature(*feature, Box::new(c), Box::new(else_branch));
2433 res.insert(m, expr);
2434 }
2435 for (m, c) in e2_monomials.into_iter() {
2436 let expr = Expr::IfFeature(*feature, Box::new(Expr::zero()), Box::new(c));
2437 res.insert(m, expr);
2438 }
2439 res
2440 }
2441 }
2442 }
2443
2444 pub fn linearize(
2471 &self,
2472 evaluated: HashSet<Column>,
2473 ) -> Result<Linearization<Expr<F, Column>, Column>, ExprError<Column>> {
2474 let mut res: HashMap<Column, Expr<F, Column>> = HashMap::new();
2475 let mut constant_term: Expr<F, Column> = Self::zero();
2476 let monomials = self.monomials(&evaluated);
2477
2478 for (m, c) in monomials {
2479 let (evaluated, mut unevaluated): (Vec<_>, _) =
2480 m.into_iter().partition(|v| evaluated.contains(&v.col));
2481 let c = evaluated
2482 .into_iter()
2483 .fold(c, |acc, v| acc * Expr::Atom(ExprInner::Cell(v)));
2484 if unevaluated.is_empty() {
2485 constant_term += c;
2486 } else if unevaluated.len() == 1 {
2487 let var = unevaluated.remove(0);
2488 match var.row {
2489 Next => {
2490 return Err(ExprError::MissingEvaluation(var.col, var.row));
2491 }
2492 Curr => {
2493 let e = match res.remove(&var.col) {
2494 Some(v) => v + c,
2495 None => c,
2496 };
2497 res.insert(var.col, e);
2498 }
2510 }
2511 } else {
2512 return Err(ExprError::FailedLinearization(unevaluated));
2513 }
2514 }
2515 Ok(Linearization {
2516 constant_term,
2517 index_terms: res.into_iter().collect(),
2518 })
2519 }
2520}
2521
2522impl<T: Literal> Zero for Operations<T>
2525where
2526 T::F: Field,
2527{
2528 fn zero() -> Self {
2529 Self::literal(T::F::zero())
2530 }
2531
2532 fn is_zero(&self) -> bool {
2533 if let Some(x) = self.to_literal_ref() {
2534 x.is_zero()
2535 } else {
2536 false
2537 }
2538 }
2539}
2540
2541impl<T: Literal + PartialEq> One for Operations<T>
2542where
2543 T::F: Field,
2544{
2545 fn one() -> Self {
2546 Self::literal(T::F::one())
2547 }
2548
2549 fn is_one(&self) -> bool {
2550 if let Some(x) = self.to_literal_ref() {
2551 x.is_one()
2552 } else {
2553 false
2554 }
2555 }
2556}
2557
2558impl<T: Literal> Neg for Operations<T>
2559where
2560 T::F: One + Neg<Output = T::F> + Copy,
2561{
2562 type Output = Self;
2563
2564 fn neg(self) -> Self {
2565 match self.to_literal() {
2566 Ok(x) => Self::literal(x.neg()),
2567 Err(x) => Operations::Mul(Box::new(Self::literal(T::F::one().neg())), Box::new(x)),
2568 }
2569 }
2570}
2571
2572impl<T: Literal> Add<Self> for Operations<T>
2573where
2574 T::F: Field,
2575{
2576 type Output = Self;
2577 fn add(self, other: Self) -> Self {
2578 if self.is_zero() {
2579 return other;
2580 }
2581 if other.is_zero() {
2582 return self;
2583 }
2584 let (x, y) = {
2585 match (self.to_literal(), other.to_literal()) {
2586 (Ok(x), Ok(y)) => return Self::literal(x + y),
2587 (Ok(x), Err(y)) => (Self::literal(x), y),
2588 (Err(x), Ok(y)) => (x, Self::literal(y)),
2589 (Err(x), Err(y)) => (x, y),
2590 }
2591 };
2592 Operations::Add(Box::new(x), Box::new(y))
2593 }
2594}
2595
2596impl<T: Literal> Sub<Self> for Operations<T>
2597where
2598 T::F: Field,
2599{
2600 type Output = Self;
2601 fn sub(self, other: Self) -> Self {
2602 if other.is_zero() {
2603 return self;
2604 }
2605 let (x, y) = {
2606 match (self.to_literal(), other.to_literal()) {
2607 (Ok(x), Ok(y)) => return Self::literal(x - y),
2608 (Ok(x), Err(y)) => (Self::literal(x), y),
2609 (Err(x), Ok(y)) => (x, Self::literal(y)),
2610 (Err(x), Err(y)) => (x, y),
2611 }
2612 };
2613 Operations::Sub(Box::new(x), Box::new(y))
2614 }
2615}
2616
2617impl<T: Literal + PartialEq> Mul<Self> for Operations<T>
2618where
2619 T::F: Field,
2620{
2621 type Output = Self;
2622 fn mul(self, other: Self) -> Self {
2623 if self.is_zero() || other.is_zero() {
2624 return Self::zero();
2625 }
2626
2627 if self.is_one() {
2628 return other;
2629 }
2630 if other.is_one() {
2631 return self;
2632 }
2633 let (x, y) = {
2634 match (self.to_literal(), other.to_literal()) {
2635 (Ok(x), Ok(y)) => return Self::literal(x * y),
2636 (Ok(x), Err(y)) => (Self::literal(x), y),
2637 (Err(x), Ok(y)) => (x, Self::literal(y)),
2638 (Err(x), Err(y)) => (x, y),
2639 }
2640 };
2641 Operations::Mul(Box::new(x), Box::new(y))
2642 }
2643}
2644
2645impl<F: Zero + Clone, Column: Clone> AddAssign<Expr<F, Column>> for Expr<F, Column>
2646where
2647 ExprInner<F, Column>: Literal,
2648 <ExprInner<F, Column> as Literal>::F: Field,
2649{
2650 fn add_assign(&mut self, other: Self) {
2651 if self.is_zero() {
2652 *self = other;
2653 } else if !other.is_zero() {
2654 *self = Expr::Add(Box::new(self.clone()), Box::new(other));
2655 }
2656 }
2657}
2658
2659impl<F, Column> MulAssign<Expr<F, Column>> for Expr<F, Column>
2660where
2661 F: Zero + One + PartialEq + Clone,
2662 Column: PartialEq + Clone,
2663 ExprInner<F, Column>: Literal,
2664 <ExprInner<F, Column> as Literal>::F: Field,
2665{
2666 fn mul_assign(&mut self, other: Self) {
2667 if self.is_zero() || other.is_zero() {
2668 *self = Self::zero();
2669 } else if self.is_one() {
2670 *self = other;
2671 } else if !other.is_one() {
2672 *self = Expr::Mul(Box::new(self.clone()), Box::new(other));
2673 }
2674 }
2675}
2676
2677impl<F: Field, Column> From<u64> for Expr<F, Column> {
2678 fn from(x: u64) -> Self {
2679 Expr::Atom(ExprInner::Constant(F::from(x)))
2680 }
2681}
2682
2683impl<'a, F: Field, Column, ChallengeTerm: AlphaChallengeTerm<'a>> From<u64>
2684 for Expr<ConstantExpr<F, ChallengeTerm>, Column>
2685{
2686 fn from(x: u64) -> Self {
2687 ConstantTerm::Literal(F::from(x)).into()
2688 }
2689}
2690
2691impl<F: Field, ChallengeTerm> From<u64> for ConstantExpr<F, ChallengeTerm> {
2692 fn from(x: u64) -> Self {
2693 ConstantTerm::Literal(F::from(x)).into()
2694 }
2695}
2696
2697impl<'a, F: Field, Column: PartialEq + Copy, ChallengeTerm: AlphaChallengeTerm<'a>> Mul<F>
2698 for Expr<ConstantExpr<F, ChallengeTerm>, Column>
2699{
2700 type Output = Expr<ConstantExpr<F, ChallengeTerm>, Column>;
2701
2702 fn mul(self, y: F) -> Self::Output {
2703 Expr::from(ConstantTerm::Literal(y)) * self
2704 }
2705}
2706
2707pub trait FormattedOutput: Sized {
2712 fn is_alpha(&self) -> bool;
2713 fn ocaml(&self, cache: &mut HashMap<CacheId, Self>) -> String;
2714 fn latex(&self, cache: &mut HashMap<CacheId, Self>) -> String;
2715 fn text(&self, cache: &mut HashMap<CacheId, Self>) -> String;
2716}
2717
2718impl<'a, ChallengeTerm> FormattedOutput for ChallengeTerm
2719where
2720 ChallengeTerm: AlphaChallengeTerm<'a>,
2721{
2722 fn is_alpha(&self) -> bool {
2723 self.eq(&ChallengeTerm::ALPHA)
2724 }
2725 fn ocaml(&self, _cache: &mut HashMap<CacheId, Self>) -> String {
2726 self.to_string()
2727 }
2728
2729 fn latex(&self, _cache: &mut HashMap<CacheId, Self>) -> String {
2730 "\\".to_string() + &self.to_string()
2731 }
2732
2733 fn text(&self, _cache: &mut HashMap<CacheId, Self>) -> String {
2734 self.to_string()
2735 }
2736}
2737
2738impl<F: PrimeField> FormattedOutput for ConstantTerm<F> {
2739 fn is_alpha(&self) -> bool {
2740 false
2741 }
2742 fn ocaml(&self, _cache: &mut HashMap<CacheId, Self>) -> String {
2743 use ConstantTerm::*;
2744 match self {
2745 EndoCoefficient => "endo_coefficient".to_string(),
2746 Mds { row, col } => format!("mds({row}, {col})"),
2747 Literal(x) => format!(
2748 "field(\"{:#066X}\")",
2749 Into::<num_bigint::BigUint>::into(x.into_bigint())
2750 ),
2751 }
2752 }
2753
2754 fn latex(&self, _cache: &mut HashMap<CacheId, Self>) -> String {
2755 use ConstantTerm::*;
2756 match self {
2757 EndoCoefficient => "endo\\_coefficient".to_string(),
2758 Mds { row, col } => format!("mds({row}, {col})"),
2759 Literal(x) => format!("\\mathbb{{F}}({})", x.into_bigint().into()),
2760 }
2761 }
2762
2763 fn text(&self, _cache: &mut HashMap<CacheId, Self>) -> String {
2764 use ConstantTerm::*;
2765 match self {
2766 EndoCoefficient => "endo_coefficient".to_string(),
2767 Mds { row, col } => format!("mds({row}, {col})"),
2768 Literal(x) => format!("0x{}", x.to_hex()),
2769 }
2770 }
2771}
2772
2773impl<'a, F: PrimeField, ChallengeTerm> FormattedOutput for ConstantExprInner<F, ChallengeTerm>
2774where
2775 ChallengeTerm: AlphaChallengeTerm<'a>,
2776{
2777 fn is_alpha(&self) -> bool {
2778 use ConstantExprInner::*;
2779 match self {
2780 Challenge(x) => x.is_alpha(),
2781 Constant(x) => x.is_alpha(),
2782 }
2783 }
2784 fn ocaml(&self, cache: &mut HashMap<CacheId, Self>) -> String {
2785 use ConstantExprInner::*;
2786 match self {
2787 Challenge(x) => {
2788 let mut inner_cache = HashMap::new();
2789 let res = x.ocaml(&mut inner_cache);
2790 inner_cache.into_iter().for_each(|(k, v)| {
2791 let _ = cache.insert(k, Challenge(v));
2792 });
2793 res
2794 }
2795 Constant(x) => {
2796 let mut inner_cache = HashMap::new();
2797 let res = x.ocaml(&mut inner_cache);
2798 inner_cache.into_iter().for_each(|(k, v)| {
2799 let _ = cache.insert(k, Constant(v));
2800 });
2801 res
2802 }
2803 }
2804 }
2805 fn latex(&self, cache: &mut HashMap<CacheId, Self>) -> String {
2806 use ConstantExprInner::*;
2807 match self {
2808 Challenge(x) => {
2809 let mut inner_cache = HashMap::new();
2810 let res = x.latex(&mut inner_cache);
2811 inner_cache.into_iter().for_each(|(k, v)| {
2812 let _ = cache.insert(k, Challenge(v));
2813 });
2814 res
2815 }
2816 Constant(x) => {
2817 let mut inner_cache = HashMap::new();
2818 let res = x.latex(&mut inner_cache);
2819 inner_cache.into_iter().for_each(|(k, v)| {
2820 let _ = cache.insert(k, Constant(v));
2821 });
2822 res
2823 }
2824 }
2825 }
2826 fn text(&self, cache: &mut HashMap<CacheId, Self>) -> String {
2827 use ConstantExprInner::*;
2828 match self {
2829 Challenge(x) => {
2830 let mut inner_cache = HashMap::new();
2831 let res = x.text(&mut inner_cache);
2832 inner_cache.into_iter().for_each(|(k, v)| {
2833 let _ = cache.insert(k, Challenge(v));
2834 });
2835 res
2836 }
2837 Constant(x) => {
2838 let mut inner_cache = HashMap::new();
2839 let res = x.text(&mut inner_cache);
2840 inner_cache.into_iter().for_each(|(k, v)| {
2841 let _ = cache.insert(k, Constant(v));
2842 });
2843 res
2844 }
2845 }
2846 }
2847}
2848
2849impl<Column: FormattedOutput + Debug> FormattedOutput for Variable<Column> {
2850 fn is_alpha(&self) -> bool {
2851 false
2852 }
2853
2854 fn ocaml(&self, _cache: &mut HashMap<CacheId, Self>) -> String {
2855 format!("var({:?}, {:?})", self.col, self.row)
2856 }
2857
2858 fn latex(&self, _cache: &mut HashMap<CacheId, Self>) -> String {
2859 let col = self.col.latex(&mut HashMap::new());
2860 match self.row {
2861 Curr => col,
2862 Next => format!("\\tilde{{{col}}}"),
2863 }
2864 }
2865
2866 fn text(&self, _cache: &mut HashMap<CacheId, Self>) -> String {
2867 let col = self.col.text(&mut HashMap::new());
2868 match self.row {
2869 Curr => format!("Curr({col})"),
2870 Next => format!("Next({col})"),
2871 }
2872 }
2873}
2874
2875impl<T: FormattedOutput + Clone> FormattedOutput for Operations<T> {
2876 fn is_alpha(&self) -> bool {
2877 match self {
2878 Operations::Atom(x) => x.is_alpha(),
2879 _ => false,
2880 }
2881 }
2882 fn ocaml(&self, cache: &mut HashMap<CacheId, Self>) -> String {
2883 use Operations::*;
2884 match self {
2885 Atom(x) => {
2886 let mut inner_cache = HashMap::new();
2887 let res = x.ocaml(&mut inner_cache);
2888 inner_cache.into_iter().for_each(|(k, v)| {
2889 let _ = cache.insert(k, Atom(v));
2890 });
2891 res
2892 }
2893 Pow(x, n) => {
2894 if x.is_alpha() {
2895 format!("alpha_pow({n})")
2896 } else {
2897 format!("pow({}, {n})", x.ocaml(cache))
2898 }
2899 }
2900 Add(x, y) => format!("({} + {})", x.ocaml(cache), y.ocaml(cache)),
2901 Mul(x, y) => format!("({} * {})", x.ocaml(cache), y.ocaml(cache)),
2902 Sub(x, y) => format!("({} - {})", x.ocaml(cache), y.ocaml(cache)),
2903 Double(x) => format!("double({})", x.ocaml(cache)),
2904 Square(x) => format!("square({})", x.ocaml(cache)),
2905 Cache(id, e) => {
2906 cache.insert(*id, e.as_ref().clone());
2907 id.var_name()
2908 }
2909 IfFeature(feature, e1, e2) => {
2910 format!(
2911 "if_feature({:?}, (fun () -> {}), (fun () -> {}))",
2912 feature,
2913 e1.ocaml(cache),
2914 e2.ocaml(cache)
2915 )
2916 }
2917 }
2918 }
2919
2920 fn latex(&self, cache: &mut HashMap<CacheId, Self>) -> String {
2921 use Operations::*;
2922 match self {
2923 Atom(x) => {
2924 let mut inner_cache = HashMap::new();
2925 let res = x.latex(&mut inner_cache);
2926 inner_cache.into_iter().for_each(|(k, v)| {
2927 let _ = cache.insert(k, Atom(v));
2928 });
2929 res
2930 }
2931 Pow(x, n) => format!("{}^{{{n}}}", x.latex(cache)),
2932 Add(x, y) => format!("({} + {})", x.latex(cache), y.latex(cache)),
2933 Mul(x, y) => format!("({} \\cdot {})", x.latex(cache), y.latex(cache)),
2934 Sub(x, y) => format!("({} - {})", x.latex(cache), y.latex(cache)),
2935 Double(x) => format!("2 ({})", x.latex(cache)),
2936 Square(x) => format!("({})^2", x.latex(cache)),
2937 Cache(id, e) => {
2938 cache.insert(*id, e.as_ref().clone());
2939 id.var_name()
2940 }
2941 IfFeature(feature, _, _) => format!("{feature:?}"),
2942 }
2943 }
2944
2945 fn text(&self, cache: &mut HashMap<CacheId, Self>) -> String {
2946 use Operations::*;
2947 match self {
2948 Atom(x) => {
2949 let mut inner_cache = HashMap::new();
2950 let res = x.text(&mut inner_cache);
2951 inner_cache.into_iter().for_each(|(k, v)| {
2952 let _ = cache.insert(k, Atom(v));
2953 });
2954 res
2955 }
2956 Pow(x, n) => format!("{}^{n}", x.text(cache)),
2957 Add(x, y) => format!("({} + {})", x.text(cache), y.text(cache)),
2958 Mul(x, y) => format!("({} * {})", x.text(cache), y.text(cache)),
2959 Sub(x, y) => format!("({} - {})", x.text(cache), y.text(cache)),
2960 Double(x) => format!("double({})", x.text(cache)),
2961 Square(x) => format!("square({})", x.text(cache)),
2962 Cache(id, e) => {
2963 cache.insert(*id, e.as_ref().clone());
2964 id.var_name()
2965 }
2966 IfFeature(feature, _, _) => format!("{feature:?}"),
2967 }
2968 }
2969}
2970
2971impl<'a, F, Column: FormattedOutput + Debug + Clone, ChallengeTerm> FormattedOutput
2972 for Expr<ConstantExpr<F, ChallengeTerm>, Column>
2973where
2974 F: PrimeField,
2975 ChallengeTerm: AlphaChallengeTerm<'a>,
2976{
2977 fn is_alpha(&self) -> bool {
2978 use ExprInner::*;
2979 use Operations::*;
2980 match self {
2981 Atom(Constant(x)) => x.is_alpha(),
2982 _ => false,
2983 }
2984 }
2985 fn ocaml(
2989 &self,
2990 cache: &mut HashMap<CacheId, Expr<ConstantExpr<F, ChallengeTerm>, Column>>,
2991 ) -> String {
2992 use ExprInner::*;
2993 use Operations::*;
2994 match self {
2995 Double(x) => format!("double({})", x.ocaml(cache)),
2996 Atom(Constant(x)) => {
2997 let mut inner_cache = HashMap::new();
2998 let res = x.ocaml(&mut inner_cache);
2999 inner_cache.into_iter().for_each(|(k, v)| {
3000 let _ = cache.insert(k, Atom(Constant(v)));
3001 });
3002 res
3003 }
3004 Atom(Cell(v)) => format!("cell({})", v.ocaml(&mut HashMap::new())),
3005 Atom(UnnormalizedLagrangeBasis(i)) => {
3006 format!("unnormalized_lagrange_basis({}, {})", i.zk_rows, i.offset)
3007 }
3008 Atom(VanishesOnZeroKnowledgeAndPreviousRows) => {
3009 "vanishes_on_zero_knowledge_and_previous_rows".to_string()
3010 }
3011 Add(x, y) => format!("({} + {})", x.ocaml(cache), y.ocaml(cache)),
3012 Mul(x, y) => format!("({} * {})", x.ocaml(cache), y.ocaml(cache)),
3013 Sub(x, y) => format!("({} - {})", x.ocaml(cache), y.ocaml(cache)),
3014 Pow(x, d) => format!("pow({}, {d})", x.ocaml(cache)),
3015 Square(x) => format!("square({})", x.ocaml(cache)),
3016 Cache(id, e) => {
3017 cache.insert(*id, e.as_ref().clone());
3018 id.var_name()
3019 }
3020 IfFeature(feature, e1, e2) => {
3021 format!(
3022 "if_feature({:?}, (fun () -> {}), (fun () -> {}))",
3023 feature,
3024 e1.ocaml(cache),
3025 e2.ocaml(cache)
3026 )
3027 }
3028 }
3029 }
3030
3031 fn latex(
3032 &self,
3033 cache: &mut HashMap<CacheId, Expr<ConstantExpr<F, ChallengeTerm>, Column>>,
3034 ) -> String {
3035 use ExprInner::*;
3036 use Operations::*;
3037 match self {
3038 Double(x) => format!("2 ({})", x.latex(cache)),
3039 Atom(Constant(x)) => {
3040 let mut inner_cache = HashMap::new();
3041 let res = x.latex(&mut inner_cache);
3042 inner_cache.into_iter().for_each(|(k, v)| {
3043 let _ = cache.insert(k, Atom(Constant(v)));
3044 });
3045 res
3046 }
3047 Atom(Cell(v)) => v.latex(&mut HashMap::new()),
3048 Atom(UnnormalizedLagrangeBasis(RowOffset {
3049 zk_rows: true,
3050 offset: i,
3051 })) => {
3052 format!("unnormalized\\_lagrange\\_basis(zk\\_rows + {})", *i)
3053 }
3054 Atom(UnnormalizedLagrangeBasis(RowOffset {
3055 zk_rows: false,
3056 offset: i,
3057 })) => {
3058 format!("unnormalized\\_lagrange\\_basis({})", *i)
3059 }
3060 Atom(VanishesOnZeroKnowledgeAndPreviousRows) => {
3061 "vanishes\\_on\\_zero\\_knowledge\\_and\\_previous\\_row".to_string()
3062 }
3063 Add(x, y) => format!("({} + {})", x.latex(cache), y.latex(cache)),
3064 Mul(x, y) => format!("({} \\cdot {})", x.latex(cache), y.latex(cache)),
3065 Sub(x, y) => format!("({} - {})", x.latex(cache), y.latex(cache)),
3066 Pow(x, d) => format!("{}^{{{d}}}", x.latex(cache)),
3067 Square(x) => format!("({})^2", x.latex(cache)),
3068 Cache(id, e) => {
3069 cache.insert(*id, e.as_ref().clone());
3070 id.latex_name()
3071 }
3072 IfFeature(feature, _, _) => format!("{feature:?}"),
3073 }
3074 }
3075
3076 fn text(
3079 &self,
3080 cache: &mut HashMap<CacheId, Expr<ConstantExpr<F, ChallengeTerm>, Column>>,
3081 ) -> String {
3082 use ExprInner::*;
3083 use Operations::*;
3084 match self {
3085 Double(x) => format!("double({})", x.text(cache)),
3086 Atom(Constant(x)) => {
3087 let mut inner_cache = HashMap::new();
3088 let res = x.text(&mut inner_cache);
3089 inner_cache.into_iter().for_each(|(k, v)| {
3090 let _ = cache.insert(k, Atom(Constant(v)));
3091 });
3092 res
3093 }
3094 Atom(Cell(v)) => v.text(&mut HashMap::new()),
3095 Atom(UnnormalizedLagrangeBasis(RowOffset {
3096 zk_rows: true,
3097 offset: i,
3098 })) => match i.cmp(&0) {
3099 Ordering::Greater => format!("unnormalized_lagrange_basis(zk_rows + {})", *i),
3100 Ordering::Equal => "unnormalized_lagrange_basis(zk_rows)".to_string(),
3101 Ordering::Less => format!("unnormalized_lagrange_basis(zk_rows - {})", (-*i)),
3102 },
3103 Atom(UnnormalizedLagrangeBasis(RowOffset {
3104 zk_rows: false,
3105 offset: i,
3106 })) => {
3107 format!("unnormalized_lagrange_basis({})", *i)
3108 }
3109 Atom(VanishesOnZeroKnowledgeAndPreviousRows) => {
3110 "vanishes_on_zero_knowledge_and_previous_rows".to_string()
3111 }
3112 Add(x, y) => format!("({} + {})", x.text(cache), y.text(cache)),
3113 Mul(x, y) => format!("({} * {})", x.text(cache), y.text(cache)),
3114 Sub(x, y) => format!("({} - {})", x.text(cache), y.text(cache)),
3115 Pow(x, d) => format!("pow({}, {d})", x.text(cache)),
3116 Square(x) => format!("square({})", x.text(cache)),
3117 Cache(id, e) => {
3118 cache.insert(*id, e.as_ref().clone());
3119 id.var_name()
3120 }
3121 IfFeature(feature, _, _) => format!("{feature:?}"),
3122 }
3123 }
3124}
3125
3126impl<'a, F, Column: FormattedOutput + Debug + Clone, ChallengeTerm>
3127 Expr<ConstantExpr<F, ChallengeTerm>, Column>
3128where
3129 F: PrimeField,
3130 ChallengeTerm: AlphaChallengeTerm<'a>,
3131{
3132 pub fn latex_str(&self) -> Vec<String> {
3135 let mut env = HashMap::new();
3136 let e = self.latex(&mut env);
3137
3138 let mut env: Vec<_> = env.into_iter().collect();
3139 env.sort_by_key(|(x, _)| *x);
3142
3143 let mut res = vec![];
3144 for (k, v) in env {
3145 let mut rhs = v.latex_str();
3146 let last = rhs.pop().expect("returned an empty expression");
3147 res.push(format!("{} = {last}", k.latex_name()));
3148 res.extend(rhs);
3149 }
3150 res.push(e);
3151 res
3152 }
3153
3154 pub fn ocaml_str(&self) -> String {
3156 let mut env = HashMap::new();
3157 let e = self.ocaml(&mut env);
3158
3159 let mut env: Vec<_> = env.into_iter().collect();
3160 env.sort_by_key(|(x, _)| *x);
3163
3164 let mut res = String::new();
3165 for (k, v) in env {
3166 let rhs = v.ocaml_str();
3167 let cached = format!("let {} = {rhs} in ", k.var_name());
3168 res.push_str(&cached);
3169 }
3170
3171 res.push_str(&e);
3172 res
3173 }
3174}
3175
3176pub mod constraints {
3182 use o1_utils::Two;
3183
3184 use crate::circuits::argument::ArgumentData;
3185 use core::fmt;
3186
3187 use super::*;
3188 use crate::circuits::berkeley_columns::{coeff, witness};
3189
3190 pub trait ExprOps<F, ChallengeTerm>:
3194 Add<Output = Self>
3195 + Sub<Output = Self>
3196 + Neg<Output = Self>
3197 + Mul<Output = Self>
3198 + AddAssign<Self>
3199 + MulAssign<Self>
3200 + Clone
3201 + Zero
3202 + One
3203 + From<u64>
3204 + fmt::Debug
3205 + fmt::Display
3206 where
3208 Self: core::marker::Sized,
3209 {
3210 fn two_pow(pow: u64) -> Self;
3212
3213 fn two_to_limb() -> Self;
3215
3216 fn two_to_2limb() -> Self;
3218
3219 fn two_to_3limb() -> Self;
3221
3222 fn double(&self) -> Self;
3224
3225 fn square(&self) -> Self;
3227
3228 fn pow(&self, p: u64) -> Self;
3230
3231 fn boolean(&self) -> Self;
3233
3234 fn crumb(&self) -> Self;
3236
3237 fn literal(x: F) -> Self;
3239
3240 fn witness(row: CurrOrNext, col: usize, env: Option<&ArgumentData<F>>) -> Self;
3242
3243 fn coeff(col: usize, env: Option<&ArgumentData<F>>) -> Self;
3245
3246 fn constant(expr: ConstantExpr<F, ChallengeTerm>, env: Option<&ArgumentData<F>>) -> Self;
3248
3249 fn cache(&self, cache: &mut Cache) -> Self;
3251 }
3252 impl<F> ExprOps<F, BerkeleyChallengeTerm>
3255 for Expr<ConstantExpr<F, BerkeleyChallengeTerm>, berkeley_columns::Column>
3256 where
3257 F: PrimeField,
3258 Expr<ConstantExpr<F, BerkeleyChallengeTerm>, berkeley_columns::Column>: core::fmt::Display,
3260 {
3261 fn two_pow(pow: u64) -> Self {
3262 Expr::<ConstantExpr<F, BerkeleyChallengeTerm>, berkeley_columns::Column>::literal(
3263 <F as Two<F>>::two_pow(pow),
3264 )
3265 }
3266
3267 fn two_to_limb() -> Self {
3268 Expr::<ConstantExpr<F, BerkeleyChallengeTerm>, berkeley_columns::Column>::literal(
3269 KimchiForeignElement::<F>::two_to_limb(),
3270 )
3271 }
3272
3273 fn two_to_2limb() -> Self {
3274 Expr::<ConstantExpr<F, BerkeleyChallengeTerm>, berkeley_columns::Column>::literal(
3275 KimchiForeignElement::<F>::two_to_2limb(),
3276 )
3277 }
3278
3279 fn two_to_3limb() -> Self {
3280 Expr::<ConstantExpr<F, BerkeleyChallengeTerm>, berkeley_columns::Column>::literal(
3281 KimchiForeignElement::<F>::two_to_3limb(),
3282 )
3283 }
3284
3285 fn double(&self) -> Self {
3286 Expr::double(self.clone())
3287 }
3288
3289 fn square(&self) -> Self {
3290 Expr::square(self.clone())
3291 }
3292
3293 fn pow(&self, p: u64) -> Self {
3294 Expr::pow(self.clone(), p)
3295 }
3296
3297 fn boolean(&self) -> Self {
3298 constraints::boolean(self)
3299 }
3300
3301 fn crumb(&self) -> Self {
3302 constraints::crumb(self)
3303 }
3304
3305 fn literal(x: F) -> Self {
3306 ConstantTerm::Literal(x).into()
3307 }
3308
3309 fn witness(row: CurrOrNext, col: usize, _: Option<&ArgumentData<F>>) -> Self {
3310 witness(col, row)
3311 }
3312
3313 fn coeff(col: usize, _: Option<&ArgumentData<F>>) -> Self {
3314 coeff(col)
3315 }
3316
3317 fn constant(
3318 expr: ConstantExpr<F, BerkeleyChallengeTerm>,
3319 _: Option<&ArgumentData<F>>,
3320 ) -> Self {
3321 Expr::from(expr)
3322 }
3323
3324 fn cache(&self, cache: &mut Cache) -> Self {
3325 Expr::Cache(cache.next_id(), Box::new(self.clone()))
3326 }
3327 }
3328 impl<F: Field> ExprOps<F, BerkeleyChallengeTerm> for F {
3331 fn two_pow(pow: u64) -> Self {
3332 <F as Two<F>>::two_pow(pow)
3333 }
3334
3335 fn two_to_limb() -> Self {
3336 KimchiForeignElement::<F>::two_to_limb()
3337 }
3338
3339 fn two_to_2limb() -> Self {
3340 KimchiForeignElement::<F>::two_to_2limb()
3341 }
3342
3343 fn two_to_3limb() -> Self {
3344 KimchiForeignElement::<F>::two_to_3limb()
3345 }
3346
3347 fn double(&self) -> Self {
3348 *self * F::from(2u64)
3349 }
3350
3351 fn square(&self) -> Self {
3352 *self * *self
3353 }
3354
3355 fn pow(&self, p: u64) -> Self {
3356 self.pow([p])
3357 }
3358
3359 fn boolean(&self) -> Self {
3360 constraints::boolean(self)
3361 }
3362
3363 fn crumb(&self) -> Self {
3364 constraints::crumb(self)
3365 }
3366
3367 fn literal(x: F) -> Self {
3368 x
3369 }
3370
3371 fn witness(row: CurrOrNext, col: usize, env: Option<&ArgumentData<F>>) -> Self {
3372 match env {
3373 Some(data) => data.witness[(row, col)],
3374 None => panic!("Missing witness"),
3375 }
3376 }
3377
3378 fn coeff(col: usize, env: Option<&ArgumentData<F>>) -> Self {
3379 match env {
3380 Some(data) => data.coeffs[col],
3381 None => panic!("Missing coefficients"),
3382 }
3383 }
3384
3385 fn constant(
3386 expr: ConstantExpr<F, BerkeleyChallengeTerm>,
3387 env: Option<&ArgumentData<F>>,
3388 ) -> Self {
3389 match env {
3390 Some(data) => expr.value(&data.constants, &data.challenges),
3391 None => panic!("Missing constants"),
3392 }
3393 }
3394
3395 fn cache(&self, _: &mut Cache) -> Self {
3396 *self
3397 }
3398 }
3399
3400 pub fn boolean<F: Field, ChallengeTerm, T: ExprOps<F, ChallengeTerm>>(b: &T) -> T {
3402 b.square() - b.clone()
3403 }
3404
3405 pub fn crumb<F: Field, ChallengeTerm, T: ExprOps<F, ChallengeTerm>>(x: &T) -> T {
3407 x.clone()
3409 * (x.clone() - 1u64.into())
3410 * (x.clone() - 2u64.into())
3411 * (x.clone() - 3u64.into())
3412 }
3413
3414 pub fn compact_limb<F: Field, ChallengeTerm, T: ExprOps<F, ChallengeTerm>>(
3416 lo: &T,
3417 mi: &T,
3418 ) -> T {
3419 lo.clone() + mi.clone() * T::two_to_limb()
3420 }
3421}
3422
3423#[macro_export]
3426macro_rules! auto_clone {
3427 ($var:ident, $expr:expr) => {
3428 let $var = $expr;
3429 let $var = || $var.clone();
3430 };
3431 ($var:ident) => {
3432 let $var = || $var.clone();
3433 };
3434}
3435#[macro_export]
3436macro_rules! auto_clone_array {
3437 ($var:ident, $expr:expr) => {
3438 let $var = $expr;
3439 let $var = |i: usize| $var[i].clone();
3440 };
3441 ($var:ident) => {
3442 let $var = |i: usize| $var[i].clone();
3443 };
3444}
3445
3446pub use auto_clone;
3447pub use auto_clone_array;
3448
3449pub mod prologue {
3451 pub use super::{
3452 berkeley_columns::{coeff, constant, index, witness, witness_curr, witness_next, E},
3453 FeatureFlag,
3454 };
3455}