1use slop_air::{AirBuilder, AirBuilderWithPublicValues, PairBuilder, PairCol, VirtualPairCol};
2use slop_algebra::Field;
3use slop_matrix::dense::RowMajorMatrix;
4use slop_uni_stark::{Entry, SymbolicExpression, SymbolicVariable};
5
6use crate::{
7 air::{AirInteraction, InteractionScope, MessageBuilder},
8 PROOF_MAX_NUM_PVS,
9};
10
11use super::Interaction;
12
13pub struct InteractionBuilder<F: Field> {
15 preprocessed: RowMajorMatrix<SymbolicVariable<F>>,
16 main: RowMajorMatrix<SymbolicVariable<F>>,
17 sends: Vec<Interaction<F>>,
18 receives: Vec<Interaction<F>>,
19 public_values: Vec<F>,
20}
21
22impl<F: Field> InteractionBuilder<F> {
23 #[must_use]
25 pub fn new(preprocessed_width: usize, main_width: usize) -> Self {
26 let preprocessed_width = preprocessed_width.max(1);
27 let prep_values = (0..preprocessed_width)
28 .map(move |column| SymbolicVariable::new(Entry::Preprocessed { offset: 0 }, column))
29 .collect();
30
31 let main_values = (0..main_width)
32 .map(move |column| SymbolicVariable::new(Entry::Main { offset: 0 }, column))
33 .collect();
34
35 Self {
36 preprocessed: RowMajorMatrix::new(prep_values, preprocessed_width),
37 main: RowMajorMatrix::new(main_values, main_width),
38 sends: vec![],
39 receives: vec![],
40 public_values: vec![F::zero(); PROOF_MAX_NUM_PVS],
41 }
42 }
43
44 #[must_use]
46 pub fn interactions(self) -> (Vec<Interaction<F>>, Vec<Interaction<F>>) {
47 (self.sends, self.receives)
48 }
49}
50
51impl<F: Field> AirBuilder for InteractionBuilder<F> {
52 type F = F;
53 type Expr = SymbolicExpression<F>;
54 type Var = SymbolicVariable<F>;
55 type M = RowMajorMatrix<Self::Var>;
56
57 fn main(&self) -> Self::M {
58 self.main.clone()
59 }
60
61 fn is_first_row(&self) -> Self::Expr {
62 unimplemented!();
63 }
64
65 fn is_last_row(&self) -> Self::Expr {
66 unimplemented!();
67 }
68
69 fn is_transition_window(&self, _: usize) -> Self::Expr {
70 unimplemented!();
71 }
72
73 fn assert_zero<I: Into<Self::Expr>>(&mut self, _x: I) {}
74}
75
76impl<F: Field> PairBuilder for InteractionBuilder<F> {
77 fn preprocessed(&self) -> Self::M {
78 self.preprocessed.clone()
79 }
80}
81
82impl<F: Field> MessageBuilder<AirInteraction<SymbolicExpression<F>>> for InteractionBuilder<F> {
83 fn send(&mut self, message: AirInteraction<SymbolicExpression<F>>, scope: InteractionScope) {
84 let values =
85 message.values.into_iter().map(|v| symbolic_to_virtual_pair(&v)).collect::<Vec<_>>();
86
87 let multiplicity = symbolic_to_virtual_pair(&message.multiplicity);
88
89 self.sends.push(Interaction::new(values, multiplicity, message.kind, scope));
90 }
91
92 fn receive(&mut self, message: AirInteraction<SymbolicExpression<F>>, scope: InteractionScope) {
93 let values =
94 message.values.into_iter().map(|v| symbolic_to_virtual_pair(&v)).collect::<Vec<_>>();
95
96 let multiplicity = symbolic_to_virtual_pair(&message.multiplicity);
97
98 self.receives.push(Interaction::new(values, multiplicity, message.kind, scope));
99 }
100}
101
102impl<F: Field> AirBuilderWithPublicValues for InteractionBuilder<F> {
103 type PublicVar = F;
104
105 fn public_values(&self) -> &[Self::PublicVar] {
106 &self.public_values
107 }
108}
109
110fn symbolic_to_virtual_pair<F: Field>(expression: &SymbolicExpression<F>) -> VirtualPairCol<F> {
111 if expression.degree_multiple() > 1 {
112 panic!("degree multiple is too high");
113 }
114
115 let (column_weights, constant) = eval_symbolic_to_virtual_pair(expression);
116
117 let column_weights = column_weights.into_iter().collect();
118
119 VirtualPairCol::new(column_weights, constant)
120}
121
122fn eval_symbolic_to_virtual_pair<F: Field>(
123 expression: &SymbolicExpression<F>,
124) -> (Vec<(PairCol, F)>, F) {
125 match expression {
126 SymbolicExpression::Constant(c) => (vec![], *c),
127 SymbolicExpression::Variable(v) => match v.entry {
128 Entry::Preprocessed { offset: 0 } => {
129 (vec![(PairCol::Preprocessed(v.index), F::one())], F::zero())
130 }
131 Entry::Main { offset: 0 } => (vec![(PairCol::Main(v.index), F::one())], F::zero()),
132 _ => panic!("not an affine expression in current row elements {:?}", v.entry),
133 },
134 SymbolicExpression::Add { x, y, .. } => {
135 let (v_l, c_l) = eval_symbolic_to_virtual_pair(x);
136 let (v_r, c_r) = eval_symbolic_to_virtual_pair(y);
137 ([v_l, v_r].concat(), c_l + c_r)
138 }
139 SymbolicExpression::Sub { x, y, .. } => {
140 let (v_l, c_l) = eval_symbolic_to_virtual_pair(x);
141 let (v_r, c_r) = eval_symbolic_to_virtual_pair(y);
142 let neg_v_r = v_r.iter().map(|(c, w)| (*c, -*w)).collect();
143 ([v_l, neg_v_r].concat(), c_l - c_r)
144 }
145 SymbolicExpression::Neg { x, .. } => {
146 let (v, c) = eval_symbolic_to_virtual_pair(x);
147 (v.iter().map(|(c, w)| (*c, -*w)).collect(), -c)
148 }
149 SymbolicExpression::Mul { x, y, .. } => {
150 let (v_l, c_l) = eval_symbolic_to_virtual_pair(x);
151 let (v_r, c_r) = eval_symbolic_to_virtual_pair(y);
152
153 let mut v = vec![];
154 v.extend(v_l.iter().map(|(c, w)| (*c, *w * c_r)));
155 v.extend(v_r.iter().map(|(c, w)| (*c, *w * c_l)));
156
157 if !v_l.is_empty() && !v_r.is_empty() {
158 panic!("Not an affine expression")
159 }
160
161 (v, c_l * c_r)
162 }
163 SymbolicExpression::IsFirstRow => {
164 panic!("not an affine expression in current row elements for first row")
165 }
166 SymbolicExpression::IsLastRow => {
167 panic!("not an affine expression in current row elements for last row")
168 }
169 SymbolicExpression::IsTransition => {
170 panic!("not an affine expression in current row elements for transition row")
171 }
172 }
173}
174
175#[cfg(test)]
176mod tests {
177 #![allow(clippy::print_stdout)]
178
179 use std::borrow::Borrow;
180
181 use slop_air::{Air, BaseAir};
182 use slop_algebra::AbstractField;
183
184 use slop_matrix::Matrix;
185 use sp1_primitives::SP1Field;
186
187 use super::*;
188 use crate::{air::SP1AirBuilder, lookup::InteractionKind};
189
190 #[test]
191 fn test_symbolic_to_virtual_pair_col() {
192 use sp1_primitives::SP1Field;
193 type F = SP1Field;
194
195 let x = SymbolicVariable::<F>::new(Entry::Main { offset: 0 }, 0);
196
197 let y = SymbolicVariable::<F>::new(Entry::Main { offset: 0 }, 1);
198
199 let z = x + y;
200
201 let (column_weights, constant) = super::eval_symbolic_to_virtual_pair(&z);
202 println!("column_weights: {column_weights:?}");
203 println!("constant: {constant:?}");
204
205 let column_weights = column_weights.into_iter().collect::<Vec<_>>();
206
207 let z = VirtualPairCol::new(column_weights, constant);
208
209 let expr: F = z.apply(&[], &[F::one(), F::one()]);
210
211 println!("expr: {expr}");
212 }
213
214 pub struct LookupTestAir;
215
216 const NUM_COLS: usize = 3;
217
218 impl<F: Field> BaseAir<F> for LookupTestAir {
219 fn width(&self) -> usize {
220 NUM_COLS
221 }
222 }
223
224 impl<AB: SP1AirBuilder> Air<AB> for LookupTestAir {
225 fn eval(&self, builder: &mut AB) {
226 let main = builder.main();
227 let local = main.row_slice(0);
228 let local: &[AB::Var] = (*local).borrow();
229
230 let x = local[0];
231 let y = local[1];
232 let z = local[2];
233
234 builder.send(
235 AirInteraction::new(
236 vec![x.into(), y.into()],
237 AB::F::from_canonical_u32(3).into(),
238 InteractionKind::Memory,
239 ),
240 InteractionScope::Local,
241 );
242 builder.send(
243 AirInteraction::new(
244 vec![x + y, z.into()],
245 AB::F::from_canonical_u32(5).into(),
246 InteractionKind::Memory,
247 ),
248 InteractionScope::Local,
249 );
250
251 builder.receive(
252 AirInteraction::new(vec![x.into()], y.into(), InteractionKind::Byte),
253 InteractionScope::Local,
254 );
255 }
256 }
257
258 #[test]
259 fn test_lookup_interactions() {
260 let air = LookupTestAir {};
261
262 let mut builder = InteractionBuilder::<SP1Field>::new(0, NUM_COLS);
263
264 air.eval(&mut builder);
265
266 let mut main = builder.main();
267 let (sends, receives) = builder.interactions();
268
269 for interaction in receives {
270 print!("Receive values: ");
271 for value in interaction.values {
272 let expr = value.apply::<SymbolicExpression<SP1Field>, SymbolicVariable<SP1Field>>(
273 &[],
274 main.row_mut(0),
275 );
276 print!("{expr:?}, ");
277 }
278
279 let multiplicity = interaction
280 .multiplicity
281 .apply::<SymbolicExpression<SP1Field>, SymbolicVariable<SP1Field>>(
282 &[],
283 main.row_mut(0),
284 );
285
286 println!(", multiplicity: {multiplicity:?}");
287 }
288
289 for interaction in sends {
290 print!("Send values: ");
291 for value in interaction.values {
292 let expr = value.apply::<SymbolicExpression<SP1Field>, SymbolicVariable<SP1Field>>(
293 &[],
294 main.row_mut(0),
295 );
296 print!("{expr:?}, ");
297 }
298
299 let multiplicity = interaction
300 .multiplicity
301 .apply::<SymbolicExpression<SP1Field>, SymbolicVariable<SP1Field>>(
302 &[],
303 main.row_mut(0),
304 );
305
306 println!(", multiplicity: {multiplicity:?}");
307 }
308 }
309}