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 =
95 format!("{} {} {}", interaction.scope, interaction.kind, vec_to_string(values));
96 key_to_vec_data.entry(key.clone()).or_insert_with(Vec::new).push(InteractionData {
97 chip_name: chip.name().to_string(),
98 kind: interaction.kind,
99 row,
100 interaction_number: m,
101 is_send,
102 multiplicity: multiplicity_eval,
103 });
104 let current = key_to_count.entry(key.clone()).or_insert(F::zero());
105 if is_send {
106 *current += multiplicity_eval;
107 } else {
108 *current -= multiplicity_eval;
109 }
110 }
111 }
112 }
113
114 (key_to_vec_data, key_to_count)
115}
116
117#[allow(clippy::needless_pass_by_value)]
120pub fn debug_interactions_with_all_chips<F, A>(
121 chips: &[Chip<F, A>],
122 preprocessed_traces: &Traces<F, CpuBackend>,
124 traces: &Traces<F, CpuBackend>,
126 public_values: Vec<F>,
127 interaction_kinds: Vec<InteractionKind>,
128 scope: InteractionScope,
129) -> bool
130where
131 F: Field,
132 A: MachineAir<F>,
133{
134 let mut final_map = BTreeMap::new();
135 let mut total = F::zero();
136
137 for chip in chips.iter() {
138 let mut total_events = 0;
139
140 let (_, count) = debug_interactions::<F, A>(
141 chip,
142 preprocessed_traces,
143 traces,
144 interaction_kinds.clone(),
145 scope,
146 );
147 total_events += count.len();
148 for (key, value) in count.iter() {
149 let entry = final_map.entry(key.clone()).or_insert((F::zero(), BTreeMap::new()));
150 entry.0 += *value;
151 total += *value;
152 *entry.1.entry(chip.name().to_string()).or_insert(F::zero()) += *value;
153 }
154
155 tracing::info!("{} chip has {} distinct events", chip.name(), total_events);
156 }
157
158 let mut folder = DebugPublicValuesConstraintFolder::<F> {
159 perm_challenges: (&F::zero(), &[]),
160 alpha: F::zero(),
161 accumulator: F::zero(),
162 interactions: vec![],
163 public_values: &public_values,
164 _marker: PhantomData,
165 };
166 A::Record::eval_public_values(&mut folder);
167
168 for (kind, scope, values, multiplicity) in folder.interactions.iter() {
169 let key = format!("{} {} {}", scope, kind, vec_to_string(values.clone()));
170 let entry = final_map.entry(key.clone()).or_insert((F::zero(), BTreeMap::new()));
171 entry.0 += *multiplicity;
172 total += *multiplicity;
173 *entry.1.entry("EvalPublicValues".to_string()).or_insert(F::zero()) += *multiplicity;
174 }
175
176 tracing::info!("Final counts below.");
177 tracing::info!("==================");
178
179 let mut any_nonzero = false;
180 for (key, (value, chip_values)) in final_map.clone() {
181 if !F::is_zero(&value) {
182 tracing::info!("Interaction key: {} Send-Receive Discrepancy: {}", key, value);
183 any_nonzero = true;
184 for (chip, chip_value) in chip_values {
185 tracing::info!(
186 " {} chip's send-receive discrepancy for this key is {}",
187 chip,
188 chip_value
189 );
190 }
191 }
192 }
193
194 tracing::info!("==================");
195 if !any_nonzero {
196 tracing::info!("All chips have the same number of sends and receives.");
197 }
198
199 !any_nonzero
200}