sp1_stark/lookup/
debug.rs1use p3_baby_bear::BabyBear;
2use p3_field::{AbstractField, Field, PrimeField32, PrimeField64};
3use p3_matrix::Matrix;
4use std::collections::BTreeMap;
5
6use super::InteractionKind;
7use crate::{
8 air::{InteractionScope, MachineAir},
9 MachineChip, StarkGenericConfig, StarkMachine, StarkProvingKey, Val,
10};
11
12#[derive(Debug)]
14pub struct InteractionData<F: Field> {
15 pub chip_name: String,
17 pub kind: InteractionKind,
19 pub row: usize,
21 pub interaction_number: usize,
23 pub is_send: bool,
25 pub multiplicity: F,
27}
28
29#[allow(clippy::needless_pass_by_value)]
31#[must_use]
32pub fn vec_to_string<F: Field>(vec: Vec<F>) -> String {
33 let mut result = String::from("(");
34 for (i, value) in vec.iter().enumerate() {
35 if i != 0 {
36 result.push_str(", ");
37 }
38 result.push_str(&value.to_string());
39 }
40 result.push(')');
41 result
42}
43
44fn field_to_int<F: PrimeField32>(x: F) -> i32 {
49 let modulus = BabyBear::ORDER_U64;
50 let val = x.as_canonical_u64();
51 if val > modulus / 2 {
52 val as i32 - modulus as i32
53 } else {
54 val as i32
55 }
56}
57
58#[allow(clippy::type_complexity)]
60#[allow(clippy::needless_pass_by_value)]
61pub fn debug_interactions<SC: StarkGenericConfig, A: MachineAir<Val<SC>>>(
62 chip: &MachineChip<SC, A>,
63 pkey: &StarkProvingKey<SC>,
64 record: &A::Record,
65 interaction_kinds: Vec<InteractionKind>,
66 scope: InteractionScope,
67) -> (BTreeMap<String, Vec<InteractionData<Val<SC>>>>, BTreeMap<String, Val<SC>>) {
68 let mut key_to_vec_data = BTreeMap::new();
69 let mut key_to_count = BTreeMap::new();
70
71 let trace = chip.generate_trace(record, &mut A::Record::default());
72 let mut pre_traces = pkey.traces.clone();
73 let mut preprocessed_trace =
74 pkey.chip_ordering.get(&chip.name()).map(|&index| pre_traces.get_mut(index).unwrap());
75 let mut main = trace.clone();
76 let height = trace.clone().height();
77
78 let sends = chip.sends().iter().filter(|s| s.scope == scope).collect::<Vec<_>>();
79 let receives = chip.receives().iter().filter(|r| r.scope == scope).collect::<Vec<_>>();
80
81 let nb_send_interactions = sends.len();
82 for row in 0..height {
83 for (m, interaction) in sends.iter().chain(receives.iter()).enumerate() {
84 if !interaction_kinds.contains(&interaction.kind) {
85 continue;
86 }
87 let mut empty = vec![];
88 let preprocessed_row = preprocessed_trace
89 .as_mut()
90 .map(|t| t.row_mut(row))
91 .or_else(|| Some(&mut empty))
92 .unwrap();
93 let is_send = m < nb_send_interactions;
94 let multiplicity_eval: Val<SC> =
95 interaction.multiplicity.apply(preprocessed_row, main.row_mut(row));
96
97 if !multiplicity_eval.is_zero() {
98 let mut values = vec![];
99 for value in &interaction.values {
100 let expr: Val<SC> = value.apply(preprocessed_row, main.row_mut(row));
101 values.push(expr);
102 }
103 let key = format!(
104 "{} {} {}",
105 &interaction.scope.to_string(),
106 &interaction.kind.to_string(),
107 vec_to_string(values)
108 );
109 key_to_vec_data.entry(key.clone()).or_insert_with(Vec::new).push(InteractionData {
110 chip_name: chip.name(),
111 kind: interaction.kind,
112 row,
113 interaction_number: m,
114 is_send,
115 multiplicity: multiplicity_eval,
116 });
117 let current = key_to_count.entry(key.clone()).or_insert(Val::<SC>::zero());
118 if is_send {
119 *current += multiplicity_eval;
120 } else {
121 *current -= multiplicity_eval;
122 }
123 }
124 }
125 }
126
127 (key_to_vec_data, key_to_count)
128}
129
130#[allow(clippy::needless_pass_by_value)]
133pub fn debug_interactions_with_all_chips<SC, A>(
134 machine: &StarkMachine<SC, A>,
135 pkey: &StarkProvingKey<SC>,
136 shards: &[A::Record],
137 interaction_kinds: Vec<InteractionKind>,
138 scope: InteractionScope,
139) -> bool
140where
141 SC: StarkGenericConfig,
142 SC::Val: PrimeField32,
143 A: MachineAir<SC::Val>,
144{
145 if scope == InteractionScope::Local {
146 assert!(shards.len() == 1);
147 }
148
149 let mut final_map = BTreeMap::new();
150 let mut total = SC::Val::zero();
151
152 let chips = machine.chips();
153 for chip in chips.iter() {
154 let mut total_events = 0;
155 for shard in shards {
156 if !chip.included(shard) {
157 continue;
158 }
159 eprintln!("{}", chip.name());
160 let (_, count) =
161 debug_interactions::<SC, A>(chip, pkey, shard, interaction_kinds.clone(), scope);
162 total_events += count.len();
163 for (key, value) in count.iter() {
164 let entry =
165 final_map.entry(key.clone()).or_insert((SC::Val::zero(), BTreeMap::new()));
166 entry.0 += *value;
167 total += *value;
168 *entry.1.entry(chip.name()).or_insert(SC::Val::zero()) += *value;
169 }
170 }
171 tracing::info!("{} chip has {} distinct events", chip.name(), total_events);
172 }
173
174 tracing::info!("Final counts below.");
175 tracing::info!("==================");
176
177 let mut any_nonzero = false;
178 for (key, (value, chip_values)) in final_map.clone() {
179 if !Val::<SC>::is_zero(&value) {
180 tracing::info!(
181 "Interaction key: {} Send-Receive Discrepancy: {}",
182 key,
183 field_to_int(value)
184 );
185 any_nonzero = true;
186 for (chip, chip_value) in chip_values {
187 tracing::info!(
188 " {} chip's send-receive discrepancy for this key is {}",
189 chip,
190 field_to_int(chip_value)
191 );
192 }
193 }
194 }
195
196 tracing::info!("==================");
197 if !any_nonzero {
198 tracing::info!("All chips have the same number of sends and receives.");
199 } else {
200 tracing::info!("Positive values mean sent more than received.");
201 tracing::info!("Negative values mean received more than sent.");
202 if total != SC::Val::zero() {
203 tracing::info!("Total send-receive discrepancy: {}", field_to_int(total));
204 if field_to_int(total) > 0 {
205 tracing::info!("you're sending more than you are receiving");
206 } else {
207 tracing::info!("you're receiving more than you are sending");
208 }
209 } else {
210 tracing::info!(
211 "the total number of sends and receives match, but the keys don't match"
212 );
213 tracing::info!("check the arguments");
214 }
215 }
216
217 !any_nonzero
218}