openvm_stark_backend/air_builders/debug/
mod.rs
1use std::sync::{Arc, Mutex};
2
3use itertools::{izip, Itertools};
4use p3_air::{
5 AirBuilder, AirBuilderWithPublicValues, ExtensionBuilder, PairBuilder, PermutationAirBuilder,
6};
7use p3_field::FieldAlgebra;
8use p3_matrix::{dense::RowMajorMatrixView, stack::VerticalPair};
9
10use super::{symbolic::SymbolicConstraints, PartitionedAirBuilder, ViewPair};
11use crate::{
12 config::{StarkGenericConfig, Val},
13 interaction::{
14 rap::InteractionPhaseAirBuilder, Interaction, InteractionBuilder, RapPhaseSeqKind,
15 SymbolicInteraction,
16 },
17 keygen::types::StarkProvingKey,
18 rap::{AnyRap, PermutationAirBuilderWithExposedValues},
19};
20
21mod check_constraints;
22
23use check_constraints::*;
24
25use crate::interaction::BusIndex;
26
27thread_local! {
28 pub static USE_DEBUG_BUILDER: Arc<Mutex<bool>> = Arc::new(Mutex::new(true));
29}
30
31#[allow(dead_code)]
35#[allow(clippy::too_many_arguments)]
36pub fn debug_constraints_and_interactions<SC: StarkGenericConfig>(
37 airs: &[Arc<dyn AnyRap<SC>>],
38 pk: &[StarkProvingKey<SC>],
39 main_views_per_air: &[Vec<RowMajorMatrixView<'_, Val<SC>>>],
40 public_values_per_air: &[Vec<Val<SC>>],
41) {
42 USE_DEBUG_BUILDER.with(|debug| {
43 if *debug.lock().unwrap() {
44 let preprocessed = izip!(airs, pk, main_views_per_air, public_values_per_air,)
45 .map(|(rap, pk, main, public_values)| {
46 let preprocessed_trace = pk
47 .preprocessed_data
48 .as_ref()
49 .map(|data| data.trace.as_view());
50 tracing::debug!("Checking constraints for {}", rap.name());
51 check_constraints(
52 rap.as_ref(),
53 &rap.name(),
54 &preprocessed_trace,
55 main,
56 public_values,
57 );
58 preprocessed_trace
59 })
60 .collect_vec();
61
62 let (air_names, interactions): (Vec<_>, Vec<_>) = pk
63 .iter()
64 .map(|pk| {
65 let sym_constraints = SymbolicConstraints::from(&pk.vk.symbolic_constraints);
66 (pk.air_name.clone(), sym_constraints.interactions)
67 })
68 .unzip();
69 check_logup(
70 &air_names,
71 &interactions,
72 &preprocessed,
73 main_views_per_air,
74 public_values_per_air,
75 );
76 }
77 });
78}
79
80pub struct DebugConstraintBuilder<'a, SC: StarkGenericConfig> {
83 pub air_name: &'a str,
84 pub row_index: usize,
85 pub preprocessed: ViewPair<'a, Val<SC>>,
86 pub partitioned_main: Vec<ViewPair<'a, Val<SC>>>,
87 pub after_challenge: Vec<ViewPair<'a, SC::Challenge>>,
88 pub challenges: &'a [Vec<SC::Challenge>],
89 pub is_first_row: Val<SC>,
90 pub is_last_row: Val<SC>,
91 pub is_transition: Val<SC>,
92 pub public_values: &'a [Val<SC>],
93 pub exposed_values_after_challenge: &'a [Vec<SC::Challenge>],
94 pub rap_phase_seq_kind: RapPhaseSeqKind,
95 pub has_common_main: bool,
96}
97
98impl<'a, SC> AirBuilder for DebugConstraintBuilder<'a, SC>
99where
100 SC: StarkGenericConfig,
101{
102 type F = Val<SC>;
103 type Expr = Val<SC>;
104 type Var = Val<SC>;
105 type M = VerticalPair<RowMajorMatrixView<'a, Val<SC>>, RowMajorMatrixView<'a, Val<SC>>>;
106
107 fn main(&self) -> Self::M {
109 if self.partitioned_main.len() == 1 {
110 self.partitioned_main[0]
111 } else {
112 panic!("Main trace is either empty or partitioned. This function should not be used.")
113 }
114 }
115
116 fn is_first_row(&self) -> Self::Expr {
117 self.is_first_row
118 }
119
120 fn is_last_row(&self) -> Self::Expr {
121 self.is_last_row
122 }
123
124 fn is_transition_window(&self, size: usize) -> Self::Expr {
125 if size == 2 {
126 self.is_transition
127 } else {
128 panic!("only supports a window size of 2")
129 }
130 }
131
132 fn assert_zero<I: Into<Self::Expr>>(&mut self, x: I) {
133 assert_eq!(
134 x.into(),
135 Val::<SC>::ZERO,
136 "constraints had nonzero value on air {},row {}",
137 self.air_name,
138 self.row_index
139 );
140 }
141
142 fn assert_eq<I1: Into<Self::Expr>, I2: Into<Self::Expr>>(&mut self, x: I1, y: I2) {
143 let x = x.into();
144 let y = y.into();
145 assert_eq!(
146 x, y,
147 "values didn't match on air {}, row {}: {} != {}",
148 self.air_name, self.row_index, x, y
149 );
150 }
151}
152
153impl<SC> PairBuilder for DebugConstraintBuilder<'_, SC>
154where
155 SC: StarkGenericConfig,
156{
157 fn preprocessed(&self) -> Self::M {
158 self.preprocessed
159 }
160}
161
162impl<SC> ExtensionBuilder for DebugConstraintBuilder<'_, SC>
163where
164 SC: StarkGenericConfig,
165{
166 type EF = SC::Challenge;
167 type ExprEF = SC::Challenge;
168 type VarEF = SC::Challenge;
169
170 fn assert_zero_ext<I>(&mut self, x: I)
171 where
172 I: Into<Self::ExprEF>,
173 {
174 assert_eq!(
175 x.into(),
176 SC::Challenge::ZERO,
177 "constraints had nonzero value on row {}",
178 self.row_index
179 );
180 }
181
182 fn assert_eq_ext<I1, I2>(&mut self, x: I1, y: I2)
183 where
184 I1: Into<Self::ExprEF>,
185 I2: Into<Self::ExprEF>,
186 {
187 let x = x.into();
188 let y = y.into();
189 assert_eq!(
190 x, y,
191 "values didn't match on air {}, row {}: {} != {}",
192 self.air_name, self.row_index, x, y
193 );
194 }
195}
196
197impl<'a, SC> PermutationAirBuilder for DebugConstraintBuilder<'a, SC>
198where
199 SC: StarkGenericConfig,
200{
201 type MP = ViewPair<'a, SC::Challenge>;
202
203 type RandomVar = SC::Challenge;
204
205 fn permutation(&self) -> Self::MP {
206 *self
207 .after_challenge
208 .first()
209 .expect("Challenge phase not supported")
210 }
211
212 fn permutation_randomness(&self) -> &[Self::EF] {
213 self.challenges
214 .first()
215 .expect("Challenge phase not supported")
216 }
217}
218
219impl<SC> AirBuilderWithPublicValues for DebugConstraintBuilder<'_, SC>
220where
221 SC: StarkGenericConfig,
222{
223 type PublicVar = Val<SC>;
224
225 fn public_values(&self) -> &[Self::F] {
226 self.public_values
227 }
228}
229
230impl<SC> PermutationAirBuilderWithExposedValues for DebugConstraintBuilder<'_, SC>
231where
232 SC: StarkGenericConfig,
233{
234 fn permutation_exposed_values(&self) -> &[Self::EF] {
235 self.exposed_values_after_challenge
236 .first()
237 .expect("Challenge phase not supported")
238 }
239}
240
241impl<SC> PartitionedAirBuilder for DebugConstraintBuilder<'_, SC>
242where
243 SC: StarkGenericConfig,
244{
245 fn cached_mains(&self) -> &[Self::M] {
246 let mut num_cached_mains = self.partitioned_main.len();
247 if self.has_common_main {
248 num_cached_mains -= 1;
249 }
250 &self.partitioned_main[..num_cached_mains]
251 }
252 fn common_main(&self) -> &Self::M {
253 assert!(self.has_common_main, "AIR doesn't have a common main trace");
254 self.partitioned_main.last().unwrap()
255 }
256}
257
258impl<SC> InteractionBuilder for DebugConstraintBuilder<'_, SC>
260where
261 SC: StarkGenericConfig,
262{
263 fn push_interaction<E: Into<Self::Expr>>(
264 &mut self,
265 _bus_index: BusIndex,
266 _fields: impl IntoIterator<Item = E>,
267 _count: impl Into<Self::Expr>,
268 _count_weight: u32,
269 ) {
270 }
272
273 fn num_interactions(&self) -> usize {
274 0
275 }
276
277 fn all_interactions(&self) -> &[Interaction<Self::Expr>] {
278 &[]
279 }
280}
281
282impl<SC: StarkGenericConfig> InteractionPhaseAirBuilder for DebugConstraintBuilder<'_, SC> {
284 fn finalize_interactions(&mut self) {}
285
286 fn max_constraint_degree(&self) -> usize {
287 0
288 }
289
290 fn rap_phase_seq_kind(&self) -> RapPhaseSeqKind {
291 self.rap_phase_seq_kind
292 }
293
294 fn symbolic_interactions(&self) -> Vec<SymbolicInteraction<Val<SC>>> {
295 vec![]
296 }
297}