miden_processor/trace/chiplets/ace/
mod.rs1use alloc::{collections::BTreeMap, vec::Vec};
2
3use miden_air::trace::{MainTrace, RowIndex, chiplets::ace::ACE_CHIPLET_NUM_COLS};
4use miden_core::{Felt, ZERO, field::ExtensionField};
5
6use crate::trace::TraceFragment;
7
8mod trace;
9pub use trace::{CircuitEvaluation, NUM_ACE_LOGUP_FRACTIONS_EVAL, NUM_ACE_LOGUP_FRACTIONS_READ};
10
11mod instruction;
12#[cfg(test)]
13mod tests;
14
15pub const PTR_OFFSET_ELEM: Felt = Felt::ONE;
16pub const PTR_OFFSET_WORD: Felt = Felt::new(4);
17pub const MAX_NUM_ACE_WIRES: u32 = instruction::MAX_ID;
18
19#[derive(Debug, Default)]
27pub struct Ace {
28 circuit_evaluations: BTreeMap<RowIndex, CircuitEvaluation>,
29}
30
31impl Ace {
32 pub(crate) fn trace_len(&self) -> usize {
34 self.circuit_evaluations.values().map(|eval_ctx| eval_ctx.num_rows()).sum()
35 }
36
37 pub(crate) fn fill_trace(self, trace: &mut TraceFragment) -> Vec<EvaluatedCircuitsMetadata> {
42 debug_assert_eq!(self.trace_len(), trace.len(), "inconsistent trace lengths");
44 debug_assert_eq!(ACE_CHIPLET_NUM_COLS, trace.width(), "inconsistent trace widths");
45
46 let mut gen_trace: [Vec<Felt>; ACE_CHIPLET_NUM_COLS] = (0..ACE_CHIPLET_NUM_COLS)
47 .map(|_| vec![ZERO; self.trace_len()])
48 .collect::<Vec<_>>()
49 .try_into()
50 .expect("failed to convert vector to array");
51
52 let mut sections_info = Vec::with_capacity(self.circuit_evaluations.keys().count());
53
54 let mut offset = 0;
55 for eval_ctx in self.circuit_evaluations.into_values() {
56 eval_ctx.fill(offset, &mut gen_trace);
57 offset += eval_ctx.num_rows();
58 let section = EvaluatedCircuitsMetadata::from_evaluation_context(&eval_ctx);
59 sections_info.push(section);
60 }
61
62 for (out_column, column) in trace.columns().zip(gen_trace) {
63 out_column.copy_from_slice(&column);
64 }
65
66 sections_info
67 }
68
69 pub(crate) fn add_circuit_evaluation(
71 &mut self,
72 clk: RowIndex,
73 circuit_eval: CircuitEvaluation,
74 ) {
75 self.circuit_evaluations.insert(clk, circuit_eval);
76 }
77}
78
79#[derive(Debug, Default, Clone)]
82pub struct EvaluatedCircuitsMetadata {
83 ctx: u32,
84 clk: u32,
85 num_vars: u32,
86 num_evals: u32,
87}
88
89impl EvaluatedCircuitsMetadata {
90 pub fn clk(&self) -> u32 {
91 self.clk
92 }
93
94 pub fn ctx(&self) -> u32 {
95 self.ctx
96 }
97
98 pub fn num_vars(&self) -> u32 {
99 self.num_vars
100 }
101
102 pub fn num_evals(&self) -> u32 {
103 self.num_evals
104 }
105
106 fn from_evaluation_context(eval_ctx: &CircuitEvaluation) -> EvaluatedCircuitsMetadata {
107 EvaluatedCircuitsMetadata {
108 ctx: eval_ctx.ctx(),
109 clk: eval_ctx.clk(),
110 num_vars: eval_ctx.num_read_rows(),
111 num_evals: eval_ctx.num_eval_rows(),
112 }
113 }
114}
115
116#[derive(Clone, Debug, Default)]
126pub struct AceHints {
127 offset_chiplet_trace: usize,
128 pub sections: Vec<EvaluatedCircuitsMetadata>,
129}
130
131impl AceHints {
132 pub fn new(offset_chiplet_trace: usize, sections: Vec<EvaluatedCircuitsMetadata>) -> Self {
133 Self { offset_chiplet_trace, sections }
134 }
135
136 pub(crate) fn offset(&self) -> usize {
137 self.offset_chiplet_trace
138 }
139
140 pub(crate) fn build_divisors<E: ExtensionField<Felt>>(
141 &self,
142 main_trace: &MainTrace,
143 alphas: &[E],
144 ) -> Vec<E> {
145 let num_fractions = self.num_fractions();
146 let mut total_values = vec![E::ZERO; num_fractions];
147 let mut total_inv_values = vec![E::ZERO; num_fractions];
148
149 let mut chiplet_offset = self.offset_chiplet_trace;
150 let mut values_offset = 0;
151 let mut acc = E::ONE;
152 for section in self.sections.iter() {
153 let clk = section.clk();
154 let ctx = section.ctx();
155
156 let values = &mut total_values[values_offset
157 ..values_offset + NUM_ACE_LOGUP_FRACTIONS_READ * section.num_vars() as usize];
158 let inv_values = &mut total_inv_values[values_offset
159 ..values_offset + NUM_ACE_LOGUP_FRACTIONS_READ * section.num_vars() as usize];
160
161 for (i, (value, inv_value)) in values
163 .chunks_mut(NUM_ACE_LOGUP_FRACTIONS_READ)
164 .zip(inv_values.chunks_mut(NUM_ACE_LOGUP_FRACTIONS_READ))
165 .enumerate()
166 {
167 let trace_row = i + chiplet_offset;
168
169 let wire_0 = main_trace.chiplet_ace_wire_0(trace_row.into());
170 let wire_1 = main_trace.chiplet_ace_wire_1(trace_row.into());
171
172 let value_0 = alphas[0]
173 + alphas[1] * Felt::from_u32(clk)
174 + alphas[2] * Felt::from_u32(ctx)
175 + alphas[3] * wire_0[0]
176 + alphas[4] * wire_0[1]
177 + alphas[5] * wire_0[2];
178 let value_1 = alphas[0]
179 + alphas[1] * Felt::from_u32(clk)
180 + alphas[2] * Felt::from_u32(ctx)
181 + alphas[3] * wire_1[0]
182 + alphas[4] * wire_1[1]
183 + alphas[5] * wire_1[2];
184
185 value[0] = value_0;
186 value[1] = value_1;
187 inv_value[0] = acc;
188 acc *= value_0;
189 inv_value[1] = acc;
190 acc *= value_1;
191 }
192
193 chiplet_offset += section.num_vars() as usize;
194 values_offset += NUM_ACE_LOGUP_FRACTIONS_READ * section.num_vars() as usize;
195
196 let values = &mut total_values[values_offset
198 ..values_offset + NUM_ACE_LOGUP_FRACTIONS_EVAL * section.num_evals() as usize];
199 let inv_values = &mut total_inv_values[values_offset
200 ..values_offset + NUM_ACE_LOGUP_FRACTIONS_EVAL * section.num_evals() as usize];
201 for (i, (value, inv_value)) in values
202 .chunks_mut(NUM_ACE_LOGUP_FRACTIONS_EVAL)
203 .zip(inv_values.chunks_mut(NUM_ACE_LOGUP_FRACTIONS_EVAL))
204 .enumerate()
205 {
206 let trace_row = i + chiplet_offset;
207
208 let wire_0 = main_trace.chiplet_ace_wire_0(trace_row.into());
209 let wire_1 = main_trace.chiplet_ace_wire_1(trace_row.into());
210 let wire_2 = main_trace.chiplet_ace_wire_2(trace_row.into());
211
212 let value_0 = alphas[0]
213 + alphas[1] * Felt::from_u32(clk)
214 + alphas[2] * Felt::from_u32(ctx)
215 + alphas[3] * wire_0[0]
216 + alphas[4] * wire_0[1]
217 + alphas[5] * wire_0[2];
218
219 let value_1 = alphas[0]
220 + alphas[1] * Felt::from_u32(clk)
221 + alphas[2] * Felt::from_u32(ctx)
222 + alphas[3] * wire_1[0]
223 + alphas[4] * wire_1[1]
224 + alphas[5] * wire_1[2];
225
226 let value_2 = alphas[0]
227 + alphas[1] * Felt::from_u32(clk)
228 + alphas[2] * Felt::from_u32(ctx)
229 + alphas[3] * wire_2[0]
230 + alphas[4] * wire_2[1]
231 + alphas[5] * wire_2[2];
232
233 value[0] = value_0;
234 value[1] = value_1;
235 value[2] = value_2;
236 inv_value[0] = acc;
237 acc *= value_0;
238 inv_value[1] = acc;
239 acc *= value_1;
240 inv_value[2] = acc;
241 acc *= value_2;
242 }
243
244 chiplet_offset += section.num_evals() as usize;
245 values_offset += NUM_ACE_LOGUP_FRACTIONS_EVAL * section.num_evals() as usize;
246 }
247
248 acc = acc.inverse();
250
251 for i in (0..total_values.len()).rev() {
252 total_inv_values[i] *= acc;
253 acc *= total_values[i];
254 }
255
256 total_inv_values
257 }
258
259 fn num_fractions(&self) -> usize {
260 self.sections
261 .iter()
262 .map(|section| {
263 NUM_ACE_LOGUP_FRACTIONS_READ * (section.num_vars as usize)
264 + NUM_ACE_LOGUP_FRACTIONS_EVAL * (section.num_evals as usize)
265 })
266 .sum()
267 }
268}