use std::ops::Mul;
use num_traits::Zero;
use stwo::core::fields::m31::BaseField;
use stwo::core::fields::qm31::{SecureField, SECURE_EXTENSION_DEGREE};
use stwo::core::pcs::TreeVec;
use stwo::core::utils::offset_bit_reversed_circle_domain_index;
use stwo::core::Fraction;
use stwo::prover::backend::gpu::GpuBackend;
use stwo::prover::backend::simd::column::VeryPackedBaseColumn;
use stwo::prover::backend::simd::m31::LOG_N_LANES;
use stwo::prover::backend::simd::very_packed_m31::{
VeryPackedBaseField, VeryPackedSecureField, LOG_N_VERY_PACKED_ELEMS,
};
use stwo::prover::backend::Column;
use stwo::prover::poly::circle::CircleEvaluation;
use stwo::prover::poly::BitReversedOrder;
use crate::logup::LogupAtRow;
use crate::{EvalAtRow, INTERACTION_TRACE_IDX};
pub struct GpuDomainEvaluator<'a> {
pub trace_eval:
&'a TreeVec<Vec<&'a CircleEvaluation<GpuBackend, BaseField, BitReversedOrder>>>,
pub column_index_per_interaction: Vec<usize>,
pub vec_row: usize,
pub random_coeff_powers: &'a [SecureField],
pub row_res: VeryPackedSecureField,
pub constraint_index: usize,
pub domain_log_size: u32,
pub eval_domain_log_size: u32,
pub logup: LogupAtRow<Self>,
}
impl<'a> GpuDomainEvaluator<'a> {
pub fn new(
trace_eval: &'a TreeVec<Vec<&CircleEvaluation<GpuBackend, BaseField, BitReversedOrder>>>,
vec_row: usize,
random_coeff_powers: &'a [SecureField],
domain_log_size: u32,
eval_log_size: u32,
log_size: u32,
claimed_sum: SecureField,
) -> Self {
Self {
trace_eval,
column_index_per_interaction: vec![0; trace_eval.len()],
vec_row,
random_coeff_powers,
row_res: VeryPackedSecureField::zero(),
constraint_index: 0,
domain_log_size,
eval_domain_log_size: eval_log_size,
logup: LogupAtRow::new(INTERACTION_TRACE_IDX, claimed_sum, log_size),
}
}
}
impl EvalAtRow for GpuDomainEvaluator<'_> {
type F = VeryPackedBaseField;
type EF = VeryPackedSecureField;
fn next_interaction_mask<const N: usize>(
&mut self,
interaction: usize,
offsets: [isize; N],
) -> [Self::F; N] {
let col_index = self.column_index_per_interaction[interaction];
self.column_index_per_interaction[interaction] += 1;
offsets.map(|off| {
if off == 0 {
unsafe {
let col = &self
.trace_eval
.get_unchecked(interaction)
.get_unchecked(col_index)
.values;
let very_packed_col = VeryPackedBaseColumn::transform_under_ref(col);
return *very_packed_col.data.get_unchecked(self.vec_row);
};
}
VeryPackedBaseField::from_array(std::array::from_fn(|i| {
let row_index = offset_bit_reversed_circle_domain_index(
(self.vec_row << (LOG_N_LANES + LOG_N_VERY_PACKED_ELEMS)) + i,
self.domain_log_size,
self.eval_domain_log_size,
off,
);
self.trace_eval[interaction][col_index].at(row_index)
}))
})
}
fn add_constraint<G>(&mut self, constraint: G)
where
Self::EF: Mul<G, Output = Self::EF> + From<G>,
{
self.row_res +=
VeryPackedSecureField::broadcast(self.random_coeff_powers[self.constraint_index])
* constraint;
self.constraint_index += 1;
}
fn combine_ef(values: [Self::F; SECURE_EXTENSION_DEGREE]) -> Self::EF {
VeryPackedSecureField::from_very_packed_m31s(values)
}
crate::logup_proxy!();
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_log_lanes_constant() {
assert!(LOG_N_LANES >= 2);
assert!(LOG_N_LANES <= 8);
}
#[test]
fn test_very_packed_elems_constant() {
assert!(LOG_N_VERY_PACKED_ELEMS <= 4);
}
}