1use p3_field::{Algebra, ExtensionField, Field, InjectiveMonomial};
2use serde::{Deserialize, Serialize};
3
4use crate::symbolic::variable::{BaseEntry, SymbolicVariable};
5use crate::symbolic::{SymLeaf, SymbolicExpr};
6use crate::{AirBuilder, WindowAccess};
7
8#[derive(Clone, Debug, Serialize, Deserialize)]
13pub enum BaseLeaf<F> {
14 Variable(SymbolicVariable<F>),
16
17 IsFirstRow,
19
20 IsLastRow,
22
23 IsTransition,
25
26 Constant(F),
28}
29
30pub type SymbolicExpression<F> = SymbolicExpr<BaseLeaf<F>>;
35
36impl<F: Field> SymLeaf for BaseLeaf<F> {
37 type F = F;
38
39 const ZERO: Self = Self::Constant(F::ZERO);
40 const ONE: Self = Self::Constant(F::ONE);
41 const TWO: Self = Self::Constant(F::TWO);
42 const NEG_ONE: Self = Self::Constant(F::NEG_ONE);
43
44 fn degree_multiple(&self) -> usize {
45 match self {
46 Self::Variable(v) => v.degree_multiple(),
47 Self::IsFirstRow | Self::IsLastRow => 1,
48 Self::IsTransition | Self::Constant(_) => 0,
49 }
50 }
51
52 fn poly_degree(&self, trace_len: usize, periodic_periods: &[usize]) -> usize {
53 match self {
54 Self::Variable(v) => v.poly_degree(trace_len, periodic_periods),
55 Self::IsFirstRow | Self::IsLastRow => trace_len.saturating_sub(1),
59 Self::IsTransition => 1,
60 Self::Constant(_) => 0,
61 }
62 }
63
64 fn as_const(&self) -> Option<&F> {
65 match self {
66 Self::Constant(c) => Some(c),
67 _ => None,
68 }
69 }
70
71 fn from_const(c: F) -> Self {
72 Self::Constant(c)
73 }
74}
75
76impl<F: Field, EF: ExtensionField<F>> From<SymbolicVariable<F>> for SymbolicExpression<EF> {
77 fn from(var: SymbolicVariable<F>) -> Self {
78 Self::Leaf(BaseLeaf::Variable(SymbolicVariable::new(
79 var.entry, var.index,
80 )))
81 }
82}
83
84impl<F: Field, EF: ExtensionField<F>> From<F> for SymbolicExpression<EF> {
85 fn from(f: F) -> Self {
86 Self::Leaf(BaseLeaf::Constant(f.into()))
87 }
88}
89
90impl<F: Field> SymbolicExpression<F> {
91 pub fn resolve<AB>(&self, builder: &AB) -> AB::Expr
114 where
115 AB: AirBuilder<F = F>,
116 {
117 match self {
118 Self::Leaf(leaf) => match leaf {
119 BaseLeaf::Variable(v) => match v.entry {
120 BaseEntry::Main { offset } => {
123 let main = builder.main();
124 match offset {
125 0 => main
126 .current(v.index)
127 .expect("main column index out of bounds")
128 .into(),
129 1 => main
130 .next(v.index)
131 .expect("main column index out of bounds")
132 .into(),
133 _ => panic!("expressions cannot span more than two rows"),
134 }
135 }
136 BaseEntry::Preprocessed { offset } => {
138 let prep = builder.preprocessed();
139 match offset {
140 0 => prep
141 .current(v.index)
142 .expect("preprocessed column index out of bounds")
143 .into(),
144 1 => prep
145 .next(v.index)
146 .expect("preprocessed column index out of bounds")
147 .into(),
148 _ => panic!("expressions cannot span more than two rows"),
149 }
150 }
151 BaseEntry::Public => builder.public_values()[v.index].into(),
153 BaseEntry::Periodic => builder.periodic_values()[v.index].into(),
156 },
157 BaseLeaf::IsFirstRow => builder.is_first_row(),
159 BaseLeaf::IsLastRow => builder.is_last_row(),
160 BaseLeaf::IsTransition => builder.is_transition_window(2),
161 BaseLeaf::Constant(c) => AB::Expr::from(*c),
163 },
164 Self::Add { x, y, .. } => x.resolve(builder) + y.resolve(builder),
166 Self::Sub { x, y, .. } => x.resolve(builder) - y.resolve(builder),
167 Self::Neg { x, .. } => -x.resolve(builder),
168 Self::Mul { x, y, .. } => x.resolve(builder) * y.resolve(builder),
169 }
170 }
171}
172
173impl<F: Field> Algebra<F> for SymbolicExpression<F> {}
174
175impl<F: Field> Algebra<SymbolicVariable<F>> for SymbolicExpression<F> {}
176
177impl<F: Field + InjectiveMonomial<N>, const N: u64> InjectiveMonomial<N> for SymbolicExpression<F> {}
180
181#[cfg(test)]
182mod tests {
183 use alloc::sync::Arc;
184 use alloc::vec;
185 use alloc::vec::Vec;
186
187 use p3_baby_bear::BabyBear;
188 use p3_field::PrimeCharacteristicRing;
189 use p3_matrix::dense::RowMajorMatrix;
190
191 use super::*;
192 use crate::symbolic::BaseEntry;
193
194 #[test]
195 fn test_symbolic_expression_degree_multiple() {
196 let constant_expr =
197 SymbolicExpression::<BabyBear>::Leaf(BaseLeaf::Constant(BabyBear::new(5)));
198 assert_eq!(
199 constant_expr.degree_multiple(),
200 0,
201 "Constant should have degree 0"
202 );
203
204 let variable_expr = SymbolicExpression::Leaf(BaseLeaf::Variable(SymbolicVariable::new(
205 BaseEntry::Main { offset: 0 },
206 1,
207 )));
208 assert_eq!(
209 variable_expr.degree_multiple(),
210 1,
211 "Main variable should have degree 1"
212 );
213
214 let preprocessed_var = SymbolicExpression::Leaf(BaseLeaf::Variable(SymbolicVariable::new(
215 BaseEntry::Preprocessed { offset: 0 },
216 2,
217 )));
218 assert_eq!(
219 preprocessed_var.degree_multiple(),
220 1,
221 "Preprocessed variable should have degree 1"
222 );
223
224 let public_var = SymbolicExpression::Leaf(BaseLeaf::Variable(
225 SymbolicVariable::<BabyBear>::new(BaseEntry::Public, 4),
226 ));
227 assert_eq!(
228 public_var.degree_multiple(),
229 0,
230 "Public variable should have degree 0"
231 );
232
233 let is_first_row = SymbolicExpression::<BabyBear>::Leaf(BaseLeaf::IsFirstRow);
234 assert_eq!(
235 is_first_row.degree_multiple(),
236 1,
237 "IsFirstRow should have degree 1"
238 );
239
240 let is_last_row = SymbolicExpression::<BabyBear>::Leaf(BaseLeaf::IsLastRow);
241 assert_eq!(
242 is_last_row.degree_multiple(),
243 1,
244 "IsLastRow should have degree 1"
245 );
246
247 let is_transition = SymbolicExpression::<BabyBear>::Leaf(BaseLeaf::IsTransition);
248 assert_eq!(
249 is_transition.degree_multiple(),
250 0,
251 "IsTransition should have degree 0"
252 );
253
254 let add_expr = SymbolicExpr::<BaseLeaf<BabyBear>>::Add {
255 x: Arc::new(variable_expr.clone()),
256 y: Arc::new(preprocessed_var.clone()),
257 degree_multiple: 1,
258 };
259 assert_eq!(
260 add_expr.degree_multiple(),
261 1,
262 "Addition should take max degree of inputs"
263 );
264
265 let sub_expr = SymbolicExpr::<BaseLeaf<BabyBear>>::Sub {
266 x: Arc::new(variable_expr.clone()),
267 y: Arc::new(preprocessed_var.clone()),
268 degree_multiple: 1,
269 };
270 assert_eq!(
271 sub_expr.degree_multiple(),
272 1,
273 "Subtraction should take max degree of inputs"
274 );
275
276 let neg_expr = SymbolicExpr::<BaseLeaf<BabyBear>>::Neg {
277 x: Arc::new(variable_expr.clone()),
278 degree_multiple: 1,
279 };
280 assert_eq!(
281 neg_expr.degree_multiple(),
282 1,
283 "Negation should keep the degree"
284 );
285
286 let mul_expr = SymbolicExpr::<BaseLeaf<BabyBear>>::Mul {
287 x: Arc::new(variable_expr),
288 y: Arc::new(preprocessed_var),
289 degree_multiple: 2,
290 };
291 assert_eq!(
292 mul_expr.degree_multiple(),
293 2,
294 "Multiplication should sum degrees"
295 );
296 }
297
298 #[test]
299 fn test_symbolic_expression_poly_degree() {
300 const N: usize = 8;
301
302 let is_transition = SymbolicExpression::<BabyBear>::Leaf(BaseLeaf::IsTransition);
305 assert_eq!(is_transition.poly_degree(N, &[]), 1);
306
307 let is_first_row = SymbolicExpression::<BabyBear>::Leaf(BaseLeaf::IsFirstRow);
308 assert_eq!(is_first_row.poly_degree(N, &[]), N - 1);
309
310 let constant = SymbolicExpression::Leaf(BaseLeaf::Constant(BabyBear::new(5)));
312 assert_eq!(constant.poly_degree(N, &[]), 0);
313
314 let main = SymbolicExpression::<BabyBear>::from(SymbolicVariable::new(
316 BaseEntry::Main { offset: 0 },
317 0,
318 ));
319 let guarded = is_transition * main.clone();
320 assert_eq!(guarded.poly_degree(N, &[]), N);
321
322 let p0 =
326 SymbolicExpression::<BabyBear>::from(SymbolicVariable::new(BaseEntry::Periodic, 0));
327 let p1 =
328 SymbolicExpression::<BabyBear>::from(SymbolicVariable::new(BaseEntry::Periodic, 1));
329 let periodic_product = p0 * p1;
330 assert_eq!(periodic_product.poly_degree(N, &[2, 2]), N);
331
332 let sum = main + SymbolicExpression::Leaf(BaseLeaf::IsTransition);
334 assert_eq!(sum.poly_degree(N, &[]), N - 1);
335 }
336
337 #[test]
338 fn poly_degree_handles_shared_dag_in_linear_time() {
339 const DEPTH: usize = 30;
344 const N: usize = 1 << 10;
345
346 let mut expr =
347 SymbolicExpression::<BabyBear>::from(SymbolicVariable::new(BaseEntry::Periodic, 0));
348 for _ in 0..DEPTH {
349 expr = expr.clone() * expr.clone();
350 }
351
352 assert_eq!(expr.poly_degree(N, &[2]), (N / 2) << DEPTH);
354 }
355
356 #[test]
357 fn test_addition_of_constants() {
358 let a = SymbolicExpression::Leaf(BaseLeaf::Constant(BabyBear::new(3)));
359 let b = SymbolicExpression::Leaf(BaseLeaf::Constant(BabyBear::new(4)));
360 let result = a + b;
361 match result {
362 SymbolicExpr::Leaf(BaseLeaf::Constant(val)) => assert_eq!(val, BabyBear::new(7)),
363 _ => panic!("Addition of constants did not simplify correctly"),
364 }
365 }
366
367 #[test]
368 fn test_subtraction_of_constants() {
369 let a = SymbolicExpression::Leaf(BaseLeaf::Constant(BabyBear::new(10)));
370 let b = SymbolicExpression::Leaf(BaseLeaf::Constant(BabyBear::new(4)));
371 let result = a - b;
372 match result {
373 SymbolicExpr::Leaf(BaseLeaf::Constant(val)) => assert_eq!(val, BabyBear::new(6)),
374 _ => panic!("Subtraction of constants did not simplify correctly"),
375 }
376 }
377
378 #[test]
379 fn test_negation() {
380 let a = SymbolicExpression::Leaf(BaseLeaf::Constant(BabyBear::new(7)));
381 let result = -a;
382 match result {
383 SymbolicExpr::Leaf(BaseLeaf::Constant(val)) => {
384 assert_eq!(val, BabyBear::NEG_ONE * BabyBear::new(7));
385 }
386 _ => panic!("Negation did not work correctly"),
387 }
388 }
389
390 #[test]
391 fn test_multiplication_of_constants() {
392 let a = SymbolicExpression::Leaf(BaseLeaf::Constant(BabyBear::new(3)));
393 let b = SymbolicExpression::Leaf(BaseLeaf::Constant(BabyBear::new(5)));
394 let result = a * b;
395 match result {
396 SymbolicExpr::Leaf(BaseLeaf::Constant(val)) => assert_eq!(val, BabyBear::new(15)),
397 _ => panic!("Multiplication of constants did not simplify correctly"),
398 }
399 }
400
401 #[test]
402 fn test_degree_multiple_for_addition() {
403 let a = SymbolicExpression::Leaf(BaseLeaf::Variable(SymbolicVariable::<BabyBear>::new(
404 BaseEntry::Main { offset: 0 },
405 1,
406 )));
407 let b = SymbolicExpression::Leaf(BaseLeaf::Variable(SymbolicVariable::<BabyBear>::new(
408 BaseEntry::Main { offset: 0 },
409 2,
410 )));
411 let result = a + b;
412 match result {
413 SymbolicExpr::Add {
414 degree_multiple,
415 x,
416 y,
417 } => {
418 assert_eq!(degree_multiple, 1);
419 assert!(
420 matches!(&*x, SymbolicExpr::Leaf(BaseLeaf::Variable(v)) if v.index == 1 && matches!(v.entry, BaseEntry::Main { offset: 0 }))
421 );
422 assert!(
423 matches!(&*y, SymbolicExpr::Leaf(BaseLeaf::Variable(v)) if v.index == 2 && matches!(v.entry, BaseEntry::Main { offset: 0 }))
424 );
425 }
426 _ => panic!("Addition did not create an Add expression"),
427 }
428 }
429
430 #[test]
431 fn test_degree_multiple_for_multiplication() {
432 let a = SymbolicExpression::Leaf(BaseLeaf::Variable(SymbolicVariable::<BabyBear>::new(
433 BaseEntry::Main { offset: 0 },
434 1,
435 )));
436 let b = SymbolicExpression::Leaf(BaseLeaf::Variable(SymbolicVariable::<BabyBear>::new(
437 BaseEntry::Main { offset: 0 },
438 2,
439 )));
440 let result = a * b;
441
442 match result {
443 SymbolicExpr::Mul {
444 degree_multiple,
445 x,
446 y,
447 } => {
448 assert_eq!(degree_multiple, 2, "Multiplication should sum degrees");
449
450 assert!(
451 matches!(&*x, SymbolicExpr::Leaf(BaseLeaf::Variable(v))
452 if v.index == 1 && matches!(v.entry, BaseEntry::Main { offset: 0 })
453 ),
454 "Left operand should match `a`"
455 );
456
457 assert!(
458 matches!(&*y, SymbolicExpr::Leaf(BaseLeaf::Variable(v))
459 if v.index == 2 && matches!(v.entry, BaseEntry::Main { offset: 0 })
460 ),
461 "Right operand should match `b`"
462 );
463 }
464 _ => panic!("Multiplication did not create a `Mul` expression"),
465 }
466 }
467
468 #[test]
469 fn test_sum_operator() {
470 let expressions = vec![
471 SymbolicExpression::Leaf(BaseLeaf::Constant(BabyBear::new(2))),
472 SymbolicExpression::Leaf(BaseLeaf::Constant(BabyBear::new(3))),
473 SymbolicExpression::Leaf(BaseLeaf::Constant(BabyBear::new(5))),
474 ];
475 let result: SymbolicExpression<BabyBear> = expressions.into_iter().sum();
476 match result {
477 SymbolicExpr::Leaf(BaseLeaf::Constant(val)) => assert_eq!(val, BabyBear::new(10)),
478 _ => panic!("Sum did not produce correct result"),
479 }
480 }
481
482 #[test]
483 fn test_product_operator() {
484 let expressions = vec![
485 SymbolicExpression::Leaf(BaseLeaf::Constant(BabyBear::new(2))),
486 SymbolicExpression::Leaf(BaseLeaf::Constant(BabyBear::new(3))),
487 SymbolicExpression::Leaf(BaseLeaf::Constant(BabyBear::new(4))),
488 ];
489 let result: SymbolicExpression<BabyBear> = expressions.into_iter().product();
490 match result {
491 SymbolicExpr::Leaf(BaseLeaf::Constant(val)) => assert_eq!(val, BabyBear::new(24)),
492 _ => panic!("Product did not produce correct result"),
493 }
494 }
495
496 #[test]
497 fn test_default_is_zero() {
498 let expr: SymbolicExpression<BabyBear> = Default::default();
500
501 assert!(matches!(
503 expr,
504 SymbolicExpr::Leaf(BaseLeaf::Constant(c)) if c == BabyBear::ZERO
505 ));
506 }
507
508 #[test]
509 fn test_ring_constants() {
510 assert!(matches!(
512 SymbolicExpression::<BabyBear>::ZERO,
513 SymbolicExpr::Leaf(BaseLeaf::Constant(c)) if c == BabyBear::ZERO
514 ));
515 assert!(matches!(
517 SymbolicExpression::<BabyBear>::ONE,
518 SymbolicExpr::Leaf(BaseLeaf::Constant(c)) if c == BabyBear::ONE
519 ));
520 assert!(matches!(
522 SymbolicExpression::<BabyBear>::TWO,
523 SymbolicExpr::Leaf(BaseLeaf::Constant(c)) if c == BabyBear::TWO
524 ));
525 assert!(matches!(
527 SymbolicExpression::<BabyBear>::NEG_ONE,
528 SymbolicExpr::Leaf(BaseLeaf::Constant(c)) if c == BabyBear::NEG_ONE
529 ));
530 }
531
532 #[test]
533 fn test_from_symbolic_variable() {
534 let var = SymbolicVariable::<BabyBear>::new(BaseEntry::Main { offset: 0 }, 3);
536 let expr: SymbolicExpression<BabyBear> = var.into();
538 match expr {
540 SymbolicExpr::Leaf(BaseLeaf::Variable(v)) => {
541 assert!(matches!(v.entry, BaseEntry::Main { offset: 0 }));
542 assert_eq!(v.index, 3);
543 }
544 _ => panic!("Expected Variable variant"),
545 }
546 }
547
548 #[test]
549 fn test_from_field_element() {
550 let field_val = BabyBear::new(42);
552 let expr: SymbolicExpression<BabyBear> = field_val.into();
553 assert!(matches!(
555 expr,
556 SymbolicExpr::Leaf(BaseLeaf::Constant(c)) if c == field_val
557 ));
558 }
559
560 #[test]
561 fn test_from_prime_subfield() {
562 let prime_subfield_val = <BabyBear as PrimeCharacteristicRing>::PrimeSubfield::new(7);
564 let expr = SymbolicExpression::<BabyBear>::from_prime_subfield(prime_subfield_val);
565 assert!(matches!(
567 expr,
568 SymbolicExpr::Leaf(BaseLeaf::Constant(c)) if c == BabyBear::new(7)
569 ));
570 }
571
572 #[test]
573 fn test_assign_operators() {
574 let mut expr = SymbolicExpression::Leaf(BaseLeaf::Constant(BabyBear::new(5)));
576 expr += SymbolicExpression::Leaf(BaseLeaf::Constant(BabyBear::new(3)));
577 assert!(matches!(
578 expr,
579 SymbolicExpr::Leaf(BaseLeaf::Constant(c)) if c == BabyBear::new(8)
580 ));
581
582 let mut expr = SymbolicExpression::Leaf(BaseLeaf::Constant(BabyBear::new(10)));
584 expr -= SymbolicExpression::Leaf(BaseLeaf::Constant(BabyBear::new(4)));
585 assert!(matches!(
586 expr,
587 SymbolicExpr::Leaf(BaseLeaf::Constant(c)) if c == BabyBear::new(6)
588 ));
589
590 let mut expr = SymbolicExpression::Leaf(BaseLeaf::Constant(BabyBear::new(6)));
592 expr *= SymbolicExpression::Leaf(BaseLeaf::Constant(BabyBear::new(7)));
593 assert!(matches!(
594 expr,
595 SymbolicExpr::Leaf(BaseLeaf::Constant(c)) if c == BabyBear::new(42)
596 ));
597 }
598
599 #[test]
600 fn test_subtraction_creates_sub_node() {
601 let a = SymbolicExpression::Leaf(BaseLeaf::Variable(SymbolicVariable::<BabyBear>::new(
603 BaseEntry::Main { offset: 0 },
604 0,
605 )));
606 let b = SymbolicExpression::Leaf(BaseLeaf::Variable(SymbolicVariable::<BabyBear>::new(
607 BaseEntry::Main { offset: 0 },
608 1,
609 )));
610
611 let result = a - b;
613
614 match result {
616 SymbolicExpr::Sub {
617 x,
618 y,
619 degree_multiple,
620 } => {
621 assert_eq!(degree_multiple, 1);
623
624 assert!(matches!(
626 x.as_ref(),
627 SymbolicExpr::Leaf(BaseLeaf::Variable(v))
628 if v.index == 0 && matches!(v.entry, BaseEntry::Main { offset: 0 })
629 ));
630
631 assert!(matches!(
633 y.as_ref(),
634 SymbolicExpr::Leaf(BaseLeaf::Variable(v))
635 if v.index == 1 && matches!(v.entry, BaseEntry::Main { offset: 0 })
636 ));
637 }
638 _ => panic!("Expected Sub variant"),
639 }
640 }
641
642 #[test]
643 fn test_negation_creates_neg_node() {
644 let var = SymbolicExpression::Leaf(BaseLeaf::Variable(SymbolicVariable::<BabyBear>::new(
646 BaseEntry::Main { offset: 0 },
647 0,
648 )));
649
650 let result = -var;
652
653 match result {
655 SymbolicExpr::Neg { x, degree_multiple } => {
656 assert_eq!(degree_multiple, 1);
658
659 assert!(matches!(
661 x.as_ref(),
662 SymbolicExpr::Leaf(BaseLeaf::Variable(v))
663 if v.index == 0 && matches!(v.entry, BaseEntry::Main { offset: 0 })
664 ));
665 }
666 _ => panic!("Expected Neg variant"),
667 }
668 }
669
670 #[test]
671 fn test_empty_sum_returns_zero() {
672 let empty: Vec<SymbolicExpression<BabyBear>> = vec![];
674 let result: SymbolicExpression<BabyBear> = empty.into_iter().sum();
675 assert!(matches!(
676 result,
677 SymbolicExpr::Leaf(BaseLeaf::Constant(c)) if c == BabyBear::ZERO
678 ));
679 }
680
681 #[test]
682 fn test_empty_product_returns_one() {
683 let empty: Vec<SymbolicExpression<BabyBear>> = vec![];
685 let result: SymbolicExpression<BabyBear> = empty.into_iter().product();
686 assert!(matches!(
687 result,
688 SymbolicExpr::Leaf(BaseLeaf::Constant(c)) if c == BabyBear::ONE
689 ));
690 }
691
692 #[test]
693 fn test_mixed_degree_addition() {
694 let constant = SymbolicExpression::Leaf(BaseLeaf::Constant(BabyBear::new(5)));
696
697 let var = SymbolicExpression::Leaf(BaseLeaf::Variable(SymbolicVariable::<BabyBear>::new(
699 BaseEntry::Main { offset: 0 },
700 0,
701 )));
702
703 let result = constant + var;
705
706 match result {
707 SymbolicExpr::Add {
708 x,
709 y,
710 degree_multiple,
711 } => {
712 assert_eq!(degree_multiple, 1);
714
715 assert!(matches!(
717 x.as_ref(),
718 SymbolicExpr::Leaf(BaseLeaf::Constant(c)) if *c == BabyBear::new(5)
719 ));
720
721 assert!(matches!(
723 y.as_ref(),
724 SymbolicExpr::Leaf(BaseLeaf::Variable(v))
725 if v.index == 0 && matches!(v.entry, BaseEntry::Main { offset: 0 })
726 ));
727 }
728 _ => panic!("Expected Add variant"),
729 }
730 }
731
732 #[test]
733 fn test_chained_multiplication_degree() {
734 let a = SymbolicExpression::Leaf(BaseLeaf::Variable(SymbolicVariable::<BabyBear>::new(
736 BaseEntry::Main { offset: 0 },
737 0,
738 )));
739 let b = SymbolicExpression::Leaf(BaseLeaf::Variable(SymbolicVariable::<BabyBear>::new(
740 BaseEntry::Main { offset: 0 },
741 1,
742 )));
743 let c = SymbolicExpression::Leaf(BaseLeaf::Variable(SymbolicVariable::<BabyBear>::new(
744 BaseEntry::Main { offset: 0 },
745 2,
746 )));
747
748 let ab = a * b;
750 assert_eq!(ab.degree_multiple(), 2);
751
752 let abc = ab * c;
754 assert_eq!(abc.degree_multiple(), 3);
755 }
756
757 #[test]
758 fn test_add_zero_identity_folding() {
759 let var = SymbolicExpression::Leaf(BaseLeaf::Variable(SymbolicVariable::<BabyBear>::new(
760 BaseEntry::Main { offset: 0 },
761 0,
762 )));
763 let zero = SymbolicExpression::<BabyBear>::Leaf(BaseLeaf::Constant(BabyBear::ZERO));
764
765 let result = var.clone() + zero.clone();
767 assert!(
768 matches!(result, SymbolicExpr::Leaf(BaseLeaf::Variable(_))),
769 "x + 0 should fold to x"
770 );
771
772 let result = zero + var;
774 assert!(
775 matches!(result, SymbolicExpr::Leaf(BaseLeaf::Variable(_))),
776 "0 + x should fold to x"
777 );
778 }
779
780 #[test]
781 fn test_sub_zero_identity_folding() {
782 let var = SymbolicExpression::Leaf(BaseLeaf::Variable(SymbolicVariable::<BabyBear>::new(
783 BaseEntry::Main { offset: 0 },
784 0,
785 )));
786 let zero = SymbolicExpression::<BabyBear>::Leaf(BaseLeaf::Constant(BabyBear::ZERO));
787
788 let result = var.clone() - zero.clone();
790 assert!(
791 matches!(result, SymbolicExpr::Leaf(BaseLeaf::Variable(_))),
792 "x - 0 should fold to x"
793 );
794
795 let result = zero - var;
797 match result {
798 SymbolicExpr::Neg { x, degree_multiple } => {
799 assert_eq!(degree_multiple, 1);
800 assert!(matches!(
801 x.as_ref(),
802 SymbolicExpr::Leaf(BaseLeaf::Variable(v))
803 if v.index == 0 && v.entry == BaseEntry::Main { offset: 0 }
804 ));
805 }
806 _ => panic!("0 - x should fold to Neg(x)"),
807 }
808 }
809
810 #[test]
811 fn test_mul_zero_identity_folding() {
812 let var = SymbolicExpression::Leaf(BaseLeaf::Variable(SymbolicVariable::<BabyBear>::new(
813 BaseEntry::Main { offset: 0 },
814 0,
815 )));
816 let zero = SymbolicExpression::<BabyBear>::Leaf(BaseLeaf::Constant(BabyBear::ZERO));
817
818 let result = var.clone() * zero.clone();
820 assert!(
821 matches!(result, SymbolicExpr::Leaf(BaseLeaf::Constant(c)) if c == BabyBear::ZERO),
822 "x * 0 should fold to 0"
823 );
824
825 let result = zero * var;
827 assert!(
828 matches!(result, SymbolicExpr::Leaf(BaseLeaf::Constant(c)) if c == BabyBear::ZERO),
829 "0 * x should fold to 0"
830 );
831 }
832
833 #[test]
834 fn test_mul_one_identity_folding() {
835 let var = SymbolicExpression::Leaf(BaseLeaf::Variable(SymbolicVariable::<BabyBear>::new(
836 BaseEntry::Main { offset: 0 },
837 0,
838 )));
839 let one = SymbolicExpression::<BabyBear>::Leaf(BaseLeaf::Constant(BabyBear::ONE));
840
841 let result = var.clone() * one.clone();
843 assert!(
844 matches!(result, SymbolicExpr::Leaf(BaseLeaf::Variable(_))),
845 "x * 1 should fold to x"
846 );
847
848 let result = one * var;
850 assert!(
851 matches!(result, SymbolicExpr::Leaf(BaseLeaf::Variable(_))),
852 "1 * x should fold to x"
853 );
854 }
855
856 #[test]
857 fn test_identity_folding_preserves_degree() {
858 let var = SymbolicExpression::Leaf(BaseLeaf::Variable(SymbolicVariable::<BabyBear>::new(
859 BaseEntry::Main { offset: 0 },
860 0,
861 )));
862 let zero = SymbolicExpression::<BabyBear>::Leaf(BaseLeaf::Constant(BabyBear::ZERO));
863 let one = SymbolicExpression::<BabyBear>::Leaf(BaseLeaf::Constant(BabyBear::ONE));
864
865 let result = var.clone() + zero.clone();
867 assert_eq!(result.degree_multiple(), 1);
868
869 let result = var.clone() - zero.clone();
871 assert_eq!(result.degree_multiple(), 1);
872
873 let result = zero.clone() - var.clone();
875 assert_eq!(result.degree_multiple(), 1);
876
877 let result = var.clone() * one;
879 assert_eq!(result.degree_multiple(), 1);
880
881 let result = var * zero;
883 assert_eq!(result.degree_multiple(), 0);
884 }
885
886 struct ResolveTestBuilder {
894 main: RowMajorMatrix<BabyBear>,
895 public_values: Vec<BabyBear>,
896 periodic_row: Vec<BabyBear>,
897 is_first: BabyBear,
898 is_last: BabyBear,
899 is_transition: BabyBear,
900 }
901
902 impl AirBuilder for ResolveTestBuilder {
903 type F = BabyBear;
904 type Expr = BabyBear;
905 type Var = BabyBear;
906 type PreprocessedWindow = RowMajorMatrix<BabyBear>;
907 type MainWindow = RowMajorMatrix<BabyBear>;
908 type PublicVar = BabyBear;
909 type PeriodicVar = BabyBear;
910
911 fn main(&self) -> Self::MainWindow {
912 self.main.clone()
913 }
914
915 fn preprocessed(&self) -> &Self::PreprocessedWindow {
916 unimplemented!("no preprocessed columns in test builder")
917 }
918
919 fn is_first_row(&self) -> Self::Expr {
920 self.is_first
921 }
922
923 fn is_last_row(&self) -> Self::Expr {
924 self.is_last
925 }
926
927 fn is_transition(&self) -> Self::Expr {
928 self.is_transition
929 }
930
931 fn assert_zero<I: Into<Self::Expr>>(&mut self, _: I) {}
932
933 fn public_values(&self) -> &[Self::PublicVar] {
934 &self.public_values
935 }
936
937 fn periodic_values(&self) -> &[Self::PeriodicVar] {
938 &self.periodic_row
939 }
940 }
941
942 fn test_builder() -> ResolveTestBuilder {
950 ResolveTestBuilder {
951 main: RowMajorMatrix::new(
952 vec![
953 BabyBear::new(10),
954 BabyBear::new(20), BabyBear::new(30),
956 BabyBear::new(40), ],
958 2, ),
960 public_values: vec![BabyBear::new(99)],
961 periodic_row: vec![BabyBear::new(7), BabyBear::new(13)],
964 is_first: BabyBear::ONE,
965 is_last: BabyBear::ZERO,
966 is_transition: BabyBear::ONE,
967 }
968 }
969
970 #[test]
971 fn resolve_main_current_row() {
972 let b = test_builder();
973 let expr =
975 SymbolicExpression::from(SymbolicVariable::new(BaseEntry::Main { offset: 0 }, 0));
976 assert_eq!(expr.resolve(&b), BabyBear::new(10));
977 }
978
979 #[test]
980 fn resolve_main_next_row() {
981 let b = test_builder();
982 let expr =
984 SymbolicExpression::from(SymbolicVariable::new(BaseEntry::Main { offset: 1 }, 1));
985 assert_eq!(expr.resolve(&b), BabyBear::new(40));
986 }
987
988 #[test]
989 fn resolve_public_value() {
990 let b = test_builder();
991 let expr = SymbolicExpression::from(SymbolicVariable::new(BaseEntry::Public, 0));
993 assert_eq!(expr.resolve(&b), BabyBear::new(99));
994 }
995
996 #[test]
997 fn resolve_constant() {
998 let b = test_builder();
999 let expr = SymbolicExpression::<BabyBear>::from(BabyBear::new(42));
1000 assert_eq!(expr.resolve(&b), BabyBear::new(42));
1001 }
1002
1003 #[test]
1004 fn resolve_selectors() {
1005 let b = test_builder();
1006
1007 let first = SymbolicExpression::<BabyBear>::Leaf(BaseLeaf::IsFirstRow);
1008 assert_eq!(first.resolve(&b), BabyBear::ONE, "is_first_row = 1");
1009
1010 let last = SymbolicExpression::<BabyBear>::Leaf(BaseLeaf::IsLastRow);
1011 assert_eq!(last.resolve(&b), BabyBear::ZERO, "is_last_row = 0");
1012
1013 let trans = SymbolicExpression::<BabyBear>::Leaf(BaseLeaf::IsTransition);
1014 assert_eq!(trans.resolve(&b), BabyBear::ONE, "is_transition = 1");
1015 }
1016
1017 #[test]
1018 fn resolve_arithmetic() {
1019 let b = test_builder();
1020
1021 let col0 =
1023 SymbolicExpression::from(SymbolicVariable::new(BaseEntry::Main { offset: 0 }, 0));
1024 let col1 =
1025 SymbolicExpression::from(SymbolicVariable::new(BaseEntry::Main { offset: 0 }, 1));
1026
1027 let add = col0.clone() + col1.clone();
1029 assert_eq!(add.resolve(&b), BabyBear::new(30));
1030
1031 let sub = col0.clone() - col1.clone();
1033 assert_eq!(sub.resolve(&b), BabyBear::new(10) - BabyBear::new(20));
1034
1035 let mul = col0.clone() * col1;
1037 assert_eq!(mul.resolve(&b), BabyBear::new(200));
1038
1039 let neg = -col0;
1041 assert_eq!(neg.resolve(&b), -BabyBear::new(10));
1042 }
1043
1044 #[test]
1045 fn resolve_periodic_columns() {
1046 let b = test_builder();
1055
1056 let p0 =
1058 SymbolicExpression::from(SymbolicVariable::<BabyBear>::new(BaseEntry::Periodic, 0));
1059 assert_eq!(p0.resolve(&b), BabyBear::new(7));
1060
1061 let p1 =
1063 SymbolicExpression::from(SymbolicVariable::<BabyBear>::new(BaseEntry::Periodic, 1));
1064 assert_eq!(p1.resolve(&b), BabyBear::new(13));
1065 }
1066
1067 #[test]
1068 fn resolve_periodic_combines_with_arithmetic() {
1069 let b = test_builder();
1081
1082 let col0 =
1083 SymbolicExpression::from(SymbolicVariable::new(BaseEntry::Main { offset: 0 }, 0));
1084 let p0 =
1085 SymbolicExpression::from(SymbolicVariable::<BabyBear>::new(BaseEntry::Periodic, 0));
1086 let p1 =
1087 SymbolicExpression::from(SymbolicVariable::<BabyBear>::new(BaseEntry::Periodic, 1));
1088
1089 let expr = col0 * p0 + p1;
1090 assert_eq!(expr.resolve(&b), BabyBear::new(83));
1091 }
1092
1093 #[test]
1094 fn serde_round_trip_preserves_resolution() {
1095 let b = test_builder();
1098 let main_cur =
1099 SymbolicExpression::from(SymbolicVariable::new(BaseEntry::Main { offset: 0 }, 0));
1100 let main_next =
1101 SymbolicExpression::from(SymbolicVariable::new(BaseEntry::Main { offset: 1 }, 1));
1102 let public =
1103 SymbolicExpression::from(SymbolicVariable::<BabyBear>::new(BaseEntry::Public, 0));
1104 let periodic =
1105 SymbolicExpression::from(SymbolicVariable::<BabyBear>::new(BaseEntry::Periodic, 0));
1106 let transition = SymbolicExpression::<BabyBear>::Leaf(BaseLeaf::IsTransition);
1107
1108 let expr = main_cur * main_next - public + periodic * transition
1109 - SymbolicExpression::from(BabyBear::new(5));
1110
1111 let json = serde_json::to_string(&expr).unwrap();
1112 let decoded: SymbolicExpression<BabyBear> = serde_json::from_str(&json).unwrap();
1113
1114 assert_eq!(decoded.resolve(&b), expr.resolve(&b));
1116 assert_eq!(serde_json::to_string(&decoded).unwrap(), json);
1118 assert_eq!(decoded.degree_multiple(), expr.degree_multiple());
1119 }
1120}