sp1_core_machine/utils/
mod.rs

1pub mod concurrency;
2mod logger;
3mod prove;
4mod span;
5mod test;
6pub mod uni_stark;
7
8pub use logger::*;
9use p3_field::Field;
10pub use prove::*;
11use sp1_curves::params::Limbs;
12pub use span::*;
13pub use test::*;
14pub use uni_stark::*;
15
16use crate::memory::MemoryCols;
17
18use generic_array::ArrayLength;
19use p3_maybe_rayon::prelude::{ParallelBridge, ParallelIterator};
20
21pub use sp1_primitives::consts::{
22    bytes_to_words_le, bytes_to_words_le_vec, num_to_comma_separated, words_to_bytes_le,
23    words_to_bytes_le_vec,
24};
25
26pub const fn indices_arr<const N: usize>() -> [usize; N] {
27    let mut indices_arr = [0; N];
28    let mut i = 0;
29    while i < N {
30        indices_arr[i] = i;
31        i += 1;
32    }
33    indices_arr
34}
35
36pub fn pad_to_power_of_two<const N: usize, T: Clone + Default>(values: &mut Vec<T>) {
37    debug_assert!(values.len().is_multiple_of(N));
38    let mut n_real_rows = values.len() / N;
39    if n_real_rows < 16 {
40        n_real_rows = 16;
41    }
42    values.resize(n_real_rows.next_power_of_two() * N, T::default());
43}
44
45pub fn limbs_from_prev_access<T: Copy, N: ArrayLength, M: MemoryCols<T>>(
46    cols: &[M],
47) -> Limbs<T, N> {
48    let vec = cols.iter().flat_map(|access| access.prev_value().0).collect::<Vec<T>>();
49
50    let sized = vec.try_into().unwrap_or_else(|_| panic!("failed to convert to limbs"));
51    Limbs(sized)
52}
53
54pub fn limbs_from_access<T: Copy, N: ArrayLength, M: MemoryCols<T>>(cols: &[M]) -> Limbs<T, N> {
55    let vec = cols.iter().flat_map(|access| access.value().0).collect::<Vec<T>>();
56
57    let sized = vec.try_into().unwrap_or_else(|_| panic!("failed to convert to limbs"));
58    Limbs(sized)
59}
60
61/// Pad to a power of two, with an option to specify the power.
62//
63// The `rows` argument represents the rows of a matrix stored in row-major order. The function will
64// pad the rows using `row_fn` to create the padded rows. The padding will be to the next power of
65// of two of `size_log_2` is `None`, or to the specified `size_log_2` if it is not `None`. The
66// function will panic of the number of rows is larger than the specified `size_log2`
67pub fn pad_rows_fixed<R: Clone>(
68    rows: &mut Vec<R>,
69    row_fn: impl Fn() -> R,
70    size_log2: Option<usize>,
71) {
72    let nb_rows = rows.len();
73    let dummy_row = row_fn();
74    rows.resize(next_power_of_two(nb_rows, size_log2), dummy_row);
75}
76
77/// Returns the next power of two that is >= `n` and >= 16. If `fixed_power` is set, it will return
78/// `2^fixed_power` after checking that `n <= 2^fixed_power`.
79pub fn next_power_of_two(n: usize, fixed_power: Option<usize>) -> usize {
80    match fixed_power {
81        Some(power) => {
82            let padded_nb_rows = 1 << power;
83            if n * 2 < padded_nb_rows {
84                tracing::debug!(
85                    "fixed log2 rows can be potentially reduced: got {}, expected {}",
86                    n,
87                    padded_nb_rows
88                );
89            }
90            if n > padded_nb_rows {
91                panic!("fixed log2 rows is too small: got {n}, expected {padded_nb_rows}");
92            }
93            padded_nb_rows
94        }
95        None => {
96            let mut padded_nb_rows = n.next_power_of_two();
97            if padded_nb_rows < 16 {
98                padded_nb_rows = 16;
99            }
100            padded_nb_rows
101        }
102    }
103}
104
105pub fn chunk_vec<T>(mut vec: Vec<T>, chunk_size: usize) -> Vec<Vec<T>> {
106    let mut result = Vec::new();
107    while !vec.is_empty() {
108        let current_chunk_size = std::cmp::min(chunk_size, vec.len());
109        let current_chunk = vec.drain(..current_chunk_size).collect::<Vec<T>>();
110        result.push(current_chunk);
111    }
112    result
113}
114
115#[inline]
116pub fn log2_strict_usize(n: usize) -> usize {
117    let res = n.trailing_zeros();
118    assert_eq!(n.wrapping_shr(res), 1, "Not a power of two: {n}");
119    res as usize
120}
121
122pub fn par_for_each_row<P, F>(vec: &mut [F], num_elements_per_event: usize, processor: P)
123where
124    F: Send,
125    P: Fn(usize, &mut [F]) + Send + Sync,
126{
127    // Split the vector into `num_cpus` chunks, but at least `num_cpus` rows per chunk.
128    assert!(vec.len().is_multiple_of(num_elements_per_event));
129    let len = vec.len() / num_elements_per_event;
130    let cpus = num_cpus::get();
131    let ceil_div = len.div_ceil(cpus);
132    let chunk_size = std::cmp::max(ceil_div, cpus);
133
134    vec.chunks_mut(chunk_size * num_elements_per_event).enumerate().par_bridge().for_each(
135        |(i, chunk)| {
136            chunk.chunks_mut(num_elements_per_event).enumerate().for_each(|(j, row)| {
137                assert!(row.len() == num_elements_per_event);
138                processor(i * chunk_size + j, row);
139            });
140        },
141    );
142}
143
144/// Returns whether the `SP1_DEBUG` environment variable is enabled or disabled.
145///
146/// This variable controls whether backtraces are attached to compiled circuit programs, as well
147/// as whether cycle tracking is performed for circuit programs.
148///
149/// By default, the variable is disabled.
150pub fn sp1_debug_mode() -> bool {
151    let value = std::env::var("SP1_DEBUG").unwrap_or_else(|_| "false".to_string());
152    value == "1" || value.to_lowercase() == "true"
153}
154
155/// Returns a vector of zeros of the given length. This is faster than vec![F::zero(); len] which
156/// requires copying.
157///
158/// This function is safe to use only for fields that can be transmuted from 0u32.
159pub fn zeroed_f_vec<F: Field>(len: usize) -> Vec<F> {
160    debug_assert!(std::mem::size_of::<F>() == 4);
161
162    let vec = vec![0u32; len];
163    unsafe { std::mem::transmute::<Vec<u32>, Vec<F>>(vec) }
164}