use alloc::vec::Vec;
use core::ops::Range;
use miden_air::trace::{
RowIndex,
chiplets::ace::{
CLK_IDX, CTX_IDX, EVAL_OP_IDX, ID_0_IDX, ID_1_IDX, ID_2_IDX, M_0_IDX, M_1_IDX, PTR_IDX,
READ_NUM_EVAL_IDX, SELECTOR_BLOCK_IDX, SELECTOR_START_IDX, V_0_0_IDX, V_0_1_IDX, V_1_0_IDX,
V_1_1_IDX, V_2_0_IDX, V_2_1_IDX,
},
};
use miden_core::{
Felt, Word,
field::{BasedVectorSpace, QuadFelt},
};
use super::{
MAX_NUM_ACE_WIRES,
instruction::{Op, decode_instruction},
};
use crate::{ContextId, errors::AceError};
pub const NUM_ACE_LOGUP_FRACTIONS_READ: usize = 2;
pub const NUM_ACE_LOGUP_FRACTIONS_EVAL: usize = 3;
#[derive(Debug, Clone)]
pub struct CircuitEvaluation {
ctx: ContextId,
clk: RowIndex,
wire_bus: WireBus,
col_ptr: Vec<Felt>,
col_op: Vec<Felt>,
col_wire_out: WireColumn,
col_wire_left: WireColumn,
col_wire_right: WireColumn,
num_read_rows: u32,
num_eval_rows: u32,
}
impl CircuitEvaluation {
pub fn new(ctx: ContextId, clk: RowIndex, num_read_rows: u32, num_eval_rows: u32) -> Self {
let num_wires = 2 * (num_read_rows as u64) + (num_eval_rows as u64);
assert!(num_wires <= MAX_NUM_ACE_WIRES as u64, "too many wires");
let num_rows = num_read_rows + num_eval_rows;
Self {
ctx,
clk,
wire_bus: WireBus::new(num_wires as u32),
col_ptr: Vec::with_capacity(num_rows as usize),
col_op: Vec::with_capacity(num_eval_rows as usize),
col_wire_out: WireColumn::new(num_rows as usize),
col_wire_left: WireColumn::new(num_rows as usize),
col_wire_right: WireColumn::new(num_eval_rows as usize),
num_read_rows,
num_eval_rows,
}
}
pub fn num_rows(&self) -> usize {
(self.num_read_rows + self.num_eval_rows) as usize
}
pub fn clk(&self) -> u32 {
self.clk.into()
}
pub fn ctx(&self) -> u32 {
self.ctx.into()
}
pub fn num_read_rows(&self) -> u32 {
self.num_read_rows
}
pub fn num_eval_rows(&self) -> u32 {
self.num_eval_rows
}
pub fn do_read(&mut self, ptr: Felt, word: Word) {
let v_0 = QuadFelt::from_basis_coefficients_fn(|i: usize| [word[0], word[1]][i]);
let id_0 = self.wire_bus.insert(v_0);
self.col_wire_out.push(id_0, v_0);
let v_1 = QuadFelt::from_basis_coefficients_fn(|i: usize| [word[2], word[3]][i]);
let id_1 = self.wire_bus.insert(v_1);
self.col_wire_left.push(id_1, v_1);
self.col_ptr.push(ptr);
}
pub fn do_eval(&mut self, ptr: Felt, instruction: Felt) -> Result<(), AceError> {
let (id_l, id_r, op) = decode_instruction(instruction)
.ok_or(AceError("failed to decode instruction".into()))?;
let v_l = self
.wire_bus
.read_value(id_l)
.ok_or(AceError("failed to read from the wiring bus".into()))?;
let id_l = Felt::from_u32(id_l);
self.col_wire_left.push(id_l, v_l);
let v_r = self
.wire_bus
.read_value(id_r)
.ok_or(AceError("failed to read from the wiring bus".into()))?;
let id_r = Felt::from_u32(id_r);
self.col_wire_right.push(id_r, v_r);
let v_out = match op {
Op::Sub => v_l - v_r,
Op::Mul => v_l * v_r,
Op::Add => v_l + v_r,
};
let id_out = self.wire_bus.insert(v_out);
self.col_wire_out.push(id_out, v_out);
let op_sub = -Felt::ONE;
let op_mul = Felt::ZERO;
let op_add = Felt::ONE;
let op = match op {
Op::Sub => op_sub,
Op::Mul => op_mul,
Op::Add => op_add,
};
self.col_op.push(op);
self.col_ptr.push(ptr);
Ok(())
}
pub fn fill(&self, offset: usize, columns: &mut [Vec<Felt>]) {
let num_read_rows = self.num_read_rows as usize;
let num_eval_rows = self.num_eval_rows as usize;
let num_rows = num_read_rows + num_eval_rows;
let read_range = Range {
start: offset,
end: offset + num_read_rows,
};
let eval_range = Range {
start: read_range.end,
end: read_range.end + num_eval_rows,
};
columns[SELECTOR_START_IDX][offset] = Felt::ONE;
columns[SELECTOR_START_IDX][(offset + 1)..].fill(Felt::ZERO);
let f_read = Felt::ZERO;
let f_eval = Felt::ONE;
columns[SELECTOR_BLOCK_IDX][read_range.clone()].fill(f_read);
columns[SELECTOR_BLOCK_IDX][eval_range.clone()].fill(f_eval);
let ctx_felt = self.ctx.into();
columns[CTX_IDX][offset..offset + num_rows].fill(ctx_felt);
columns[PTR_IDX][offset..offset + num_rows].copy_from_slice(&self.col_ptr);
let clk_felt = self.clk.into();
columns[CLK_IDX][offset..offset + num_rows].fill(clk_felt);
let eval_section_first_idx = Felt::from_u32(self.num_eval_rows - 1);
columns[READ_NUM_EVAL_IDX][read_range.clone()].fill(eval_section_first_idx);
columns[EVAL_OP_IDX][eval_range.clone()].copy_from_slice(&self.col_op);
columns[ID_0_IDX][offset..offset + num_rows].copy_from_slice(&self.col_wire_out.id);
columns[V_0_0_IDX][offset..offset + num_rows].copy_from_slice(&self.col_wire_out.v_0);
columns[V_0_1_IDX][offset..offset + num_rows].copy_from_slice(&self.col_wire_out.v_1);
columns[ID_1_IDX][offset..offset + num_rows].copy_from_slice(&self.col_wire_left.id);
columns[V_1_0_IDX][offset..offset + num_rows].copy_from_slice(&self.col_wire_left.v_0);
columns[V_1_1_IDX][offset..offset + num_rows].copy_from_slice(&self.col_wire_left.v_1);
columns[ID_2_IDX][eval_range.clone()].copy_from_slice(&self.col_wire_right.id);
columns[V_2_0_IDX][eval_range.clone()].copy_from_slice(&self.col_wire_right.v_0);
columns[V_2_1_IDX][eval_range.clone()].copy_from_slice(&self.col_wire_right.v_1);
let mut multiplicities_iter = self.wire_bus.wires.iter().map(|(_v, m)| Felt::from_u32(*m));
for row_index in read_range {
let m_0 = multiplicities_iter
.next()
.expect("the m0 multiplicities were not constructed properly");
let m_1 = multiplicities_iter
.next()
.expect("the m1 multiplicities were not constructed properly");
columns[M_0_IDX][row_index] = m_0;
columns[M_1_IDX][row_index] = m_1;
}
for row_index in eval_range {
let m_0 = multiplicities_iter
.next()
.expect("the m0 multiplicities were not constructed properly");
columns[M_0_IDX][row_index] = m_0;
}
debug_assert!(multiplicities_iter.next().is_none());
}
pub fn output_value(&self) -> Option<QuadFelt> {
if !self.wire_bus.is_finalized() {
return None;
}
self.wire_bus.wires.last().map(|(v, _m)| *v)
}
}
#[derive(Debug, Clone)]
struct WireColumn {
id: Vec<Felt>,
v_0: Vec<Felt>,
v_1: Vec<Felt>,
}
impl WireColumn {
fn new(num_rows: usize) -> Self {
Self {
id: Vec::with_capacity(num_rows),
v_0: Vec::with_capacity(num_rows),
v_1: Vec::with_capacity(num_rows),
}
}
fn push(&mut self, id: Felt, v: QuadFelt) {
self.id.push(id);
let v: &[Felt] = v.as_basis_coefficients_slice();
self.v_0.push(v[0]);
self.v_1.push(v[1]);
}
}
#[derive(Debug, Clone)]
struct WireBus {
id_next: Felt,
wires: Vec<(QuadFelt, u32)>,
num_wires: u32,
}
impl WireBus {
fn new(num_wires: u32) -> Self {
Self {
wires: Vec::with_capacity(num_wires as usize),
num_wires,
id_next: Felt::from_u32(num_wires - 1),
}
}
fn insert(&mut self, value: QuadFelt) -> Felt {
debug_assert!(!self.is_finalized());
self.wires.push((value, 0));
let id = self.id_next;
self.id_next -= Felt::ONE;
id
}
fn read_value(&mut self, id: u32) -> Option<QuadFelt> {
let (v, m) = self
.num_wires
.checked_sub(id + 1)
.and_then(|id| self.wires.get_mut(id as usize))?;
*m += 1;
Some(*v)
}
fn is_finalized(&self) -> bool {
self.wires.len() == self.num_wires as usize
}
}