Skip to content

Commit 22c297e

Browse files
updated mod-builder
- support checking more constants during setup - support new finalization (with dummy row and temp range checker)
1 parent 1ee9fba commit 22c297e

File tree

2 files changed

+68
-24
lines changed

2 files changed

+68
-24
lines changed

crates/circuits/mod-builder/src/builder.rs

Lines changed: 38 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
use std::{cell::RefCell, ops::Deref, rc::Rc};
1+
use std::{cell::RefCell, iter, ops::Deref, rc::Rc};
22

3+
use itertools::{zip_eq, Itertools};
34
use num_bigint::{BigInt, BigUint, Sign};
45
use num_traits::Zero;
56
use openvm_circuit_primitives::{
@@ -209,6 +210,9 @@ pub struct FieldExpr {
209210
pub check_carry_mod_to_zero: CheckCarryModToZeroSubAir,
210211

211212
pub range_bus: VariableRangeCheckerBus,
213+
214+
// any values other than the prime modulus that need to be checked at setup
215+
pub setup_values: Vec<BigUint>,
212216
}
213217

214218
impl FieldExpr {
@@ -229,8 +233,20 @@ impl FieldExpr {
229233
builder,
230234
check_carry_mod_to_zero: subair,
231235
range_bus,
236+
setup_values: vec![],
232237
}
233238
}
239+
240+
pub fn new_with_setup_values(
241+
builder: ExprBuilder,
242+
range_bus: VariableRangeCheckerBus,
243+
needs_setup: bool,
244+
setup_values: Vec<BigUint>,
245+
) -> Self {
246+
let mut ret = Self::new(builder, range_bus, needs_setup);
247+
ret.setup_values = setup_values;
248+
ret
249+
}
234250
}
235251

236252
impl Deref for FieldExpr {
@@ -295,18 +311,27 @@ impl<AB: InteractionBuilder> SubAir<AB> for FieldExpr {
295311
// however this has the challenge that when the same chip is used
296312
// across continuation segments,
297313
// only the first segment will have setup called
298-
for i in 0..inputs[0].len().max(self.builder.prime_limbs.len()) {
299-
let lhs = if i < inputs[0].len() {
300-
inputs[0][i].into()
301-
} else {
302-
AB::Expr::ZERO
303-
};
304-
let rhs = if i < self.builder.prime_limbs.len() {
305-
AB::Expr::from_canonical_usize(self.builder.prime_limbs[i])
306-
} else {
307-
AB::Expr::ZERO
308-
};
309-
builder.when(is_setup.clone()).assert_eq(lhs, rhs);
314+
315+
let expected = iter::empty()
316+
.chain(self.builder.prime_limbs.clone())
317+
.chain(self.setup_values.iter().flat_map(|x| {
318+
big_uint_to_num_limbs(x, self.builder.limb_bits, self.builder.num_limbs)
319+
.into_iter()
320+
}))
321+
.collect_vec();
322+
323+
let reads: Vec<AB::Expr> = inputs
324+
.clone()
325+
.into_iter()
326+
.flatten()
327+
.map(Into::into)
328+
.take(expected.len())
329+
.collect();
330+
331+
for (lhs, rhs) in zip_eq(&reads, expected) {
332+
builder
333+
.when(is_setup.clone())
334+
.assert_eq(lhs.clone(), AB::F::from_canonical_usize(rhs));
310335
}
311336
}
312337

crates/circuits/mod-builder/src/core_chip.rs

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use itertools::Itertools;
22
use num_bigint::BigUint;
3+
use num_traits::Zero;
34
use openvm_circuit::arch::{
45
AdapterAirContext, AdapterRuntimeContext, DynAdapterInterface, DynArray, MinimalInstruction,
56
Result, VmAdapterInterface, VmCoreAir, VmCoreChip,
@@ -292,20 +293,38 @@ where
292293
if !self.should_finalize || num_records == 0 {
293294
return;
294295
}
295-
// We will copy over the core part of last row to padded rows (all rows after num_records).
296+
296297
let core_width = <Self::Air as BaseAir<F>>::width(&self.air);
297298
let adapter_width = trace.width() - core_width;
298-
let last_row = trace
299-
.rows()
300-
.nth(num_records - 1)
301-
.unwrap()
302-
.collect::<Vec<_>>();
303-
let last_row_core = last_row.split_at(adapter_width).1;
299+
let dummy_row = self.generate_dummy_trace_row(adapter_width, core_width);
304300
for row in trace.rows_mut().skip(num_records) {
305-
let core_row = row.split_at_mut(adapter_width).1;
306-
// The same as last row, except "is_valid" (the first element of core part) is zero.
307-
core_row.copy_from_slice(last_row_core);
308-
core_row[0] = F::ZERO;
301+
row.copy_from_slice(&dummy_row);
309302
}
310303
}
311304
}
305+
306+
impl FieldExpressionCoreChip {
307+
// We will be setting is_valid = 0. That forces all flags be 0 (otherwise setup will be -1).
308+
// We generate a dummy row with all flags set to 0, then we set is_valid = 0.
309+
fn generate_dummy_trace_row<F: PrimeField32>(
310+
&self,
311+
adapter_width: usize,
312+
core_width: usize,
313+
) -> Vec<F> {
314+
let record = FieldExpressionRecord {
315+
inputs: vec![BigUint::zero(); self.air.num_inputs()],
316+
flags: vec![false; self.air.num_flags()],
317+
};
318+
let mut row = vec![F::ZERO; adapter_width + core_width];
319+
let core_row = &mut row[adapter_width..];
320+
// We **do not** want this trace row to update the range checker
321+
// so we must create a temporary range checker
322+
let tmp_range_checker = SharedVariableRangeCheckerChip::new(self.range_checker.bus());
323+
self.air.expr.generate_subrow(
324+
(tmp_range_checker.as_ref(), record.inputs, record.flags),
325+
core_row,
326+
);
327+
core_row[0] = F::ZERO; // is_valid = 0
328+
row
329+
}
330+
}

0 commit comments

Comments
 (0)