openvm_stark_backend/test_utils/dummy_airs/interaction/
dummy_interaction_air.rs1use std::{iter, sync::Arc};
7
8use itertools::izip;
9use p3_air::{Air, BaseAir, BaseAirWithPublicValues};
10use p3_field::PrimeCharacteristicRing;
11use p3_matrix::{dense::RowMajorMatrix, Matrix};
12
13use crate::{
14 air_builders::PartitionedAirBuilder,
15 interaction::{BusIndex, InteractionBuilder},
16 prover::{
17 stacked_pcs::stacked_commit, AirProvingContext, ColMajorMatrix, CommittedTraceData,
18 CpuColMajorBackend,
19 },
20 AirRef, PartitionedBaseAir, StarkProtocolConfig,
21};
22
23pub struct DummyInteractionCols;
24impl DummyInteractionCols {
25 pub fn count_col() -> usize {
26 0
27 }
28 pub fn field_col(field_idx: usize) -> usize {
29 field_idx + 1
30 }
31}
32
33#[derive(Clone, Copy)]
34pub struct DummyInteractionAir {
35 field_width: usize,
36 pub is_send: bool,
38 bus_index: BusIndex,
39 pub count_weight: u32,
40 pub partition: bool,
42}
43
44impl DummyInteractionAir {
45 pub fn new(field_width: usize, is_send: bool, bus_index: BusIndex) -> Self {
46 Self {
47 field_width,
48 is_send,
49 bus_index,
50 count_weight: 0,
51 partition: false,
52 }
53 }
54
55 pub fn partition(self) -> Self {
56 Self {
57 partition: true,
58 ..self
59 }
60 }
61
62 pub fn field_width(&self) -> usize {
63 self.field_width
64 }
65}
66
67impl<F> BaseAirWithPublicValues<F> for DummyInteractionAir {}
68impl<F> PartitionedBaseAir<F> for DummyInteractionAir {
69 fn cached_main_widths(&self) -> Vec<usize> {
70 if self.partition {
71 vec![self.field_width]
72 } else {
73 vec![]
74 }
75 }
76 fn common_main_width(&self) -> usize {
77 if self.partition {
78 1
79 } else {
80 1 + self.field_width
81 }
82 }
83}
84impl<F> BaseAir<F> for DummyInteractionAir {
85 fn width(&self) -> usize {
86 1 + self.field_width
87 }
88
89 fn preprocessed_trace(&self) -> Option<RowMajorMatrix<F>> {
90 None
91 }
92}
93
94impl<AB: InteractionBuilder + PartitionedAirBuilder> Air<AB> for DummyInteractionAir {
95 fn eval(&self, builder: &mut AB) {
96 let (fields, count) = if self.partition {
97 let local_0 = builder.common_main().row_slice(0).unwrap();
98 let local_1 = builder.cached_mains()[0].row_slice(0).unwrap();
99 let count = local_0[0];
100 let fields = local_1.to_vec();
101 (fields, count)
102 } else {
103 let main = builder.main();
104 let local = main.row_slice(0).expect("window should have two elements");
105 let count = local[DummyInteractionCols::count_col()];
106 let fields: Vec<_> = (0..self.field_width)
107 .map(|i| local[DummyInteractionCols::field_col(i)])
108 .collect();
109 (fields, count)
110 };
111 if self.is_send {
112 builder.push_interaction(self.bus_index, fields, count, self.count_weight);
113 } else {
114 builder.push_interaction(
115 self.bus_index,
116 fields,
117 AB::Expr::NEG_ONE * count,
118 self.count_weight,
119 );
120 }
121 }
122}
123
124pub struct DummyInteractionChip<SC> {
127 config: Option<SC>,
128 data: Option<DummyInteractionData>,
129 pub air: DummyInteractionAir,
130}
131
132#[derive(Debug, Clone)]
133pub struct DummyInteractionData {
134 pub count: Vec<u32>,
135 pub fields: Vec<Vec<u32>>,
136}
137
138impl<SC> DummyInteractionChip<SC> {
139 pub fn new_without_partition(field_width: usize, is_send: bool, bus_index: BusIndex) -> Self {
140 let air = DummyInteractionAir::new(field_width, is_send, bus_index);
141 Self {
142 config: None,
143 data: None,
144 air,
145 }
146 }
147 pub fn new_with_partition(
148 config: SC,
149 field_width: usize,
150 is_send: bool,
151 bus_index: BusIndex,
152 ) -> Self {
153 let air = DummyInteractionAir::new(field_width, is_send, bus_index).partition();
154 Self {
155 config: Some(config),
156 data: None,
157 air,
158 }
159 }
160 pub fn load_data(&mut self, data: DummyInteractionData) {
161 let DummyInteractionData { count, fields } = &data;
162 let h = count.len();
163 assert_eq!(fields.len(), h);
164 let w = fields[0].len();
165 assert_eq!(self.air.field_width, w);
166 assert!(fields.iter().all(|r| r.len() == w));
167 self.data = Some(data);
168 }
169 pub fn air(&self) -> AirRef<SC>
170 where
171 SC: StarkProtocolConfig,
172 {
173 Arc::new(self.air)
174 }
175}
176
177impl<SC: StarkProtocolConfig> DummyInteractionChip<SC> {
178 pub fn generate_proving_ctx(&self) -> AirProvingContext<CpuColMajorBackend<SC>> {
179 assert!(self.data.is_some());
180 let data = self.data.clone().unwrap();
181 if self.air.partition {
182 self.generate_traces_with_partition(data)
183 } else {
184 let trace = self.generate_traces_without_partition(data);
185 AirProvingContext::simple_no_pis(ColMajorMatrix::from_row_major(&trace))
186 }
187 }
188}
189
190impl<SC: StarkProtocolConfig> DummyInteractionChip<SC> {
191 pub fn generate_traces_with_partition(
192 &self,
193 data: DummyInteractionData,
194 ) -> AirProvingContext<CpuColMajorBackend<SC>> {
195 let DummyInteractionData {
196 mut count,
197 mut fields,
198 } = data;
199 let h = count.len();
200 assert_eq!(fields.len(), h);
201 let w = fields[0].len();
202 assert_eq!(self.air.field_width, w);
203 assert!(fields.iter().all(|r| r.len() == w));
204 let h = h.next_power_of_two();
205 count.resize(h, 0);
206 fields.resize(h, vec![0; w]);
207 let common_main_val: Vec<_> = count.into_iter().map(SC::F::from_u32).collect();
208 let cached_trace_val: Vec<_> = fields.into_iter().flatten().map(SC::F::from_u32).collect();
209 let cached_trace_rm = RowMajorMatrix::new(cached_trace_val, w);
210 let cached_trace = ColMajorMatrix::from_row_major(&cached_trace_rm);
211
212 let config = self.config.as_ref().expect("params required for partition");
213 let params = config.params();
214 let (commit, data) = stacked_commit(
215 config.hasher(),
216 params.l_skip,
217 params.n_stack,
218 params.log_blowup,
219 params.k_whir(),
220 &[&cached_trace],
221 )
222 .unwrap();
223
224 let common_main_rm = RowMajorMatrix::new(common_main_val, 1);
225 AirProvingContext {
226 cached_mains: vec![CommittedTraceData {
227 commitment: commit,
228 trace: cached_trace,
229 data: Arc::new(data),
230 }],
231 common_main: ColMajorMatrix::from_row_major(&common_main_rm),
232 public_values: vec![],
233 }
234 }
235
236 fn generate_traces_without_partition(
237 &self,
238 data: DummyInteractionData,
239 ) -> RowMajorMatrix<SC::F> {
240 let DummyInteractionData { count, fields } = data;
241 let h = count.len();
242 assert_eq!(fields.len(), h);
243 let w = fields[0].len();
244 assert_eq!(self.air.field_width, w);
245 assert!(fields.iter().all(|r| r.len() == w));
246 let common_main_val: Vec<_> = izip!(count, fields)
247 .flat_map(|(count, fields)| iter::once(count).chain(fields))
248 .chain(iter::repeat(0))
249 .take((w + 1) * h.next_power_of_two())
250 .map(SC::F::from_u32)
251 .collect();
252 RowMajorMatrix::new(common_main_val, w + 1)
253 }
254}