miden_processor/fast/
circuit_eval.rs

1use miden_air::RowIndex;
2use miden_core::{Felt, FieldElement, QuadFelt};
3
4use super::{FastProcessor, memory::Memory};
5use crate::{
6    ContextId, ExecutionError,
7    chiplets::{CircuitEvaluation, MAX_NUM_ACE_WIRES, PTR_OFFSET_ELEM, PTR_OFFSET_WORD},
8    errors::{AceError, ErrorContext},
9};
10
11impl FastProcessor {
12    /// Checks that the evaluation of an arithmetic circuit is equal to zero.
13    ///
14    /// The inputs are composed of:
15    ///
16    /// 1. a pointer to the memory region containing the arithmetic circuit description, which
17    ///    itself is arranged as:
18    ///
19    ///    a. `Read` section:
20    ///       1. Inputs to the circuit which are elements in the quadratic extension field,
21    ///       2. Constants of the circuit which are elements in the quadratic extension field,
22    ///
23    ///    b. `Eval` section, which contains the encodings of the evaluation gates of the circuit,
24    ///    where each gate is encoded as a single base field element.
25    /// 2. the number of quadratic extension field elements read in the `READ` section,
26    /// 3. the number of field elements, one base field element per gate, in the `EVAL` section,
27    ///
28    /// Stack transition:
29    /// [ptr, num_read, num_eval, ...] -> [ptr, num_read, num_eval, ...]
30    pub fn op_eval_circuit(&mut self, err_ctx: &impl ErrorContext) -> Result<(), ExecutionError> {
31        let num_eval = self.stack_get(2);
32        let num_read = self.stack_get(1);
33        let ptr = self.stack_get(0);
34        let ctx = self.ctx;
35        let circuit_evaluation =
36            eval_circuit_fast_(ctx, ptr, self.clk, num_read, num_eval, &mut self.memory, err_ctx)?;
37        self.ace.add_circuit_evaluation(self.clk, circuit_evaluation);
38
39        Ok(())
40    }
41}
42
43/// Identical to `[chiplets::ace::eval_circuit]` but adapted for use with `[FastProcessor]`.
44#[allow(clippy::too_many_arguments)]
45pub fn eval_circuit_fast_(
46    ctx: ContextId,
47    ptr: Felt,
48    clk: RowIndex,
49    num_vars: Felt,
50    num_eval: Felt,
51    mem: &mut Memory,
52    err_ctx: &impl ErrorContext,
53) -> Result<CircuitEvaluation, ExecutionError> {
54    let num_vars = num_vars.as_int();
55    let num_eval = num_eval.as_int();
56
57    let num_wires = num_vars + num_eval;
58    if num_wires > MAX_NUM_ACE_WIRES as u64 {
59        return Err(ExecutionError::failed_arithmetic_evaluation(
60            err_ctx,
61            AceError::TooManyWires(num_wires),
62        ));
63    }
64
65    // Ensure vars and instructions are word-aligned and non-empty. Note that variables are
66    // quadratic extension field elements while instructions are encoded as base field elements.
67    // Hence we can pack 2 variables and 4 instructions per word.
68    if !num_vars.is_multiple_of(2) || num_vars == 0 {
69        return Err(ExecutionError::failed_arithmetic_evaluation(
70            err_ctx,
71            AceError::NumVarIsNotWordAlignedOrIsEmpty(num_vars),
72        ));
73    }
74    if !num_eval.is_multiple_of(4) || num_eval == 0 {
75        return Err(ExecutionError::failed_arithmetic_evaluation(
76            err_ctx,
77            AceError::NumEvalIsNotWordAlignedOrIsEmpty(num_eval),
78        ));
79    }
80
81    // Ensure instructions are word-aligned and non-empty
82    let num_read_rows = num_vars as u32 / 2;
83    let num_eval_rows = num_eval as u32;
84
85    let mut evaluation_context = CircuitEvaluation::new(ctx, clk, num_read_rows, num_eval_rows);
86
87    let mut ptr = ptr;
88    // perform READ operations
89    for _ in 0..num_read_rows {
90        let word = mem.read_word(ctx, ptr, clk, err_ctx).map_err(ExecutionError::MemoryError)?;
91        evaluation_context.do_read(ptr, word)?;
92        ptr += PTR_OFFSET_WORD;
93    }
94    // perform EVAL operations
95    for _ in 0..num_eval_rows {
96        let instruction =
97            mem.read_element(ctx, ptr, err_ctx).map_err(ExecutionError::MemoryError)?;
98        evaluation_context.do_eval(ptr, instruction, err_ctx)?;
99        ptr += PTR_OFFSET_ELEM;
100    }
101
102    // Ensure the circuit evaluated to zero.
103    if !evaluation_context.output_value().is_some_and(|eval| eval == QuadFelt::ZERO) {
104        return Err(ExecutionError::failed_arithmetic_evaluation(
105            err_ctx,
106            AceError::CircuitNotEvaluateZero,
107        ));
108    }
109
110    Ok(evaluation_context)
111}