1#[cfg(test)]
2use alloc::vec::Vec;
3
4use miden_air::{
5 MIDEN_AIR_COUNT, PcsParams, memory,
6 trace::{MIN_TRACE_LEN, eidos_compression::EIDOS_COMPRESSION_CYCLE_LEN},
7};
8
9use super::chiplets::Chiplets;
10use crate::{Felt, ONE};
11#[cfg(test)]
12use crate::{operation::Operation, utils::ToElements};
13
14#[derive(Debug)]
23pub struct RowMajorTraceWriter<'a, E> {
24 data: &'a mut [E],
25 payload: usize,
26 stride: usize,
27}
28
29impl<'a, E: Copy> RowMajorTraceWriter<'a, E> {
30 #[cfg(test)]
32 pub(super) fn new(data: &'a mut [E], width: usize) -> Self {
33 Self::with_stride(data, width, width)
34 }
35
36 pub fn with_stride(data: &'a mut [E], payload: usize, stride: usize) -> Self {
39 debug_assert!(stride >= payload, "stride must be >= payload");
40 debug_assert_eq!(data.len() % stride, 0, "buffer length must be a multiple of stride");
41 Self { data, payload, stride }
42 }
43
44 #[inline(always)]
46 pub fn write_row(&mut self, row: usize, values: &[E]) {
47 debug_assert_eq!(values.len(), self.payload);
48 let start = row * self.stride;
49 self.data[start..start + self.payload].copy_from_slice(values);
50 }
51}
52
53pub struct ChipletTraceFragment<'a> {
62 band: &'a mut [Felt],
64 stride: usize,
65 col_start: usize,
66 num_rows: usize,
67 num_cols: usize,
68 row_offset: usize,
70 prefix_one_cols: &'static [usize],
72}
73
74impl<'a> ChipletTraceFragment<'a> {
75 pub fn row_major(
77 band: &'a mut [Felt],
78 stride: usize,
79 col_start: usize,
80 num_cols: usize,
81 ) -> Self {
82 Self::with_overheads(band, stride, col_start, num_cols, 0, &[])
83 }
84
85 pub fn with_overheads(
88 band: &'a mut [Felt],
89 stride: usize,
90 col_start: usize,
91 num_cols: usize,
92 row_offset: usize,
93 prefix_one_cols: &'static [usize],
94 ) -> Self {
95 debug_assert_eq!(band.len() % stride, 0, "band length must be a multiple of stride");
96 debug_assert!(col_start + num_cols <= stride, "column band overruns the row stride");
97 debug_assert!(
98 prefix_one_cols.iter().all(|&c| c < col_start),
99 "prefix_one_cols must lie before col_start",
100 );
101 let num_rows = band.len() / stride;
102 Self {
103 band,
104 stride,
105 col_start,
106 num_rows,
107 num_cols,
108 row_offset,
109 prefix_one_cols,
110 }
111 }
112
113 pub fn width(&self) -> usize {
118 self.num_cols
119 }
120
121 pub fn len(&self) -> usize {
123 self.num_rows
124 }
125
126 pub fn prefix_mut(&mut self, row: usize) -> &mut [Felt] {
129 let row_start = row * self.stride;
130 &mut self.band[row_start..row_start + self.col_start]
131 }
132
133 pub fn copy_rows_from(&mut self, src: &[Felt]) {
139 debug_assert_eq!(src.len(), self.num_rows * self.num_cols, "source buffer size mismatch");
140 self.copy_rows_into(0, src);
141 }
142
143 pub fn copy_rows_into(&mut self, row_offset: usize, src: &[Felt]) {
146 debug_assert_eq!(src.len() % self.num_cols, 0, "source buffer size not row-aligned");
147 let chunk_rows = src.len() / self.num_cols;
148 debug_assert!(
149 row_offset + chunk_rows <= self.num_rows,
150 "chunk overruns fragment row range",
151 );
152 let write_chip_clk = self.stride > self.col_start + self.num_cols;
153 let clk_col = self.stride - 1;
154 for r in 0..chunk_rows {
155 let dst_row = row_offset + r;
156 let row_start = dst_row * self.stride;
157 let row = &mut self.band[row_start..row_start + self.stride];
158 for &col in self.prefix_one_cols {
159 row[col] = ONE;
160 }
161 row[self.col_start..self.col_start + self.num_cols]
162 .copy_from_slice(&src[r * self.num_cols..(r + 1) * self.num_cols]);
163 if write_chip_clk {
164 row[clk_col] = Felt::from_u32((self.row_offset + dst_row + 1) as u32);
165 }
166 }
167 }
168}
169
170#[derive(Debug, Default, Eq, PartialEq, Clone, Copy)]
178pub struct TraceLenSummary {
179 core_rows: usize,
180 chiplets: ChipletsLengths,
181 eidos_compression_rows: usize,
182 byte_pair_lookup_rows: usize,
183 padded_heights: Option<[usize; MIDEN_AIR_COUNT]>,
186}
187
188impl TraceLenSummary {
189 pub fn new(
190 core_rows: usize,
191 chiplets: ChipletsLengths,
192 eidos_compression_rows: usize,
193 byte_pair_lookup_rows: usize,
194 ) -> Self {
195 TraceLenSummary {
196 core_rows,
197 chiplets,
198 eidos_compression_rows,
199 byte_pair_lookup_rows,
200 padded_heights: None,
201 }
202 }
203
204 pub fn new_with_padded(
207 core_rows: usize,
208 chiplets: ChipletsLengths,
209 eidos_compression_rows: usize,
210 byte_pair_lookup_rows: usize,
211 padded_heights: [usize; MIDEN_AIR_COUNT],
212 ) -> Self {
213 TraceLenSummary {
214 core_rows,
215 chiplets,
216 eidos_compression_rows,
217 byte_pair_lookup_rows,
218 padded_heights: Some(padded_heights),
219 }
220 }
221
222 pub fn core_rows(&self) -> usize {
224 self.core_rows
225 }
226
227 pub fn chiplets(&self) -> ChipletsLengths {
229 self.chiplets
230 }
231
232 pub fn chiplets_rows(&self) -> usize {
234 self.chiplets.trace_len()
235 }
236
237 pub fn eidos_compression_rows(&self) -> usize {
239 self.eidos_compression_rows
240 }
241
242 pub fn eidos_compression_count(&self) -> usize {
245 debug_assert_eq!(self.eidos_compression_rows % EIDOS_COMPRESSION_CYCLE_LEN, 0);
246 self.eidos_compression_rows / EIDOS_COMPRESSION_CYCLE_LEN
247 }
248
249 pub fn byte_pair_lookup_rows(&self) -> usize {
253 self.byte_pair_lookup_rows
254 }
255
256 pub fn trace_len(&self) -> usize {
258 self.core_rows
259 .max(self.chiplets_rows())
260 .max(self.eidos_compression_rows)
261 .max(self.byte_pair_lookup_rows)
262 }
263
264 pub fn core_height(&self) -> usize {
266 padded_height(self.core_rows)
267 }
268
269 pub fn chiplets_height(&self) -> usize {
271 padded_height(self.chiplets_rows())
272 }
273
274 pub fn eidos_compression_height(&self) -> usize {
276 padded_height(self.eidos_compression_rows)
277 }
278
279 pub fn padded_trace_len(&self) -> usize {
281 self.padded_heights
282 .map(|heights| heights.into_iter().max().expect("heights is non-empty"))
283 .unwrap_or_else(|| {
284 self.core_height()
285 .max(self.chiplets_height())
286 .max(self.eidos_compression_height())
287 .max(self.byte_pair_lookup_rows)
288 })
289 }
290
291 pub fn padded_heights(&self) -> Option<&[usize; MIDEN_AIR_COUNT]> {
293 self.padded_heights.as_ref()
294 }
295
296 pub fn prover_memory_bytes(&self, params: &PcsParams) -> Option<u64> {
299 memory::prover_peak_bytes(self.padded_heights()?, params)
300 }
301
302 pub fn padding_percentage(&self) -> usize {
304 (self.padded_trace_len() - self.trace_len()) * 100 / self.padded_trace_len()
305 }
306}
307
308#[cfg(test)]
309mod tests {
310 use super::*;
311
312 #[test]
313 fn trace_len_summary_reports_eidos_compression_count() {
314 let summary =
315 TraceLenSummary::new(0, ChipletsLengths::default(), 3 * EIDOS_COMPRESSION_CYCLE_LEN, 0);
316
317 assert_eq!(summary.eidos_compression_rows(), 3 * EIDOS_COMPRESSION_CYCLE_LEN);
318 assert_eq!(summary.eidos_compression_count(), 3);
319 }
320
321 #[test]
322 fn trace_len_summary_records_four_air_heights_in_instance_order() {
323 let heights = [MIN_TRACE_LEN, 2 * MIN_TRACE_LEN, 4 * MIN_TRACE_LEN, 1 << 16];
324 let summary = TraceLenSummary::new_with_padded(
325 17,
326 ChipletsLengths::from_parts(19, 0, 0, 0, 0),
327 3 * EIDOS_COMPRESSION_CYCLE_LEN,
328 1 << 16,
329 heights,
330 );
331
332 assert_eq!(summary.core_rows(), 17);
333 assert_eq!(summary.chiplets_rows(), 20);
335 assert_eq!(summary.eidos_compression_rows(), 3 * EIDOS_COMPRESSION_CYCLE_LEN);
336 assert_eq!(summary.byte_pair_lookup_rows(), 1 << 16);
337 assert_eq!(summary.padded_heights(), Some(&heights));
338 assert_eq!(summary.padded_trace_len(), 1 << 16);
339
340 let params = miden_air::config::pcs_params();
341 assert_eq!(
342 summary.prover_memory_bytes(¶ms),
343 memory::prover_peak_bytes(&heights, ¶ms)
344 );
345 }
346}
347
348fn padded_height(rows: usize) -> usize {
349 rows.next_power_of_two().max(MIN_TRACE_LEN)
350}
351
352#[derive(Default, Clone, Copy, Debug, PartialEq, Eq)]
357pub struct ChipletsLengths {
358 hash_chiplet_len: usize,
359 bitwise_chiplet_len: usize,
360 memory_chiplet_len: usize,
361 ace_chiplet_len: usize,
362 kernel_rom_len: usize,
363}
364
365impl ChipletsLengths {
366 pub fn new(chiplets: &Chiplets) -> Self {
367 ChipletsLengths {
368 hash_chiplet_len: chiplets.bitwise_start().into(),
369 bitwise_chiplet_len: chiplets.memory_start() - chiplets.bitwise_start(),
370 memory_chiplet_len: chiplets.ace_start() - chiplets.memory_start(),
371 ace_chiplet_len: chiplets.kernel_rom_start() - chiplets.ace_start(),
372 kernel_rom_len: chiplets.padding_start() - chiplets.kernel_rom_start(),
373 }
374 }
375
376 pub fn from_parts(
377 hash_len: usize,
378 bitwise_len: usize,
379 memory_len: usize,
380 ace_len: usize,
381 kernel_len: usize,
382 ) -> Self {
383 ChipletsLengths {
384 hash_chiplet_len: hash_len,
385 bitwise_chiplet_len: bitwise_len,
386 memory_chiplet_len: memory_len,
387 ace_chiplet_len: ace_len,
388 kernel_rom_len: kernel_len,
389 }
390 }
391
392 pub fn hash_chiplet_len(&self) -> usize {
394 self.hash_chiplet_len
395 }
396
397 pub fn bitwise_chiplet_len(&self) -> usize {
399 self.bitwise_chiplet_len
400 }
401
402 pub fn memory_chiplet_len(&self) -> usize {
404 self.memory_chiplet_len
405 }
406
407 pub fn ace_chiplet_len(&self) -> usize {
409 self.ace_chiplet_len
410 }
411
412 pub fn kernel_rom_len(&self) -> usize {
414 self.kernel_rom_len
415 }
416
417 pub fn trace_len(&self) -> usize {
421 self.hash_chiplet_len()
422 + self.bitwise_chiplet_len()
423 + self.memory_chiplet_len()
424 + self.ace_chiplet_len()
425 + self.kernel_rom_len()
426 + 1
427 }
428}
429
430pub(crate) fn split_element_u32_into_u16(value: Felt) -> (Felt, Felt) {
436 let (hi, lo) = split_u32_into_u16(value.as_canonical_u64());
437 (Felt::new_unchecked(hi as u64), Felt::new_unchecked(lo as u64))
438}
439
440pub(crate) fn split_u32_into_u16(value: u64) -> (u16, u16) {
445 const U32MAX: u64 = u32::MAX as u64;
446 debug_assert!(value <= U32MAX, "not a 32-bit value");
447
448 let lo = value as u16;
449 let hi = (value >> 16) as u16;
450
451 (hi, lo)
452}
453
454#[cfg(test)]
461pub(super) fn build_span_with_respan_ops() -> (Vec<Operation>, Vec<Felt>) {
462 let iv = [1, 3, 5, 7, 9, 11, 13, 15, 17].to_elements();
463 let ops = alloc::vec![
464 Operation::Push(iv[0]),
465 Operation::Push(iv[1]),
466 Operation::Push(iv[2]),
467 Operation::Push(iv[3]),
468 Operation::Push(iv[4]),
469 Operation::Push(iv[5]),
470 Operation::Push(iv[6]),
471 Operation::Push(iv[7]),
473 Operation::Push(iv[8]),
474 Operation::Add,
475 Operation::Drop,
477 Operation::Drop,
478 Operation::Drop,
479 Operation::Drop,
480 Operation::Drop,
481 Operation::Drop,
482 Operation::Drop,
483 Operation::Drop,
484 ];
485 (ops, iv)
486}