1use std::{cell::RefCell, cmp::min, iter, ops::Deref, rc::Rc};
2
3use itertools::{zip_eq, Itertools};
4use num_bigint::{BigInt, BigUint, Sign};
5use num_traits::{One, Zero};
6use openvm_circuit_primitives::{
7 bigint::{
8 check_carry_mod_to_zero::{CheckCarryModToZeroCols, CheckCarryModToZeroSubAir},
9 check_carry_to_zero::get_carry_max_abs_and_bits,
10 utils::*,
11 OverflowInt,
12 },
13 var_range::{VariableRangeCheckerBus, VariableRangeCheckerChip},
14 ColumnsAir, SubAir, TraceSubRowGenerator,
15};
16use openvm_stark_backend::{
17 interaction::InteractionBuilder,
18 p3_air::{Air, AirBuilder, BaseAir},
19 p3_field::{Field, PrimeCharacteristicRing, PrimeField64},
20 p3_matrix::Matrix,
21 BaseAirWithPublicValues, PartitionedBaseAir,
22};
23
24use super::{FieldVariable, SymbolicExpr};
25
26#[derive(Clone)]
27pub struct ExprBuilderConfig {
28 pub modulus: BigUint,
29 pub num_limbs: usize,
30 pub limb_bits: usize,
31}
32
33impl ExprBuilderConfig {
34 pub fn check_valid(&self) {
35 assert!(self.modulus.bits() <= (self.num_limbs * self.limb_bits) as u64);
36 }
37}
38
39#[derive(Clone)]
40pub struct ExprBuilder {
41 pub prime: BigUint,
43 pub prime_bigint: BigInt,
45 pub prime_limbs: Vec<usize>,
46
47 pub num_input: usize,
48 pub num_flags: usize,
49
50 pub num_variables: usize,
52
53 pub constants: Vec<(BigUint, Vec<usize>)>, pub limb_bits: usize,
57 pub num_limbs: usize,
59 proper_max: BigUint,
60 pub range_checker_bits: usize,
62 pub max_carry_bits: usize,
64
65 pub q_limbs: Vec<usize>,
67 pub carry_limbs: Vec<usize>,
69
70 pub constraints: Vec<SymbolicExpr>,
72
73 pub computes: Vec<SymbolicExpr>,
75
76 pub output_indices: Vec<usize>,
77
78 debug: bool,
80
81 finalized: bool,
84
85 needs_setup: bool,
91}
92
93const MODULUS_BITS: usize = 31;
95
96impl ExprBuilder {
97 pub fn new(config: ExprBuilderConfig, range_checker_bits: usize) -> Self {
98 let prime_bigint = BigInt::from_biguint(Sign::Plus, config.modulus.clone());
99 let proper_max = (BigUint::one() << (config.num_limbs * config.limb_bits)) - BigUint::one();
100 let max_carry_bits = MODULUS_BITS - config.limb_bits - 2;
102 assert!(config.limb_bits + 2 < MODULUS_BITS);
104 Self {
105 prime: config.modulus.clone(),
106 prime_bigint,
107 prime_limbs: big_uint_to_limbs(&config.modulus, config.limb_bits),
108 num_input: 0,
109 num_flags: 0,
110 limb_bits: config.limb_bits,
111 num_limbs: config.num_limbs,
112 proper_max,
113 range_checker_bits,
114 max_carry_bits: min(max_carry_bits, range_checker_bits),
115 num_variables: 0,
116 constants: vec![],
117 q_limbs: vec![],
118 carry_limbs: vec![],
119 constraints: vec![],
120 computes: vec![],
121 output_indices: vec![],
122 debug: false,
123 finalized: false,
124 needs_setup: false,
125 }
126 }
127
128 pub fn set_debug(&mut self) {
130 self.debug = true;
131 }
132
133 #[allow(unused)]
134 fn debug_print(&self, msg: &str) {
135 if self.debug {
136 println!("{msg}");
137 }
138 }
139
140 pub fn is_finalized(&self) -> bool {
141 self.finalized
142 }
143
144 pub fn finalize(&mut self, needs_setup: bool) {
145 self.finalized = true;
146 self.needs_setup = needs_setup;
147
148 assert!(needs_setup || self.num_flags == 0);
150
151 if needs_setup && self.num_flags == 0 {
153 self.new_flag();
154 }
155 }
156
157 pub fn new_input(builder: Rc<RefCell<ExprBuilder>>) -> FieldVariable {
158 let mut borrowed = builder.borrow_mut();
159 let num_limbs = borrowed.num_limbs;
160 let limb_bits = borrowed.limb_bits;
161 borrowed.num_input += 1;
162 let (num_input, max_carry_bits) = (borrowed.num_input, borrowed.max_carry_bits);
163 drop(borrowed);
164 FieldVariable {
165 expr: SymbolicExpr::Input(num_input - 1),
166 builder: builder.clone(),
167 limb_max_abs: (1 << limb_bits) - 1,
168 max_overflow_bits: limb_bits,
169 expr_limbs: num_limbs,
170 max_carry_bits,
171 }
172 }
173
174 pub fn new_flag(&mut self) -> usize {
175 self.num_flags += 1;
176 self.num_flags - 1
177 }
178
179 pub fn needs_setup(&self) -> bool {
180 assert!(self.finalized); self.needs_setup
182 }
183
184 pub fn new_var(&mut self) -> (usize, SymbolicExpr) {
188 self.num_variables += 1;
189 self.constraints.push(SymbolicExpr::Input(0));
192 self.computes.push(SymbolicExpr::Input(0));
193 self.q_limbs.push(0);
194 self.carry_limbs.push(0);
195 (
196 self.num_variables - 1,
197 SymbolicExpr::Var(self.num_variables - 1),
198 )
199 }
200
201 pub fn new_const(builder: Rc<RefCell<ExprBuilder>>, value: BigUint) -> FieldVariable {
205 let mut borrowed = builder.borrow_mut();
206 let index = borrowed.constants.len();
207 let limb_bits = borrowed.limb_bits;
208 let num_limbs = borrowed.num_limbs;
209 let limbs = big_uint_to_num_limbs(&value, limb_bits, num_limbs);
210 let max_carry_bits = borrowed.max_carry_bits;
211 borrowed.constants.push((value.clone(), limbs));
212 drop(borrowed);
213
214 FieldVariable {
215 expr: SymbolicExpr::Const(index, value, num_limbs),
216 builder,
217 limb_max_abs: (1 << limb_bits) - 1,
218 max_overflow_bits: limb_bits,
219 expr_limbs: num_limbs,
220 max_carry_bits,
221 }
222 }
223
224 pub fn set_constraint(&mut self, index: usize, constraint: SymbolicExpr) {
225 let (q_limbs, carry_limbs) = constraint.constraint_limbs(
226 &self.prime,
227 self.limb_bits,
228 self.num_limbs,
229 &self.proper_max,
230 );
231 self.constraints[index] = constraint;
232 self.q_limbs[index] = q_limbs;
233 self.carry_limbs[index] = carry_limbs;
234 }
235
236 pub fn set_compute(&mut self, index: usize, compute: SymbolicExpr) {
237 self.computes[index] = compute;
238 }
239
240 pub fn proper_max(&self) -> &BigUint {
244 &self.proper_max
245 }
246}
247
248#[derive(Clone)]
249pub struct FieldExpr {
250 pub builder: ExprBuilder,
251
252 pub check_carry_mod_to_zero: CheckCarryModToZeroSubAir,
253
254 pub range_bus: VariableRangeCheckerBus,
255
256 pub setup_values: Vec<BigUint>,
258}
259
260impl FieldExpr {
261 pub fn new(
262 builder: ExprBuilder,
263 range_bus: VariableRangeCheckerBus,
264 needs_setup: bool,
265 ) -> Self {
266 let mut builder = builder;
267 builder.finalize(needs_setup);
268 let subair = CheckCarryModToZeroSubAir::new(
269 builder.prime.clone(),
270 builder.limb_bits,
271 range_bus.inner.index,
272 range_bus.range_max_bits,
273 );
274 FieldExpr {
275 builder,
276 check_carry_mod_to_zero: subair,
277 range_bus,
278 setup_values: vec![],
279 }
280 }
281
282 pub fn new_with_setup_values(
283 builder: ExprBuilder,
284 range_bus: VariableRangeCheckerBus,
285 needs_setup: bool,
286 setup_values: Vec<BigUint>,
287 ) -> Self {
288 let mut ret = Self::new(builder, range_bus, needs_setup);
289 ret.setup_values = setup_values;
290 ret
291 }
292
293 pub fn num_inputs(&self) -> usize {
294 self.builder.num_input
295 }
296
297 pub fn num_vars(&self) -> usize {
298 self.builder.num_variables
299 }
300
301 pub fn num_flags(&self) -> usize {
302 self.builder.num_flags
303 }
304
305 pub fn output_indices(&self) -> &[usize] {
306 &self.builder.output_indices
307 }
308}
309
310impl Deref for FieldExpr {
311 type Target = ExprBuilder;
312
313 fn deref(&self) -> &ExprBuilder {
314 &self.builder
315 }
316}
317
318impl<F: Field> BaseAirWithPublicValues<F> for FieldExpr {}
319impl<F: Field> PartitionedBaseAir<F> for FieldExpr {}
320impl ColumnsAir for FieldExpr {}
323impl<F: Field> BaseAir<F> for FieldExpr {
324 fn width(&self) -> usize {
325 assert!(self.builder.is_finalized());
326 self.num_limbs * (self.builder.num_input + self.builder.num_variables)
327 + self.builder.q_limbs.iter().sum::<usize>()
328 + self.builder.carry_limbs.iter().sum::<usize>()
329 + self.builder.num_flags
330 + 1 }
332}
333
334impl<AB: InteractionBuilder> Air<AB> for FieldExpr {
335 fn eval(&self, builder: &mut AB) {
336 let main = builder.main();
337 let local = main.row_slice(0).expect("window should have two elements");
338 SubAir::eval(self, builder, &local);
339 }
340}
341
342impl<AB: InteractionBuilder> SubAir<AB> for FieldExpr {
343 type AirContext<'a>
345 = &'a [AB::Var]
346 where
347 AB: 'a,
348 AB::Var: 'a,
349 AB::Expr: 'a;
350
351 fn eval<'a>(&'a self, builder: &'a mut AB, local: &'a [AB::Var])
352 where
353 AB::Var: 'a,
354 AB::Expr: 'a,
355 {
356 assert!(self.builder.is_finalized());
357 let FieldExprCols {
358 is_valid,
359 inputs,
360 vars,
361 q_limbs,
362 carry_limbs,
363 flags,
364 } = self.load_vars(local);
365
366 builder.assert_bool(is_valid);
367
368 if self.builder.needs_setup() {
369 let is_setup = flags.iter().fold(is_valid.into(), |acc, &x| acc - x);
370 builder.assert_bool(is_setup.clone());
371 let expected = iter::empty()
378 .chain({
379 let mut prime_limbs = self.builder.prime_limbs.clone();
380 prime_limbs.resize(self.builder.num_limbs, 0);
381 prime_limbs
382 })
383 .chain(self.setup_values.iter().flat_map(|x| {
384 big_uint_to_num_limbs(x, self.builder.limb_bits, self.builder.num_limbs)
385 .into_iter()
386 }))
387 .collect_vec();
388
389 let reads: Vec<AB::Expr> = inputs
390 .clone()
391 .into_iter()
392 .flatten()
393 .map(Into::into)
394 .take(expected.len())
395 .collect();
396
397 for (lhs, rhs) in zip_eq(&reads, expected) {
398 builder
399 .when(is_setup.clone())
400 .assert_eq(lhs.clone(), AB::F::from_usize(rhs));
401 }
402 }
403
404 let inputs = load_overflow::<AB>(inputs, self.limb_bits);
405 let vars = load_overflow::<AB>(vars, self.limb_bits);
406 let constants: Vec<_> = self
407 .constants
408 .iter()
409 .map(|(_, limbs)| {
410 let limbs_expr: Vec<_> = limbs
411 .iter()
412 .map(|limb| AB::Expr::from_usize(*limb))
413 .collect();
414 OverflowInt::from_unsigned_limbs(limbs_expr, self.limb_bits)
415 })
416 .collect();
417
418 for flag in flags.iter() {
419 builder.assert_bool(*flag);
420 }
421 for i in 0..self.constraints.len() {
422 let expr = self.constraints[i]
423 .evaluate_overflow_expr::<AB>(&inputs, &vars, &constants, &flags);
424 self.check_carry_mod_to_zero.eval(
425 builder,
426 (
427 expr,
428 CheckCarryModToZeroCols {
429 carries: carry_limbs[i].clone(),
430 quotient: q_limbs[i].clone(),
431 },
432 is_valid.into(),
433 ),
434 );
435 }
436
437 for var in vars.iter() {
438 for limb in var.limbs().iter() {
439 range_check(
440 builder,
441 self.range_bus.inner.index,
442 self.range_bus.range_max_bits,
443 self.limb_bits,
444 limb.clone(),
445 is_valid,
446 );
447 }
448 }
449 }
450}
451
452type Vecs<T> = Vec<Vec<T>>;
453
454pub struct FieldExprCols<T> {
455 pub is_valid: T,
456 pub inputs: Vecs<T>,
457 pub vars: Vecs<T>,
458 pub q_limbs: Vecs<T>,
459 pub carry_limbs: Vecs<T>,
460 pub flags: Vec<T>,
461}
462
463impl<F: PrimeField64> TraceSubRowGenerator<F> for FieldExpr {
464 type TraceContext<'a> = (&'a VariableRangeCheckerChip, Vec<BigUint>, Vec<bool>);
465 type ColsMut<'a> = &'a mut [F];
466
467 fn generate_subrow<'a>(
468 &'a self,
469 (range_checker, inputs, flags): (&'a VariableRangeCheckerChip, Vec<BigUint>, Vec<bool>),
470 sub_row: &'a mut [F],
471 ) {
472 assert!(self.builder.is_finalized());
473 assert_eq!(inputs.len(), self.num_input);
474 assert_eq!(self.num_variables, self.constraints.len());
475
476 assert_eq!(flags.len(), self.builder.num_flags);
477
478 let limb_bits = self.limb_bits;
479 let mut vars = vec![BigUint::zero(); self.num_variables];
480
481 let input_bigint = inputs
483 .iter()
484 .map(|x| BigInt::from_biguint(Sign::Plus, x.clone()))
485 .collect::<Vec<BigInt>>();
486 let mut vars_bigint = vec![BigInt::zero(); self.num_variables];
487
488 let input_overflow = inputs
490 .iter()
491 .map(|x| OverflowInt::<isize>::from_biguint(x, self.limb_bits, Some(self.num_limbs)))
492 .collect::<Vec<_>>();
493 let zero = OverflowInt::<isize>::from_unsigned_limbs(vec![0], limb_bits);
494 let mut vars_overflow = vec![zero; self.num_variables];
495 let prime_overflow = OverflowInt::<isize>::from_biguint(&self.prime, self.limb_bits, None);
498
499 let constants: Vec<_> = self
500 .constants
501 .iter()
502 .map(|(_, limbs)| {
503 let limbs_isize: Vec<_> = limbs.iter().map(|i| *i as isize).collect();
504 OverflowInt::from_unsigned_limbs(limbs_isize, self.limb_bits)
505 })
506 .collect();
507
508 let mut all_q = vec![];
509 let mut all_carry = vec![];
510 for i in 0..self.constraints.len() {
511 let r = self.computes[i].compute(&inputs, &vars, &flags, &self.prime);
512 vars[i] = r.clone();
513 vars_bigint[i] = BigInt::from_biguint(Sign::Plus, r);
514 vars_overflow[i] =
515 OverflowInt::<isize>::from_biguint(&vars[i], self.limb_bits, Some(self.num_limbs));
516 }
517 for i in 0..self.constraints.len() {
520 let expr_bigint =
522 self.constraints[i].evaluate_bigint(&input_bigint, &vars_bigint, &flags);
523 let q = &expr_bigint / &self.prime_bigint;
524 debug_assert_eq!(expr_bigint, &q * &self.prime_bigint);
526 let q_limbs = big_int_to_num_limbs(&q, limb_bits, self.q_limbs[i]);
527 assert_eq!(q_limbs.len(), self.q_limbs[i]); for &q in q_limbs.iter() {
529 range_checker.add_count((q + (1 << limb_bits)) as u32, limb_bits + 1);
530 }
531 let q_overflow = OverflowInt::from_signed_limbs(q_limbs.clone(), limb_bits);
532 let expr = self.constraints[i].evaluate_overflow_isize(
534 &input_overflow,
535 &vars_overflow,
536 &constants,
537 &flags,
538 );
539 let expr = expr - q_overflow * prime_overflow.clone();
540 let carries = expr.calculate_carries(limb_bits);
541 assert_eq!(carries.len(), self.carry_limbs[i]); let max_overflow_bits = expr.max_overflow_bits();
543 let (carry_min_abs, carry_bits) =
544 get_carry_max_abs_and_bits(max_overflow_bits, limb_bits);
545 for &carry in carries.iter() {
546 range_checker.add_count((carry + carry_min_abs as isize) as u32, carry_bits);
547 }
548 all_q.push(vec_isize_to_f::<F>(q_limbs));
549 all_carry.push(vec_isize_to_f::<F>(carries));
550 }
551 for var in vars_overflow.iter() {
552 for limb in var.limbs().iter() {
553 range_checker.add_count(*limb as u32, limb_bits);
554 }
555 }
556
557 let input_limbs = input_overflow
558 .iter()
559 .map(|x| vec_isize_to_f::<F>(x.limbs().to_vec()))
560 .collect::<Vec<_>>();
561 let vars_limbs = vars_overflow
562 .iter()
563 .map(|x| vec_isize_to_f::<F>(x.limbs().to_vec()))
564 .collect::<Vec<_>>();
565
566 sub_row.copy_from_slice(
567 &[
568 vec![F::ONE],
569 input_limbs.concat(),
570 vars_limbs.concat(),
571 all_q.concat(),
572 all_carry.concat(),
573 flags.iter().map(|x| F::from_bool(*x)).collect::<Vec<_>>(),
574 ]
575 .concat(),
576 );
577 }
578}
579
580impl FieldExpr {
581 pub fn canonical_num_limbs(&self) -> usize {
582 self.builder.num_limbs
583 }
584
585 pub fn canonical_limb_bits(&self) -> usize {
586 self.builder.limb_bits
587 }
588
589 pub fn execute(&self, inputs: &[BigUint], flags: &[bool]) -> Vec<BigUint> {
590 assert!(self.builder.is_finalized());
591
592 #[cfg(debug_assertions)]
593 {
594 let is_setup = self.builder.needs_setup() && flags.iter().all(|&x| !x);
595 if is_setup {
596 assert_eq!(inputs[0], self.builder.prime);
597 assert!(inputs.len() > self.setup_values.len());
599 for (expected, actual) in self.setup_values.iter().zip(inputs.iter().skip(1)) {
600 assert_eq!(expected, actual);
601 }
602 }
603 }
604
605 let mut vars = vec![BigUint::zero(); self.num_variables];
606 for i in 0..self.constraints.len() {
607 let r = self.computes[i].compute(inputs, &vars, flags, &self.prime);
608 vars[i] = r; }
610 vars
611 }
612
613 pub fn execute_with_output(&self, inputs: &[BigUint], flags: &[bool]) -> Vec<BigUint> {
614 let vars = self.execute(inputs, flags);
615 self.builder
616 .output_indices
617 .iter()
618 .map(|i| vars[*i].clone())
619 .collect()
620 }
621
622 pub fn load_vars<T: Clone>(&self, arr: &[T]) -> FieldExprCols<T> {
623 assert!(self.builder.is_finalized());
624 let is_valid = arr[0].clone();
625 let mut idx = 1;
626 let mut inputs = vec![];
627 for _ in 0..self.num_input {
628 inputs.push(arr[idx..idx + self.num_limbs].to_vec());
629 idx += self.num_limbs;
630 }
631 let mut vars = vec![];
632 for _ in 0..self.num_variables {
633 vars.push(arr[idx..idx + self.num_limbs].to_vec());
634 idx += self.num_limbs;
635 }
636 let mut q_limbs = vec![];
637 for q in self.q_limbs.iter() {
638 q_limbs.push(arr[idx..idx + q].to_vec());
639 idx += q;
640 }
641 let mut carry_limbs = vec![];
642 for c in self.carry_limbs.iter() {
643 carry_limbs.push(arr[idx..idx + c].to_vec());
644 idx += c;
645 }
646 let flags = arr[idx..idx + self.num_flags].to_vec();
647 FieldExprCols {
648 is_valid,
649 inputs,
650 vars,
651 q_limbs,
652 carry_limbs,
653 flags,
654 }
655 }
656}
657
658fn load_overflow<AB: AirBuilder>(
659 arr: Vecs<AB::Var>,
660 limb_bits: usize,
661) -> Vec<OverflowInt<AB::Expr>> {
662 let mut result = vec![];
663 for x in arr.into_iter() {
664 let limbs: Vec<AB::Expr> = x.iter().cloned().map(|x| x.into()).collect();
665 result.push(OverflowInt::<AB::Expr>::from_unsigned_limbs(
666 limbs, limb_bits,
667 ));
668 }
669 result
670}