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 = 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/// Calculate the number of times we send and receive each event of the given interaction type,
122/// and print out the ones for which the set of sends and receives don't match.
123#[allow(clippy::needless_pass_by_value)]
124pub fn debug_interactions_with_all_chips<F, A>(
125    chips: &[Chip<F, A>],
126    // pkey: &MachineProvingKey<PC>,
127    preprocessed_traces: &Traces<F, CpuBackend>,
128    // shards: &[A::Record],
129    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}