use miden_crypto::{
field::{Algebra, ExtensionField, Field, TwoAdicField},
stark::air::{
LiftedAir,
symbolic::{AirLayout, SymbolicAirBuilder, SymbolicExpressionExt},
},
};
use crate::{
AceError,
circuit::{AceCircuit, emit_circuit},
dag::{AceDag, PeriodicColumnData, build_verifier_dag},
layout::{InputCounts, InputLayout},
};
#[derive(Debug, Clone, Copy)]
pub enum LayoutKind {
Native,
Masm,
}
#[derive(Debug, Clone, Copy)]
pub struct AceConfig {
pub num_quotient_chunks: usize,
pub num_vlpi_groups: usize,
pub layout: LayoutKind,
}
#[derive(Debug)]
pub struct AceArtifacts<EF> {
pub layout: InputLayout,
pub dag: AceDag<EF>,
}
pub fn build_ace_circuit_for_air<A, F, EF>(
air: &A,
config: AceConfig,
) -> Result<AceCircuit<EF>, AceError>
where
A: LiftedAir<F, EF>,
F: TwoAdicField,
EF: ExtensionField<F>,
SymbolicExpressionExt<F, EF>: Algebra<EF>,
{
let artifacts = build_ace_dag_for_air::<A, F, EF>(air, config)?;
emit_circuit(&artifacts.dag, artifacts.layout)
}
pub fn build_ace_dag_for_air<A, F, EF>(
air: &A,
config: AceConfig,
) -> Result<AceArtifacts<EF>, AceError>
where
A: LiftedAir<F, EF>,
F: TwoAdicField,
EF: ExtensionField<F>,
SymbolicExpressionExt<F, EF>: Algebra<EF>,
{
let periodic_columns = air.periodic_columns();
let counts = input_counts_for_air::<A, F, EF>(air, config, periodic_columns.len());
let layout = match config.layout {
LayoutKind::Native => InputLayout::new(counts),
LayoutKind::Masm => InputLayout::new_masm(counts),
};
layout.validate();
let air_layout = AirLayout {
preprocessed_width: 0,
main_width: counts.width,
num_public_values: counts.num_public,
permutation_width: counts.aux_width,
num_permutation_challenges: counts.num_randomness,
num_permutation_values: air.num_aux_values(),
num_periodic_columns: counts.num_periodic,
};
let mut builder = SymbolicAirBuilder::<F, EF>::new(air_layout);
air.eval(&mut builder);
let constraint_layout = builder.constraint_layout();
let base_constraints = builder.base_constraints();
let ext_constraints = builder.extension_constraints();
let periodic_data = (!periodic_columns.is_empty())
.then(|| PeriodicColumnData::from_periodic_columns::<F>(periodic_columns.to_vec()));
let dag = build_verifier_dag::<F, EF>(
&base_constraints,
&ext_constraints,
&constraint_layout,
&layout,
periodic_data.as_ref(),
);
Ok(AceArtifacts { layout, dag })
}
fn input_counts_for_air<A, F, EF>(air: &A, config: AceConfig, num_periodic: usize) -> InputCounts
where
A: LiftedAir<F, EF>,
F: Field,
EF: ExtensionField<F>,
{
assert!(config.num_quotient_chunks > 0, "num_quotient_chunks must be > 0");
assert!(
air.preprocessed_trace().is_none(),
"preprocessed trace inputs are not supported"
);
let num_randomness = air.num_randomness();
assert!(
num_randomness == 2,
"AIR must declare exactly 2 randomness challenges (alpha, beta), got {num_randomness}"
);
let num_vlpi = match config.layout {
LayoutKind::Masm => config.num_vlpi_groups * 2,
LayoutKind::Native => config.num_vlpi_groups,
};
InputCounts {
width: air.width(),
aux_width: air.aux_width(),
num_aux_boundary: air.num_aux_values(),
num_public: air.num_public_values(),
num_vlpi,
num_randomness,
num_periodic,
num_quotient_chunks: config.num_quotient_chunks,
}
}