#[cfg(test)]
use alloc::vec::Vec;
use miden_air::{
MIDEN_AIR_COUNT, PcsParams, memory,
trace::{MIN_TRACE_LEN, eidos_compression::EIDOS_COMPRESSION_CYCLE_LEN},
};
use super::chiplets::Chiplets;
use crate::{Felt, ONE};
#[cfg(test)]
use crate::{operation::Operation, utils::ToElements};
#[derive(Debug)]
pub struct RowMajorTraceWriter<'a, E> {
data: &'a mut [E],
payload: usize,
stride: usize,
}
impl<'a, E: Copy> RowMajorTraceWriter<'a, E> {
#[cfg(test)]
pub(super) fn new(data: &'a mut [E], width: usize) -> Self {
Self::with_stride(data, width, width)
}
pub fn with_stride(data: &'a mut [E], payload: usize, stride: usize) -> Self {
debug_assert!(stride >= payload, "stride must be >= payload");
debug_assert_eq!(data.len() % stride, 0, "buffer length must be a multiple of stride");
Self { data, payload, stride }
}
#[inline(always)]
pub fn write_row(&mut self, row: usize, values: &[E]) {
debug_assert_eq!(values.len(), self.payload);
let start = row * self.stride;
self.data[start..start + self.payload].copy_from_slice(values);
}
}
pub struct ChipletTraceFragment<'a> {
band: &'a mut [Felt],
stride: usize,
col_start: usize,
num_rows: usize,
num_cols: usize,
row_offset: usize,
prefix_one_cols: &'static [usize],
}
impl<'a> ChipletTraceFragment<'a> {
pub fn row_major(
band: &'a mut [Felt],
stride: usize,
col_start: usize,
num_cols: usize,
) -> Self {
Self::with_overheads(band, stride, col_start, num_cols, 0, &[])
}
pub fn with_overheads(
band: &'a mut [Felt],
stride: usize,
col_start: usize,
num_cols: usize,
row_offset: usize,
prefix_one_cols: &'static [usize],
) -> Self {
debug_assert_eq!(band.len() % stride, 0, "band length must be a multiple of stride");
debug_assert!(col_start + num_cols <= stride, "column band overruns the row stride");
debug_assert!(
prefix_one_cols.iter().all(|&c| c < col_start),
"prefix_one_cols must lie before col_start",
);
let num_rows = band.len() / stride;
Self {
band,
stride,
col_start,
num_rows,
num_cols,
row_offset,
prefix_one_cols,
}
}
pub fn width(&self) -> usize {
self.num_cols
}
pub fn len(&self) -> usize {
self.num_rows
}
pub fn prefix_mut(&mut self, row: usize) -> &mut [Felt] {
let row_start = row * self.stride;
&mut self.band[row_start..row_start + self.col_start]
}
pub fn copy_rows_from(&mut self, src: &[Felt]) {
debug_assert_eq!(src.len(), self.num_rows * self.num_cols, "source buffer size mismatch");
self.copy_rows_into(0, src);
}
pub fn copy_rows_into(&mut self, row_offset: usize, src: &[Felt]) {
debug_assert_eq!(src.len() % self.num_cols, 0, "source buffer size not row-aligned");
let chunk_rows = src.len() / self.num_cols;
debug_assert!(
row_offset + chunk_rows <= self.num_rows,
"chunk overruns fragment row range",
);
let write_chip_clk = self.stride > self.col_start + self.num_cols;
let clk_col = self.stride - 1;
for r in 0..chunk_rows {
let dst_row = row_offset + r;
let row_start = dst_row * self.stride;
let row = &mut self.band[row_start..row_start + self.stride];
for &col in self.prefix_one_cols {
row[col] = ONE;
}
row[self.col_start..self.col_start + self.num_cols]
.copy_from_slice(&src[r * self.num_cols..(r + 1) * self.num_cols]);
if write_chip_clk {
row[clk_col] = Felt::from_u32((self.row_offset + dst_row + 1) as u32);
}
}
}
}
#[derive(Debug, Default, Eq, PartialEq, Clone, Copy)]
pub struct TraceLenSummary {
core_rows: usize,
chiplets: ChipletsLengths,
eidos_compression_rows: usize,
byte_pair_lookup_rows: usize,
padded_heights: Option<[usize; MIDEN_AIR_COUNT]>,
}
impl TraceLenSummary {
pub fn new(
core_rows: usize,
chiplets: ChipletsLengths,
eidos_compression_rows: usize,
byte_pair_lookup_rows: usize,
) -> Self {
TraceLenSummary {
core_rows,
chiplets,
eidos_compression_rows,
byte_pair_lookup_rows,
padded_heights: None,
}
}
pub fn new_with_padded(
core_rows: usize,
chiplets: ChipletsLengths,
eidos_compression_rows: usize,
byte_pair_lookup_rows: usize,
padded_heights: [usize; MIDEN_AIR_COUNT],
) -> Self {
TraceLenSummary {
core_rows,
chiplets,
eidos_compression_rows,
byte_pair_lookup_rows,
padded_heights: Some(padded_heights),
}
}
pub fn core_rows(&self) -> usize {
self.core_rows
}
pub fn chiplets(&self) -> ChipletsLengths {
self.chiplets
}
pub fn chiplets_rows(&self) -> usize {
self.chiplets.trace_len()
}
pub fn eidos_compression_rows(&self) -> usize {
self.eidos_compression_rows
}
pub fn eidos_compression_count(&self) -> usize {
debug_assert_eq!(self.eidos_compression_rows % EIDOS_COMPRESSION_CYCLE_LEN, 0);
self.eidos_compression_rows / EIDOS_COMPRESSION_CYCLE_LEN
}
pub fn byte_pair_lookup_rows(&self) -> usize {
self.byte_pair_lookup_rows
}
pub fn trace_len(&self) -> usize {
self.core_rows
.max(self.chiplets_rows())
.max(self.eidos_compression_rows)
.max(self.byte_pair_lookup_rows)
}
pub fn core_height(&self) -> usize {
padded_height(self.core_rows)
}
pub fn chiplets_height(&self) -> usize {
padded_height(self.chiplets_rows())
}
pub fn eidos_compression_height(&self) -> usize {
padded_height(self.eidos_compression_rows)
}
pub fn padded_trace_len(&self) -> usize {
self.padded_heights
.map(|heights| heights.into_iter().max().expect("heights is non-empty"))
.unwrap_or_else(|| {
self.core_height()
.max(self.chiplets_height())
.max(self.eidos_compression_height())
.max(self.byte_pair_lookup_rows)
})
}
pub fn padded_heights(&self) -> Option<&[usize; MIDEN_AIR_COUNT]> {
self.padded_heights.as_ref()
}
pub fn prover_memory_bytes(&self, params: &PcsParams) -> Option<u64> {
memory::prover_peak_bytes(self.padded_heights()?, params)
}
pub fn padding_percentage(&self) -> usize {
(self.padded_trace_len() - self.trace_len()) * 100 / self.padded_trace_len()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn trace_len_summary_reports_eidos_compression_count() {
let summary =
TraceLenSummary::new(0, ChipletsLengths::default(), 3 * EIDOS_COMPRESSION_CYCLE_LEN, 0);
assert_eq!(summary.eidos_compression_rows(), 3 * EIDOS_COMPRESSION_CYCLE_LEN);
assert_eq!(summary.eidos_compression_count(), 3);
}
#[test]
fn trace_len_summary_records_four_air_heights_in_instance_order() {
let heights = [MIN_TRACE_LEN, 2 * MIN_TRACE_LEN, 4 * MIN_TRACE_LEN, 1 << 16];
let summary = TraceLenSummary::new_with_padded(
17,
ChipletsLengths::from_parts(19, 0, 0, 0, 0),
3 * EIDOS_COMPRESSION_CYCLE_LEN,
1 << 16,
heights,
);
assert_eq!(summary.core_rows(), 17);
assert_eq!(summary.chiplets_rows(), 20);
assert_eq!(summary.eidos_compression_rows(), 3 * EIDOS_COMPRESSION_CYCLE_LEN);
assert_eq!(summary.byte_pair_lookup_rows(), 1 << 16);
assert_eq!(summary.padded_heights(), Some(&heights));
assert_eq!(summary.padded_trace_len(), 1 << 16);
let params = miden_air::config::pcs_params();
assert_eq!(
summary.prover_memory_bytes(¶ms),
memory::prover_peak_bytes(&heights, ¶ms)
);
}
}
fn padded_height(rows: usize) -> usize {
rows.next_power_of_two().max(MIN_TRACE_LEN)
}
#[derive(Default, Clone, Copy, Debug, PartialEq, Eq)]
pub struct ChipletsLengths {
hash_chiplet_len: usize,
bitwise_chiplet_len: usize,
memory_chiplet_len: usize,
ace_chiplet_len: usize,
kernel_rom_len: usize,
}
impl ChipletsLengths {
pub fn new(chiplets: &Chiplets) -> Self {
ChipletsLengths {
hash_chiplet_len: chiplets.bitwise_start().into(),
bitwise_chiplet_len: chiplets.memory_start() - chiplets.bitwise_start(),
memory_chiplet_len: chiplets.ace_start() - chiplets.memory_start(),
ace_chiplet_len: chiplets.kernel_rom_start() - chiplets.ace_start(),
kernel_rom_len: chiplets.padding_start() - chiplets.kernel_rom_start(),
}
}
pub fn from_parts(
hash_len: usize,
bitwise_len: usize,
memory_len: usize,
ace_len: usize,
kernel_len: usize,
) -> Self {
ChipletsLengths {
hash_chiplet_len: hash_len,
bitwise_chiplet_len: bitwise_len,
memory_chiplet_len: memory_len,
ace_chiplet_len: ace_len,
kernel_rom_len: kernel_len,
}
}
pub fn hash_chiplet_len(&self) -> usize {
self.hash_chiplet_len
}
pub fn bitwise_chiplet_len(&self) -> usize {
self.bitwise_chiplet_len
}
pub fn memory_chiplet_len(&self) -> usize {
self.memory_chiplet_len
}
pub fn ace_chiplet_len(&self) -> usize {
self.ace_chiplet_len
}
pub fn kernel_rom_len(&self) -> usize {
self.kernel_rom_len
}
pub fn trace_len(&self) -> usize {
self.hash_chiplet_len()
+ self.bitwise_chiplet_len()
+ self.memory_chiplet_len()
+ self.ace_chiplet_len()
+ self.kernel_rom_len()
+ 1
}
}
pub(crate) fn split_element_u32_into_u16(value: Felt) -> (Felt, Felt) {
let (hi, lo) = split_u32_into_u16(value.as_canonical_u64());
(Felt::new_unchecked(hi as u64), Felt::new_unchecked(lo as u64))
}
pub(crate) fn split_u32_into_u16(value: u64) -> (u16, u16) {
const U32MAX: u64 = u32::MAX as u64;
debug_assert!(value <= U32MAX, "not a 32-bit value");
let lo = value as u16;
let hi = (value >> 16) as u16;
(hi, lo)
}
#[cfg(test)]
pub(super) fn build_span_with_respan_ops() -> (Vec<Operation>, Vec<Felt>) {
let iv = [1, 3, 5, 7, 9, 11, 13, 15, 17].to_elements();
let ops = alloc::vec![
Operation::Push(iv[0]),
Operation::Push(iv[1]),
Operation::Push(iv[2]),
Operation::Push(iv[3]),
Operation::Push(iv[4]),
Operation::Push(iv[5]),
Operation::Push(iv[6]),
Operation::Push(iv[7]),
Operation::Push(iv[8]),
Operation::Add,
Operation::Drop,
Operation::Drop,
Operation::Drop,
Operation::Drop,
Operation::Drop,
Operation::Drop,
Operation::Drop,
Operation::Drop,
];
(ops, iv)
}