1use crate::{
2 air::InteractionScope, prover::Traces, record::MachineRecord, Chip,
3 DebugPublicValuesConstraintFolder,
4};
5use slop_algebra::Field;
6use slop_alloc::CpuBackend;
7use std::{collections::BTreeMap, marker::PhantomData};
8
9use super::InteractionKind;
10
11use crate::air::MachineAir;
12
13#[derive(Debug)]
15pub struct InteractionData<F: Field> {
16 pub chip_name: String,
18 pub kind: InteractionKind,
20 pub row: usize,
22 pub interaction_number: usize,
24 pub is_send: bool,
26 pub multiplicity: F,
28}
29
30#[allow(clippy::needless_pass_by_value)]
32#[must_use]
33pub fn vec_to_string<F: Field>(vec: Vec<F>) -> String {
34 let mut result = String::from("(");
35 for (i, value) in vec.iter().enumerate() {
36 if i != 0 {
37 result.push_str(", ");
38 }
39 result.push_str(&value.to_string());
40 }
41 result.push(')');
42 result
43}
44
45#[allow(clippy::type_complexity)]
47#[allow(clippy::needless_pass_by_value)]
48pub fn debug_interactions<F: Field, A: MachineAir<F>>(
49 chip: &Chip<F, A>,
50 preprocessed_traces: &Traces<F, CpuBackend>,
51 traces: &Traces<F, CpuBackend>,
52 interaction_kinds: Vec<InteractionKind>,
53 scope: InteractionScope,
54) -> (BTreeMap<String, Vec<InteractionData<F>>>, BTreeMap<String, F>) {
55 let mut key_to_vec_data = BTreeMap::new();
56 let mut key_to_count = BTreeMap::new();
57
58 let main = traces.get(chip.name()).cloned().unwrap();
59 let pre_traces = preprocessed_traces.get(chip.name()).cloned();
60
61 let height = main.clone().num_real_entries();
62
63 let sends = chip.sends().iter().filter(|s| s.scope == scope).collect::<Vec<_>>();
64 let receives = chip.receives().iter().filter(|r| r.scope == scope).collect::<Vec<_>>();
65
66 let nb_send_interactions = sends.len();
67 for row in 0..height {
68 for (m, interaction) in sends.iter().chain(receives.iter()).enumerate() {
69 if !interaction_kinds.contains(&interaction.kind) {
70 continue;
71 }
72 let empty = vec![];
73 let preprocessed_row = match pre_traces {
74 Some(ref t) => t
75 .inner()
76 .as_ref()
77 .map_or(empty.as_slice(), |t| t.guts().get(row).unwrap().as_slice()),
78 None => empty.as_slice(),
79 };
80
81 let is_send = m < nb_send_interactions;
82
83 let main_row =
84 main.inner().as_ref().unwrap().guts().get(row).unwrap().as_slice().to_vec();
85
86 let multiplicity_eval: F = interaction.multiplicity.apply(preprocessed_row, &main_row);
87
88 if !multiplicity_eval.is_zero() {
89 let mut values = vec![];
90 for value in &interaction.values {
91 let expr: F = value.apply(preprocessed_row, &main_row);
92 values.push(expr);
93 }
94 let key = format!(
95 "{} {} {}",
96 &interaction.scope.to_string(),
97 &interaction.kind.to_string(),
98 vec_to_string(values)
99 );
100 key_to_vec_data.entry(key.clone()).or_insert_with(Vec::new).push(InteractionData {
101 chip_name: chip.name().to_string(),
102 kind: interaction.kind,
103 row,
104 interaction_number: m,
105 is_send,
106 multiplicity: multiplicity_eval,
107 });
108 let current = key_to_count.entry(key.clone()).or_insert(F::zero());
109 if is_send {
110 *current += multiplicity_eval;
111 } else {
112 *current -= multiplicity_eval;
113 }
114 }
115 }
116 }
117
118 (key_to_vec_data, key_to_count)
119}
120
121#[allow(clippy::needless_pass_by_value)]
124pub fn debug_interactions_with_all_chips<F, A>(
125 chips: &[Chip<F, A>],
126 preprocessed_traces: &Traces<F, CpuBackend>,
128 traces: &Traces<F, CpuBackend>,
130 public_values: Vec<F>,
131 interaction_kinds: Vec<InteractionKind>,
132 scope: InteractionScope,
133) -> bool
134where
135 F: Field,
136 A: MachineAir<F>,
137{
138 let mut final_map = BTreeMap::new();
139 let mut total = F::zero();
140
141 for chip in chips.iter() {
142 let mut total_events = 0;
143
144 let (_, count) = debug_interactions::<F, A>(
145 chip,
146 preprocessed_traces,
147 traces,
148 interaction_kinds.clone(),
149 scope,
150 );
151 total_events += count.len();
152 for (key, value) in count.iter() {
153 let entry = final_map.entry(key.clone()).or_insert((F::zero(), BTreeMap::new()));
154 entry.0 += *value;
155 total += *value;
156 *entry.1.entry(chip.name().to_string()).or_insert(F::zero()) += *value;
157 }
158
159 tracing::info!("{} chip has {} distinct events", chip.name(), total_events);
160 }
161
162 let mut folder = DebugPublicValuesConstraintFolder::<F> {
163 perm_challenges: (&F::zero(), &[]),
164 alpha: F::zero(),
165 accumulator: F::zero(),
166 interactions: vec![],
167 public_values: &public_values,
168 _marker: PhantomData,
169 };
170 A::Record::eval_public_values(&mut folder);
171
172 for (kind, scope, values, multiplicity) in folder.interactions.iter() {
173 let key = format!(
174 "{} {} {}",
175 &scope.to_string(),
176 &kind.to_string(),
177 vec_to_string(values.clone())
178 );
179 let entry = final_map.entry(key.clone()).or_insert((F::zero(), BTreeMap::new()));
180 entry.0 += *multiplicity;
181 total += *multiplicity;
182 *entry.1.entry("EvalPublicValues".to_string()).or_insert(F::zero()) += *multiplicity;
183 }
184
185 tracing::info!("Final counts below.");
186 tracing::info!("==================");
187
188 let mut any_nonzero = false;
189 for (key, (value, chip_values)) in final_map.clone() {
190 if !F::is_zero(&value) {
191 tracing::info!("Interaction key: {} Send-Receive Discrepancy: {}", key, value);
192 any_nonzero = true;
193 for (chip, chip_value) in chip_values {
194 tracing::info!(
195 " {} chip's send-receive discrepancy for this key is {}",
196 chip,
197 chip_value
198 );
199 }
200 }
201 }
202
203 tracing::info!("==================");
204 if !any_nonzero {
205 tracing::info!("All chips have the same number of sends and receives.");
206 }
207
208 !any_nonzero
209}