use alloc::vec::Vec;
#[cfg(any(test, feature = "testing"))]
use core::ops::Range;
#[cfg(feature = "std")]
use miden_air::trace::PADDED_TRACE_WIDTH;
use miden_air::{
AirWitness, AuxBuilder, ProcessorAir, PublicInputs, debug,
trace::{
DECODER_TRACE_OFFSET, MainTrace, STACK_TRACE_OFFSET, TRACE_WIDTH,
decoder::{NUM_USER_OP_HELPERS, USER_OP_HELPERS_OFFSET},
},
};
use crate::{
AdviceProvider, Felt, MIN_STACK_DEPTH, ProgramInfo, StackInputs, StackOutputs, Word, ZERO,
fast::ExecutionOutput,
field::{ExtensionField, QuadFelt},
precompile::{PrecompileRequest, PrecompileTranscript},
utils::{ColMatrix, Matrix, RowMajorMatrix},
};
pub(crate) mod utils;
use miden_air::trace::Challenges;
use utils::{AuxColumnBuilder, TraceFragment};
pub mod chiplets;
pub mod execution_tracer;
mod decoder;
mod parallel;
mod range;
mod stack;
mod trace_state;
#[cfg(test)]
mod tests;
pub use miden_air::trace::RowIndex;
pub use parallel::{CORE_TRACE_WIDTH, build_trace, build_trace_with_max_len};
pub use utils::{ChipletsLengths, TraceLenSummary};
#[derive(Debug)]
pub struct ExecutionTrace {
main_trace: MainTrace,
aux_trace_builders: AuxTraceBuilders,
program_info: ProgramInfo,
stack_outputs: StackOutputs,
advice: AdviceProvider,
final_pc_transcript: PrecompileTranscript,
trace_len_summary: TraceLenSummary,
}
impl ExecutionTrace {
pub fn new_from_parts(
program_info: ProgramInfo,
execution_output: ExecutionOutput,
main_trace: MainTrace,
aux_trace_builders: AuxTraceBuilders,
trace_len_summary: TraceLenSummary,
) -> Self {
Self {
main_trace,
aux_trace_builders,
program_info,
stack_outputs: execution_output.stack,
advice: execution_output.advice,
final_pc_transcript: execution_output.final_pc_transcript,
trace_len_summary,
}
}
pub fn program_info(&self) -> &ProgramInfo {
&self.program_info
}
pub fn program_hash(&self) -> &Word {
self.program_info.program_hash()
}
pub fn stack_outputs(&self) -> &StackOutputs {
&self.stack_outputs
}
pub fn public_inputs(&self) -> PublicInputs {
PublicInputs::new(
self.program_info.clone(),
self.init_stack_state(),
self.stack_outputs,
self.final_pc_transcript.state(),
)
}
pub fn to_public_values(&self) -> Vec<Felt> {
self.public_inputs().to_elements()
}
pub fn aux_trace_builders(&self) -> AuxTraceBuilders {
self.aux_trace_builders.clone()
}
pub fn main_trace(&self) -> &MainTrace {
&self.main_trace
}
pub fn main_trace_mut(&mut self) -> &mut MainTrace {
&mut self.main_trace
}
pub fn precompile_requests(&self) -> &[PrecompileRequest] {
self.advice.precompile_requests()
}
pub fn take_precompile_requests(&mut self) -> Vec<PrecompileRequest> {
self.advice.take_precompile_requests()
}
pub fn final_precompile_transcript(&self) -> PrecompileTranscript {
self.final_pc_transcript
}
pub fn init_stack_state(&self) -> StackInputs {
let mut result = [ZERO; MIN_STACK_DEPTH];
for (i, result) in result.iter_mut().enumerate() {
*result = self.main_trace.get_column(i + STACK_TRACE_OFFSET)[0];
}
result.into()
}
pub fn last_stack_state(&self) -> StackOutputs {
let last_step = self.last_step();
let mut result = [ZERO; MIN_STACK_DEPTH];
for (i, result) in result.iter_mut().enumerate() {
*result = self.main_trace.get_column(i + STACK_TRACE_OFFSET)[last_step];
}
result.into()
}
pub fn get_user_op_helpers_at(&self, clk: u32) -> [Felt; NUM_USER_OP_HELPERS] {
let mut result = [ZERO; NUM_USER_OP_HELPERS];
for (i, result) in result.iter_mut().enumerate() {
*result = self.main_trace.get_column(DECODER_TRACE_OFFSET + USER_OP_HELPERS_OFFSET + i)
[clk as usize];
}
result
}
pub fn get_trace_len(&self) -> usize {
self.main_trace.num_rows()
}
pub fn length(&self) -> usize {
self.get_trace_len()
}
pub fn trace_len_summary(&self) -> &TraceLenSummary {
&self.trace_len_summary
}
pub fn advice_provider(&self) -> &AdviceProvider {
&self.advice
}
pub fn into_outputs(self) -> (StackOutputs, AdviceProvider) {
(self.stack_outputs, self.advice)
}
pub fn check_constraints(&self) {
let public_inputs = self.public_inputs();
let trace_matrix = self.to_row_major_matrix();
let (public_values, kernel_felts) = public_inputs.to_air_inputs();
let var_len_public_inputs: &[&[Felt]] = &[&kernel_felts];
let aux_builder = self.aux_trace_builders();
let digest = crate::crypto::hash::Poseidon2::hash_elements(&public_values);
let challenges =
[QuadFelt::new([digest[0], digest[1]]), QuadFelt::new([digest[2], digest[3]])];
let witness = AirWitness::new(&trace_matrix, &public_values, var_len_public_inputs);
debug::check_constraints(&ProcessorAir, witness, &aux_builder, &challenges);
}
pub fn to_row_major_matrix(&self) -> RowMajorMatrix<Felt> {
let trace_len = self.get_trace_len();
let mut col_major_data = Vec::with_capacity(TRACE_WIDTH * trace_len);
for col_idx in 0..TRACE_WIDTH {
col_major_data.extend_from_slice(self.main_trace.get_column(col_idx));
}
let col_major_matrix = RowMajorMatrix::new(col_major_data, trace_len);
col_major_matrix.transpose()
}
fn last_step(&self) -> usize {
self.length() - 1
}
#[cfg(feature = "std")]
pub fn print(&self) {
use miden_air::trace::TRACE_WIDTH;
let mut row = [ZERO; PADDED_TRACE_WIDTH];
for i in 0..self.length() {
self.main_trace.read_row_into(i, &mut row);
std::println!(
"{:?}",
row.iter().take(TRACE_WIDTH).map(|v| v.as_canonical_u64()).collect::<Vec<_>>()
);
}
}
#[cfg(any(test, feature = "testing"))]
pub fn get_column_range(&self, range: Range<usize>) -> Vec<Vec<Felt>> {
self.main_trace.get_column_range(range)
}
pub fn build_aux_trace<E>(&self, rand_elements: &[E]) -> Option<ColMatrix<E>>
where
E: ExtensionField<Felt>,
{
let aux_columns =
self.aux_trace_builders.build_aux_columns(&self.main_trace, rand_elements);
Some(ColMatrix::new(aux_columns))
}
}
#[derive(Debug, Clone)]
pub struct AuxTraceBuilders {
pub(crate) decoder: decoder::AuxTraceBuilder,
pub(crate) stack: stack::AuxTraceBuilder,
pub(crate) range: range::AuxTraceBuilder,
pub(crate) chiplets: chiplets::AuxTraceBuilder,
}
impl AuxTraceBuilders {
pub fn build_aux_columns<E>(&self, main_trace: &MainTrace, challenges: &[E]) -> Vec<Vec<E>>
where
E: ExtensionField<Felt>,
{
let challenges = Challenges::<E>::new(challenges[0], challenges[1]);
let decoder_cols = self.decoder.build_aux_columns(main_trace, &challenges);
let stack_cols = self.stack.build_aux_columns(main_trace, &challenges);
let range_cols = self.range.build_aux_columns(main_trace, &challenges);
let chiplets_cols = self.chiplets.build_aux_columns(main_trace, &challenges);
decoder_cols
.into_iter()
.chain(stack_cols)
.chain(range_cols)
.chain(chiplets_cols)
.collect()
}
}
impl<EF: ExtensionField<Felt>> AuxBuilder<Felt, EF> for AuxTraceBuilders {
fn build_aux_trace(
&self,
main: &RowMajorMatrix<Felt>,
challenges: &[EF],
) -> (RowMajorMatrix<EF>, Vec<EF>) {
let _span = tracing::info_span!("build_aux_trace").entered();
let main_trace_col_major = {
let num_rows = main.height();
let last_program_row = (1..num_rows)
.find(|&i| {
main.get(i, 0).expect("valid indices")
!= main.get(i - 1, 0).expect("valid indices") + Felt::ONE
})
.map_or(num_rows - 1, |i| i - 1);
let transposed = main.transpose();
let columns: Vec<Vec<Felt>> = transposed.row_slices().map(|row| row.to_vec()).collect();
MainTrace::new(ColMatrix::new(columns), last_program_row.into())
};
let aux_columns = self.build_aux_columns(&main_trace_col_major, challenges);
assert!(!aux_columns.is_empty(), "aux columns should not be empty");
let trace_len = main.height();
let num_ef_cols = aux_columns.len();
let mut col_major_data = Vec::with_capacity(trace_len * num_ef_cols);
for col in aux_columns {
col_major_data.extend_from_slice(&col);
}
let aux_trace = RowMajorMatrix::new(col_major_data, trace_len).transpose();
let last_row = aux_trace
.row_slice(trace_len - 1)
.expect("aux trace has at least one row")
.to_vec();
(aux_trace, last_row)
}
}