#![cfg_attr(feature = "prover", feature(portable_simd))]
#![cfg_attr(not(feature = "std"), no_std)]
mod component;
#[cfg(feature = "prover")]
pub mod expr;
mod info;
pub mod logup;
mod point;
pub mod preprocessed_columns;
#[cfg(all(feature = "prover", feature = "std"))]
mod prover;
use core::array;
use core::fmt::Debug;
use core::ops::{Add, AddAssign, Mul, Neg, Sub};
pub use component::{FrameworkComponent, FrameworkEval, TraceLocationAllocator};
pub use info::InfoEvaluator;
use num_traits::{One, Zero};
pub use point::PointEvaluator;
use preprocessed_columns::PreProcessedColumnId;
#[cfg(all(feature = "prover", feature = "std"))]
pub use prover::{
assert_constraints_on_polys, assert_constraints_on_trace, relation_tracker, AssertEvaluator,
CpuDomainEvaluator, FractionWriter, LogupColGenerator, LogupTraceGenerator,
SimdDomainEvaluator,
};
#[cfg(all(feature = "prover", feature = "std", feature = "gpu"))]
pub use prover::{
is_gpu_constraint_kernels_enabled, set_gpu_constraint_kernels_enabled, will_use_gpu_kernels,
GpuDomainEvaluator,
};
use std_shims::Vec;
use stwo::core::fields::m31::BaseField;
use stwo::core::fields::qm31::{SecureField, SECURE_EXTENSION_DEGREE};
use stwo::core::fields::FieldExpOps;
use stwo::core::Fraction;
#[rustfmt::skip]
pub use stwo::core::verifier::PREPROCESSED_TRACE_IDX;
pub const ORIGINAL_TRACE_IDX: usize = 1;
pub const INTERACTION_TRACE_IDX: usize = 2;
type Batching = Vec<usize>;
pub trait EvalAtRow {
type F: FieldExpOps
+ Clone
+ Debug
+ Zero
+ Neg<Output = Self::F>
+ AddAssign
+ AddAssign<BaseField>
+ Add<Self::F, Output = Self::F>
+ Sub<Self::F, Output = Self::F>
+ Mul<BaseField, Output = Self::F>
+ Add<SecureField, Output = Self::EF>
+ Mul<SecureField, Output = Self::EF>
+ Neg<Output = Self::F>
+ From<BaseField>;
type EF: One
+ Clone
+ Debug
+ Zero
+ Neg<Output = Self::EF>
+ AddAssign
+ Add<BaseField, Output = Self::EF>
+ Mul<BaseField, Output = Self::EF>
+ Add<SecureField, Output = Self::EF>
+ Sub<SecureField, Output = Self::EF>
+ Mul<SecureField, Output = Self::EF>
+ Add<Self::F, Output = Self::EF>
+ Mul<Self::F, Output = Self::EF>
+ Sub<Self::EF, Output = Self::EF>
+ Mul<Self::EF, Output = Self::EF>
+ From<SecureField>
+ From<Self::F>;
fn next_trace_mask(&mut self) -> Self::F {
let [mask_item] = self.next_interaction_mask(ORIGINAL_TRACE_IDX, [0]);
mask_item
}
fn get_preprocessed_column(&mut self, _column: PreProcessedColumnId) -> Self::F {
let [mask_item] = self.next_interaction_mask(PREPROCESSED_TRACE_IDX, [0]);
mask_item
}
fn next_interaction_mask<const N: usize>(
&mut self,
interaction: usize,
offsets: [isize; N],
) -> [Self::F; N];
fn next_extension_interaction_mask<const N: usize>(
&mut self,
interaction: usize,
offsets: [isize; N],
) -> [Self::EF; N] {
let mut res_col_major =
array::from_fn(|_| self.next_interaction_mask(interaction, offsets).into_iter());
array::from_fn(|_| {
Self::combine_ef(res_col_major.each_mut().map(|iter| iter.next().unwrap()))
})
}
fn add_constraint<G>(&mut self, constraint: G)
where
Self::EF: Mul<G, Output = Self::EF> + From<G>;
fn add_intermediate(&mut self, val: Self::F) -> Self::F {
val
}
fn add_extension_intermediate(&mut self, val: Self::EF) -> Self::EF {
val
}
fn combine_ef(values: [Self::F; SECURE_EXTENSION_DEGREE]) -> Self::EF;
fn add_to_relation<R: Relation<Self::F, Self::EF>>(
&mut self,
entry: RelationEntry<'_, Self::F, Self::EF, R>,
) {
let frac = Fraction::new(
entry.multiplicity.clone(),
entry.relation.combine(entry.values),
);
self.write_logup_frac(frac);
}
fn write_logup_frac(&mut self, _fraction: Fraction<Self::EF, Self::EF>) {
unimplemented!()
}
fn finalize_logup_batched(&mut self, _batching: &Batching) {
unimplemented!()
}
fn finalize_logup(&mut self) {
unimplemented!();
}
fn finalize_logup_in_pairs(&mut self) {
unimplemented!();
}
}
macro_rules! logup_proxy {
() => {
fn write_logup_frac(&mut self, fraction: Fraction<Self::EF, Self::EF>) {
if self.logup.fracs.is_empty() {
self.logup.is_finalized = false;
}
self.logup.fracs.push(fraction.clone());
}
fn finalize_logup_batched(&mut self, batching: &crate::Batching) {
assert!(!self.logup.is_finalized, "LogupAtRow was already finalized");
assert_eq!(
batching.len(),
self.logup.fracs.len(),
"Batching must be of the same length as the number of entries"
);
let last_batch = *batching.iter().max().unwrap();
let mut fracs_by_batch =
hashbrown::HashMap::<usize, std_shims::Vec<Fraction<Self::EF, Self::EF>>>::new();
for (batch, frac) in batching.iter().zip(self.logup.fracs.iter()) {
fracs_by_batch
.entry(*batch)
.or_insert_with(std_shims::Vec::new)
.push(frac.clone());
}
let keys_set: hashbrown::HashSet<_> = fracs_by_batch.keys().cloned().collect();
let all_batches_set: hashbrown::HashSet<_> = (0..last_batch + 1).collect();
assert_eq!(
keys_set, all_batches_set,
"Batching must contain all consecutive batches"
);
let mut prev_col_cumsum = <Self::EF as num_traits::Zero>::zero();
for batch_id in (0..last_batch) {
let cur_frac: Fraction<_, _> = fracs_by_batch[&batch_id].iter().cloned().sum();
let [cur_cumsum] =
self.next_extension_interaction_mask(self.logup.interaction, [0]);
let diff = cur_cumsum.clone() - prev_col_cumsum.clone();
prev_col_cumsum = cur_cumsum;
self.add_constraint(diff * cur_frac.denominator - cur_frac.numerator);
}
let frac: Fraction<_, _> = fracs_by_batch[&last_batch].clone().into_iter().sum();
let [prev_row_cumsum, cur_cumsum] =
self.next_extension_interaction_mask(self.logup.interaction, [-1, 0]);
let diff = cur_cumsum - prev_row_cumsum - prev_col_cumsum.clone();
let shifted_diff = diff + self.logup.cumsum_shift.clone();
self.add_constraint(shifted_diff * frac.denominator - frac.numerator);
self.logup.is_finalized = true;
}
fn finalize_logup(&mut self) {
let batches = (0..self.logup.fracs.len()).collect();
self.finalize_logup_batched(&batches)
}
fn finalize_logup_in_pairs(&mut self) {
let batches = (0..self.logup.fracs.len()).map(|n| n / 2).collect();
self.finalize_logup_batched(&batches)
}
};
}
pub(crate) use logup_proxy;
pub trait RelationEFTraitBound<F: Clone>:
Clone + Zero + From<F> + From<SecureField> + Mul<F, Output = Self> + Sub<Self, Output = Self>
{
}
impl<F, EF> RelationEFTraitBound<F> for EF
where
F: Clone,
EF: Clone + Zero + From<F> + From<SecureField> + Mul<F, Output = EF> + Sub<EF, Output = EF>,
{
}
pub trait Relation<F: Clone, EF: RelationEFTraitBound<F>>: Sized {
fn combine(&self, values: &[F]) -> EF;
fn get_name(&self) -> &str;
fn get_size(&self) -> usize;
}
pub struct RelationEntry<'a, F: Clone, EF: RelationEFTraitBound<F>, R: Relation<F, EF>> {
relation: &'a R,
multiplicity: EF,
values: &'a [F],
}
impl<'a, F: Clone, EF: RelationEFTraitBound<F>, R: Relation<F, EF>> RelationEntry<'a, F, EF, R> {
pub const fn new(relation: &'a R, multiplicity: EF, values: &'a [F]) -> Self {
Self {
relation,
multiplicity,
values,
}
}
}
#[macro_export]
macro_rules! relation {
($name:tt, $size:tt) => {
#[derive(Clone, Debug, PartialEq)]
pub struct $name($crate::logup::LookupElements<$size>);
#[allow(dead_code)]
impl $name {
pub fn dummy() -> Self {
Self($crate::logup::LookupElements::dummy())
}
pub fn draw(channel: &mut impl stwo::core::channel::Channel) -> Self {
Self($crate::logup::LookupElements::draw(channel))
}
}
impl<F: Clone, EF: $crate::RelationEFTraitBound<F>> $crate::Relation<F, EF> for $name {
fn combine(&self, values: &[F]) -> EF {
self.0.combine(values)
}
fn get_name(&self) -> &str {
stringify!($name)
}
fn get_size(&self) -> usize {
$size
}
}
};
}
#[cfg(test)]
#[macro_export]
macro_rules! m31 {
($m:expr) => {
stwo::core::fields::m31::M31::from_u32_unchecked($m)
};
}
#[cfg(test)]
#[macro_export]
macro_rules! qm31 {
($m0:expr, $m1:expr, $m2:expr, $m3:expr) => {{
stwo::core::fields::qm31::QM31::from_u32_unchecked($m0, $m1, $m2, $m3)
}};
}