Skip to main content

sp1_hypercube/lookup/
builder.rs

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
13/// A builder for the lookup table interactions.
14pub 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    /// Creates a new [`InteractionBuilder`] with the given width.
24    #[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    /// Returns the sends and receives.
45    #[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}