openvm_recursion_circuit/stacking/eq_bits/
air.rs1use std::borrow::Borrow;
2
3use openvm_circuit_primitives::{
4 utils::{and, not},
5 ColumnsAir, StructReflection, StructReflectionHelper, SubAir,
6};
7use openvm_recursion_circuit_derive::AlignedBorrow;
8use openvm_stark_backend::{
9 interaction::InteractionBuilder, BaseAirWithPublicValues, PartitionedBaseAir,
10};
11use openvm_stark_sdk::config::baby_bear_poseidon2::{D_EF, F};
12use p3_air::{Air, AirBuilder, BaseAir};
13use p3_field::{
14 extension::BinomiallyExtendable, Field, PrimeCharacteristicRing, PrimeField32, TwoAdicField,
15};
16use p3_matrix::Matrix;
17
18use crate::{
19 stacking::bus::{
20 EqBitsInternalBus, EqBitsInternalMessage, EqBitsLookupBus, EqBitsLookupMessage,
21 EqRandValuesLookupBus, EqRandValuesLookupMessage,
22 },
23 subairs::nested_for_loop::{NestedForLoopIoCols, NestedForLoopSubAir},
24 utils::{
25 assert_one_ext, assert_zeros, ext_field_add_scalar, ext_field_multiply,
26 ext_field_multiply_scalar, ext_field_subtract, ext_field_subtract_scalar,
27 },
28};
29
30#[repr(C)]
31#[derive(AlignedBorrow, StructReflection)]
32pub struct EqBitsCols<F> {
33 pub proof_idx: F,
35 pub is_valid: F,
36 pub is_first: F,
37
38 pub internal_child_flag: F,
40 pub external_mult: F,
41
42 pub sub_b_value: F,
44 pub sub_eval: [F; D_EF],
45
46 pub b_lsb: F,
48 pub num_bits: F,
49 pub u_val: [F; D_EF],
50}
51
52#[derive(ColumnsAir)]
53#[columns_via(EqBitsCols<u8>)]
54pub struct EqBitsAir {
55 pub eq_bits_internal_bus: EqBitsInternalBus,
57 pub eq_bits_lookup_bus: EqBitsLookupBus,
58 pub eq_rand_values_bus: EqRandValuesLookupBus,
59
60 pub n_stack: usize,
62 pub l_skip: usize,
63}
64
65impl BaseAirWithPublicValues<F> for EqBitsAir {}
66impl PartitionedBaseAir<F> for EqBitsAir {}
67
68impl<F> BaseAir<F> for EqBitsAir {
69 fn width(&self) -> usize {
70 EqBitsCols::<F>::width()
71 }
72}
73
74impl<AB: AirBuilder + InteractionBuilder> Air<AB> for EqBitsAir
75where
76 AB::F: PrimeField32 + TwoAdicField,
77 <AB::Expr as PrimeCharacteristicRing>::PrimeSubfield: BinomiallyExtendable<{ D_EF }>,
78{
79 fn eval(&self, builder: &mut AB) {
80 let main = builder.main();
81 let (local, next) = (
82 main.row_slice(0).expect("window should have two elements"),
83 main.row_slice(1).expect("window should have two elements"),
84 );
85
86 let local: &EqBitsCols<AB::Var> = (*local).borrow();
87 let next: &EqBitsCols<AB::Var> = (*next).borrow();
88
89 NestedForLoopSubAir::<1> {}.eval(
90 builder,
91 (
92 NestedForLoopIoCols {
93 is_enabled: local.is_valid,
94 counter: [local.proof_idx],
95 is_first: [local.is_first],
96 }
97 .map_into(),
98 NestedForLoopIoCols {
99 is_enabled: next.is_valid,
100 counter: [next.proof_idx],
101 is_first: [next.is_first],
102 }
103 .map_into(),
104 ),
105 );
106 let mut when_first = builder.when(local.is_first);
110
111 when_first.assert_zero(local.sub_b_value);
112 when_first.assert_zero(local.num_bits);
113 when_first.assert_zero(local.b_lsb);
114
115 assert_zeros(&mut when_first, local.u_val);
116 assert_one_ext(&mut when_first, local.sub_eval);
117
118 self.eq_rand_values_bus.lookup_key(
125 builder,
126 local.proof_idx,
127 EqRandValuesLookupMessage {
128 idx: AB::Expr::from_usize(self.n_stack + 1) - local.num_bits,
129 u: local.u_val.map(Into::into),
130 },
131 and(not(local.is_first), local.is_valid),
132 );
133
134 self.eq_bits_internal_bus.receive(
135 builder,
136 local.proof_idx,
137 EqBitsInternalMessage {
138 b_value: local.sub_b_value.into(),
139 num_bits: local.num_bits.into() - AB::Expr::ONE,
140 eval: local.sub_eval.map(Into::into),
141 child_lsb: local.b_lsb.into(),
142 },
143 and(not(local.is_first), local.is_valid),
144 );
145
146 let b_value = AB::Expr::TWO * local.sub_b_value + local.b_lsb;
147 builder.assert_bool(local.b_lsb);
148
149 let eval = ext_field_multiply(
157 local.sub_eval,
158 ext_field_subtract_scalar::<AB::Expr>(
159 ext_field_subtract::<AB::Expr>(
160 ext_field_add_scalar::<AB::Expr>(
161 ext_field_multiply_scalar(local.u_val, AB::Expr::TWO * local.b_lsb),
162 AB::Expr::ONE,
163 ),
164 local.u_val,
165 ),
166 local.b_lsb,
167 ),
168 );
169
170 let three = AB::F::from_u8(3);
171 builder
172 .when(not(local.is_valid))
173 .assert_zero(local.internal_child_flag);
174 builder
175 .when_ne(local.internal_child_flag, three)
176 .when_ne(local.internal_child_flag, AB::Expr::TWO)
177 .assert_bool(local.internal_child_flag);
178
179 self.eq_bits_internal_bus.send(
182 builder,
183 local.proof_idx,
184 EqBitsInternalMessage {
185 b_value: b_value.clone(),
186 num_bits: local.num_bits.into(),
187 eval: eval.clone(),
188 child_lsb: AB::Expr::ZERO,
189 },
190 local.internal_child_flag
191 * (local.internal_child_flag - AB::Expr::TWO)
192 * (local.internal_child_flag * AB::Expr::TWO - AB::Expr::from_u8(5))
193 * three.inverse(),
194 );
195
196 self.eq_bits_internal_bus.send(
199 builder,
200 local.proof_idx,
201 EqBitsInternalMessage {
202 b_value: b_value.clone(),
203 num_bits: local.num_bits.into(),
204 eval: eval.clone(),
205 child_lsb: AB::Expr::ONE,
206 },
207 local.internal_child_flag
208 * (AB::Expr::ONE - local.internal_child_flag)
209 * (local.internal_child_flag * AB::Expr::TWO - AB::Expr::from_u8(7))
210 * AB::F::from_u8(6).inverse(),
211 );
212
213 self.eq_bits_lookup_bus.add_key_with_lookups(
214 builder,
215 local.proof_idx,
216 EqBitsLookupMessage {
217 b_value: b_value * AB::Expr::from_usize(1 << self.l_skip),
218 num_bits: local.num_bits.into(),
219 eval,
220 },
221 local.is_valid * local.external_mult,
222 );
223 }
224}