1use miden_crypto::{
9 field::{Algebra, ExtensionField, Field, TwoAdicField},
10 stark::air::{
11 LiftedAir,
12 symbolic::{AirLayout, SymbolicAirBuilder, SymbolicExpressionExt},
13 },
14};
15
16use crate::{
17 AceError,
18 circuit::{AceCircuit, emit_circuit},
19 dag::{AceDag, PeriodicColumnData, build_verifier_dag},
20 layout::{InputCounts, InputLayout},
21};
22
23#[derive(Debug, Clone, Copy)]
29pub enum LayoutKind {
30 Native,
32 Masm,
34}
35
36#[derive(Debug, Clone, Copy)]
38pub struct AceConfig {
39 pub num_quotient_chunks: usize,
41 pub num_aux_inputs: usize,
43 pub layout: LayoutKind,
45}
46
47#[derive(Debug)]
49pub(crate) struct AceArtifacts<EF> {
50 pub layout: InputLayout,
52 pub dag: AceDag<EF>,
54}
55
56pub fn build_ace_circuit_for_air<A, F, EF>(
61 air: &A,
62 config: AceConfig,
63) -> Result<AceCircuit<EF>, AceError>
64where
65 A: LiftedAir<F, EF>,
66 F: TwoAdicField,
67 EF: ExtensionField<F>,
68 SymbolicExpressionExt<F, EF>: Algebra<EF>,
69{
70 let artifacts = build_ace_dag_for_air::<A, F, EF>(air, config)?;
71 emit_circuit(&artifacts.dag, artifacts.layout)
72}
73
74pub fn build_layout_for_air<A, F, EF>(air: &A, config: AceConfig) -> InputLayout
78where
79 A: LiftedAir<F, EF>,
80 F: Field,
81 EF: ExtensionField<F>,
82 SymbolicExpressionExt<F, EF>: Algebra<EF>,
83{
84 let num_periodic = air.periodic_columns().len();
85 let counts = input_counts_for_air::<A, F, EF>(air, config, num_periodic);
86 let layout = match config.layout {
87 LayoutKind::Native => InputLayout::new(counts),
88 LayoutKind::Masm => InputLayout::new_masm(counts),
89 };
90 layout.validate();
91 layout
92}
93
94pub(crate) fn build_ace_dag_for_air<A, F, EF>(
99 air: &A,
100 config: AceConfig,
101) -> Result<AceArtifacts<EF>, AceError>
102where
103 A: LiftedAir<F, EF>,
104 F: TwoAdicField,
105 EF: ExtensionField<F>,
106 SymbolicExpressionExt<F, EF>: Algebra<EF>,
107{
108 let periodic_columns = air.periodic_columns();
109 let counts = input_counts_for_air::<A, F, EF>(air, config, periodic_columns.len());
110 let layout = match config.layout {
111 LayoutKind::Native => InputLayout::new(counts),
112 LayoutKind::Masm => InputLayout::new_masm(counts),
113 };
114 layout.validate();
115
116 let air_layout = AirLayout {
117 preprocessed_width: 0,
118 main_width: counts.width,
119 num_public_values: counts.num_public,
120 permutation_width: counts.aux_width,
121 num_permutation_challenges: counts.num_randomness,
122 num_permutation_values: air.num_aux_values(),
123 num_periodic_columns: counts.num_periodic,
124 };
125 let mut builder = SymbolicAirBuilder::<F, EF>::new(air_layout);
126 air.eval(&mut builder);
127 let constraint_layout = builder.constraint_layout();
128 let base_constraints = builder.base_constraints();
129 let ext_constraints = builder.extension_constraints();
130
131 let periodic_data = (!periodic_columns.is_empty())
132 .then(|| PeriodicColumnData::from_periodic_columns::<F>(periodic_columns.to_vec()));
133 let dag = build_verifier_dag::<F, EF>(
134 &base_constraints,
135 &ext_constraints,
136 &constraint_layout,
137 &layout,
138 periodic_data.as_ref(),
139 );
140
141 Ok(AceArtifacts { layout, dag })
142}
143
144fn input_counts_for_air<A, F, EF>(air: &A, config: AceConfig, num_periodic: usize) -> InputCounts
145where
146 A: LiftedAir<F, EF>,
147 F: Field,
148 EF: ExtensionField<F>,
149{
150 assert!(config.num_quotient_chunks > 0, "num_quotient_chunks must be > 0");
151 assert!(
152 air.preprocessed_trace().is_none(),
153 "preprocessed trace inputs are not supported"
154 );
155
156 let num_randomness = air.num_randomness();
157 assert!(num_randomness > 0, "AIR must declare at least one randomness challenge");
158
159 InputCounts {
160 width: air.width(),
161 aux_width: air.aux_width(),
162 num_public: air.num_public_values(),
163 num_randomness,
164 num_periodic,
165 num_aux_inputs: config.num_aux_inputs,
166 num_quotient_chunks: config.num_quotient_chunks,
167 }
168}