openvm_recursion_circuit/batch_constraint/eq_airs/eq_3b/
air.rs1use std::borrow::Borrow;
2
3use openvm_circuit_primitives::{
4 utils::{assert_array_eq, 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;
12use p3_air::{Air, AirBuilder, BaseAir};
13use p3_field::{extension::BinomiallyExtendable, PrimeCharacteristicRing};
14use p3_matrix::Matrix;
15
16use crate::{
17 batch_constraint::bus::{
18 BatchConstraintConductorBus, BatchConstraintConductorMessage,
19 BatchConstraintInnerMessageType, Eq3bBus, Eq3bMessage,
20 },
21 bus::{Eq3bShapeBus, Eq3bShapeMessage},
22 subairs::nested_for_loop::{NestedForLoopIoCols, NestedForLoopSubAir},
23 utils::{
24 base_to_ext, ext_field_add, ext_field_multiply, ext_field_multiply_scalar,
25 ext_field_one_minus,
26 },
27};
28
29#[derive(AlignedBorrow, Clone, Copy, StructReflection)]
30#[repr(C)]
31pub struct Eq3bColumns<T> {
32 pub is_valid: T,
33 pub is_first: T,
34 pub proof_idx: T,
35
36 pub sort_idx: T,
37 pub interaction_idx: T,
38
39 pub n_lift: T,
40 pub n_logup: T,
41 pub two_to_the_n_lift: T,
42 pub n: T,
43 pub hypercube_volume: T, pub n_at_least_n_lift: T,
45
46 pub has_no_interactions: T,
47
48 pub is_first_in_air: T,
49 pub is_first_in_interaction: T,
50
51 pub idx: T, pub running_idx: T, pub nth_bit: T,
54
55 pub xi: [T; D_EF],
56 pub eq: [T; D_EF],
57}
58
59#[derive(ColumnsAir)]
60#[columns_via(Eq3bColumns<u8>)]
61pub struct Eq3bAir {
62 pub eq_3b_bus: Eq3bBus,
63 pub eq_3b_shape_bus: Eq3bShapeBus,
64 pub batch_constraint_conductor_bus: BatchConstraintConductorBus,
65
66 pub l_skip: usize,
67}
68
69impl<F> BaseAirWithPublicValues<F> for Eq3bAir {}
70impl<F> PartitionedBaseAir<F> for Eq3bAir {}
71
72impl<F> BaseAir<F> for Eq3bAir {
73 fn width(&self) -> usize {
74 Eq3bColumns::<F>::width()
75 }
76}
77
78impl<AB: AirBuilder + InteractionBuilder> Air<AB> for Eq3bAir
79where
80 <AB::Expr as PrimeCharacteristicRing>::PrimeSubfield: BinomiallyExtendable<{ D_EF }>,
81{
82 fn eval(&self, builder: &mut AB) {
83 let main = builder.main();
84 let (local, next) = (
85 main.row_slice(0).expect("window should have two elements"),
86 main.row_slice(1).expect("window should have two elements"),
87 );
88
89 let local: &Eq3bColumns<AB::Var> = (*local).borrow();
90 let next: &Eq3bColumns<AB::Var> = (*next).borrow();
91
92 type LoopSubAir = NestedForLoopSubAir<3>;
93 LoopSubAir {}.eval(
94 builder,
95 (
96 NestedForLoopIoCols {
97 is_enabled: local.is_valid,
98 counter: [local.proof_idx, local.sort_idx, local.interaction_idx],
99 is_first: [
100 local.is_first,
101 local.is_first_in_air,
102 local.is_first_in_interaction,
103 ],
104 }
105 .map_into(),
106 NestedForLoopIoCols {
107 is_enabled: next.is_valid,
108 counter: [next.proof_idx, next.sort_idx, next.interaction_idx],
109 is_first: [
110 next.is_first,
111 next.is_first_in_air,
112 next.is_first_in_interaction,
113 ],
114 }
115 .map_into(),
116 ),
117 );
118
119 builder.when(local.is_first).assert_zero(local.sort_idx);
121 builder
122 .when(local.is_first_in_air)
123 .assert_zero(local.interaction_idx);
124
125 builder.assert_bool(local.n_at_least_n_lift);
126 builder.assert_bool(local.nth_bit);
127 builder.assert_bool(local.has_no_interactions);
128
129 let within_one_air = next.is_valid - next.is_first_in_air;
130 let within_one_interaction = next.is_valid - next.is_first_in_interaction;
131 let is_last_in_interaction = local.is_valid - within_one_interaction.clone();
132
133 builder
135 .when(local.is_first_in_interaction)
136 .assert_zero(local.n);
137 builder
138 .when(local.is_first_in_interaction)
139 .when(local.is_valid)
140 .assert_one(local.hypercube_volume);
141 builder
142 .when(within_one_air.clone())
143 .assert_eq(next.n_lift, local.n_lift);
144 builder
145 .when(LoopSubAir::local_is_transition(
146 next.is_valid,
147 next.is_first,
148 ))
149 .assert_eq(next.n_logup, local.n_logup);
150 builder
151 .when(within_one_interaction.clone())
152 .assert_eq(next.two_to_the_n_lift, local.two_to_the_n_lift);
153 builder
154 .when(within_one_interaction.clone())
155 .assert_eq(next.n, local.n + AB::Expr::ONE);
156 builder.when(within_one_interaction.clone()).assert_eq(
157 next.hypercube_volume,
158 local.hypercube_volume * AB::Expr::TWO,
159 );
160 builder
162 .when(within_one_interaction.clone())
163 .when(local.n_at_least_n_lift)
164 .assert_one(next.n_at_least_n_lift);
165 builder
167 .when(not(local.has_no_interactions))
168 .when(is_last_in_interaction.clone())
169 .when(local.is_valid)
170 .assert_one(local.n_at_least_n_lift);
171
172 builder
174 .when(not(local.n_at_least_n_lift))
175 .when(next.n_at_least_n_lift)
176 .assert_eq(next.n, next.n_lift);
177 builder
178 .when(not(local.n_at_least_n_lift))
179 .when(next.n_at_least_n_lift)
180 .assert_eq(next.hypercube_volume, next.two_to_the_n_lift);
181 builder
183 .when(local.is_first_in_interaction)
184 .when(local.n_at_least_n_lift)
185 .assert_zero(local.n_lift);
186 builder
187 .when(local.is_first_in_interaction)
188 .when(local.n_at_least_n_lift)
189 .assert_one(local.two_to_the_n_lift);
190 builder
191 .when(is_last_in_interaction.clone())
192 .when(not(local.has_no_interactions))
193 .assert_eq(local.n, local.n_logup);
194
195 builder.when(next.is_valid - next.is_first).assert_eq(
196 next.running_idx,
197 local.running_idx
198 + (next.is_first_in_interaction - local.has_no_interactions)
199 * local.two_to_the_n_lift,
200 );
201
202 assert_array_eq(
205 &mut builder.when(local.is_valid * local.is_first_in_interaction),
206 local.eq,
207 base_to_ext::<AB::Expr>(AB::Expr::ONE),
208 );
209 builder
210 .when(local.is_first_in_interaction)
211 .assert_zero(local.idx);
212 builder.when(local.is_first).assert_zero(local.running_idx);
213 builder
214 .when(is_last_in_interaction.clone())
215 .when(not(local.has_no_interactions))
216 .assert_eq(local.idx, local.running_idx);
217 builder
218 .when(next.is_valid)
219 .when(local.has_no_interactions)
220 .assert_eq(local.running_idx, next.running_idx);
221
222 assert_array_eq(
224 &mut builder
225 .when(local.is_valid - local.has_no_interactions)
226 .when(not(local.n_at_least_n_lift)),
227 local.eq,
228 next.eq,
229 );
230 builder
232 .when(within_one_interaction.clone())
233 .assert_eq(next.idx, local.idx + local.nth_bit * local.hypercube_volume);
234 builder
236 .when(not(local.n_at_least_n_lift))
237 .assert_zero(local.nth_bit);
238 assert_array_eq(
240 &mut builder.when(within_one_interaction.clone()),
241 next.eq,
242 ext_field_multiply(
243 local.eq,
244 ext_field_add::<AB::Expr>(
245 ext_field_multiply_scalar(local.xi, local.nth_bit),
246 ext_field_multiply_scalar::<AB::Expr>(
247 ext_field_one_minus(local.xi),
248 AB::Expr::ONE - local.nth_bit,
249 ),
250 ),
251 ),
252 );
253
254 builder
256 .when(local.has_no_interactions)
257 .assert_one(local.is_valid);
258 builder
259 .when(local.has_no_interactions)
260 .assert_one(local.is_first_in_air);
261 builder
262 .when(local.has_no_interactions)
263 .assert_zero(within_one_air);
264
265 self.batch_constraint_conductor_bus.lookup_key(
266 builder,
267 local.proof_idx,
268 BatchConstraintConductorMessage {
269 msg_type: BatchConstraintInnerMessageType::Xi.to_field(),
270 idx: local.n + AB::Expr::from_usize(self.l_skip),
271 value: local.xi.map(|x| x.into()),
272 },
273 local.n_at_least_n_lift * within_one_interaction,
274 );
275
276 self.eq_3b_shape_bus.lookup_key(
277 builder,
278 local.proof_idx,
279 Eq3bShapeMessage {
280 sort_idx: local.sort_idx.into(),
281 n_lift: local.n_lift.into(),
282 n_logup: local.n_logup.into(),
283 num_interactions: local.interaction_idx + not(local.has_no_interactions),
284 },
285 LoopSubAir::local_is_last(local.is_valid, next.is_valid, next.is_first_in_air),
286 );
287
288 self.eq_3b_bus.send(
289 builder,
290 local.proof_idx,
291 Eq3bMessage {
292 sort_idx: local.sort_idx,
293 interaction_idx: local.interaction_idx,
294 eq_3b: local.eq,
295 },
296 is_last_in_interaction * (local.is_valid - local.has_no_interactions),
297 );
298 }
299}