Skip to main content

sp1_hypercube/lookup/
debug.rs

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/// The data for an interaction.
14#[derive(Debug)]
15pub struct InteractionData<F: Field> {
16    /// The chip name.
17    pub chip_name: String,
18    /// The kind of interaction.
19    pub kind: InteractionKind,
20    /// The row of the interaction.
21    pub row: usize,
22    /// The interaction number.
23    pub interaction_number: usize,
24    /// Whether the interaction is a send.
25    pub is_send: bool,
26    /// The multiplicity of the interaction.
27    pub multiplicity: F,
28}
29
30/// Converts a vector of field elements to a string.
31#[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/// Debugs the interactions of a chip.
46#[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/// Calculate the number of times we send and receive each event of the given interaction type,
118/// and print out the ones for which the set of sends and receives don't match.
119#[allow(clippy::needless_pass_by_value)]
120pub fn debug_interactions_with_all_chips<F, A>(
121    chips: &[Chip<F, A>],
122    // pkey: &MachineProvingKey<PC>,
123    preprocessed_traces: &Traces<F, CpuBackend>,
124    // shards: &[A::Record],
125    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}