From 20412e4a9f00507d771f24d66e4dbd59d0c66849 Mon Sep 17 00:00:00 2001 From: Shahar Papini <43779613+spapinistarkware@users.noreply.github.com> Date: Sun, 24 Mar 2024 15:21:31 +0200 Subject: [PATCH] QuotientOps (#481) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This change is [Reviewable](https://reviewable.io/reviews/starkware-libs/stwo/481) --- src/core/backend/cpu/mod.rs | 1 + src/core/backend/cpu/quotients.rs | 95 +++++++++++++++++++++++++ src/core/commitment_scheme/mod.rs | 2 + src/core/commitment_scheme/quotients.rs | 30 ++++++++ 4 files changed, 128 insertions(+) create mode 100644 src/core/backend/cpu/quotients.rs create mode 100644 src/core/commitment_scheme/quotients.rs diff --git a/src/core/backend/cpu/mod.rs b/src/core/backend/cpu/mod.rs index 9d32bbfbc..5927165d0 100644 --- a/src/core/backend/cpu/mod.rs +++ b/src/core/backend/cpu/mod.rs @@ -1,5 +1,6 @@ mod circle; mod fri; +pub mod quotients; use std::fmt::Debug; diff --git a/src/core/backend/cpu/quotients.rs b/src/core/backend/cpu/quotients.rs new file mode 100644 index 000000000..a88294b84 --- /dev/null +++ b/src/core/backend/cpu/quotients.rs @@ -0,0 +1,95 @@ +use num_traits::Zero; + +use super::CPUBackend; +use crate::core::circle::CirclePoint; +use crate::core::commitment_scheme::quotients::{ColumnSampleBatch, QuotientOps}; +use crate::core::constraints::{complex_conjugate_line, pair_vanishing}; +use crate::core::fields::m31::BaseField; +use crate::core::fields::qm31::SecureField; +use crate::core::fields::secure_column::SecureColumn; +use crate::core::fields::{ComplexConjugate, FieldExpOps}; +use crate::core::poly::circle::{CircleDomain, CircleEvaluation}; +use crate::core::poly::BitReversedOrder; +use crate::core::utils::bit_reverse_index; + +impl QuotientOps for CPUBackend { + fn accumulate_quotients( + domain: CircleDomain, + columns: &[&CircleEvaluation], + random_coeff: SecureField, + samples: &[ColumnSampleBatch], + ) -> SecureColumn { + let mut res = SecureColumn::zeros(domain.size()); + for row in 0..domain.size() { + // TODO(alonh): Make an efficient bit reverse domain iterator, possibly for AVX backend. + let domain_point = domain.at(bit_reverse_index(row, domain.log_size())); + let row_value = + accumulate_row_quotients(samples, columns, row, random_coeff, domain_point); + res.set(row, row_value); + } + res + } +} + +pub fn accumulate_row_quotients( + samples: &[ColumnSampleBatch], + columns: &[&CircleEvaluation], + row: usize, + random_coeff: SecureField, + domain_point: CirclePoint, +) -> SecureField { + let mut row_accumlator = SecureField::zero(); + for sample in samples { + let mut numerator = SecureField::zero(); + for (column_index, sampled_value) in &sample.column_indices_and_values { + let column = &columns[*column_index]; + let value = column[row]; + let linear_term = complex_conjugate_line(sample.point, *sampled_value, domain_point); + numerator = numerator * random_coeff + value - linear_term; + } + + let denominator = pair_vanishing( + sample.point, + sample.point.complex_conjugate(), + domain_point.into_ef(), + ); + + row_accumlator = row_accumlator + * random_coeff.pow(sample.column_indices_and_values.len() as u128) + + numerator / denominator; + } + row_accumlator +} + +#[cfg(test)] +mod tests { + use crate::core::backend::cpu::{CPUCircleEvaluation, CPUCirclePoly}; + use crate::core::backend::CPUBackend; + use crate::core::circle::SECURE_FIELD_CIRCLE_GEN; + use crate::core::commitment_scheme::quotients::{ColumnSampleBatch, QuotientOps}; + use crate::core::poly::circle::CanonicCoset; + use crate::{m31, qm31}; + + #[test] + fn test_quotients_are_low_degree() { + const LOG_SIZE: u32 = 7; + let polynomial = CPUCirclePoly::new((0..1 << LOG_SIZE).map(|i| m31!(i)).collect()); + let eval_domain = CanonicCoset::new(LOG_SIZE + 1).circle_domain(); + let eval = polynomial.evaluate(eval_domain); + let point = SECURE_FIELD_CIRCLE_GEN; + let value = polynomial.eval_at_point(point); + let coeff = qm31!(1, 2, 3, 4); + let quot_eval = CPUBackend::accumulate_quotients( + eval_domain, + &[&eval], + coeff, + &[ColumnSampleBatch { + point, + column_indices_and_values: vec![(0, value)], + }], + ); + let quot_poly_base_field = + CPUCircleEvaluation::new(eval_domain, quot_eval.columns[0].clone()).interpolate(); + assert!(quot_poly_base_field.is_in_fft_space(LOG_SIZE)); + } +} diff --git a/src/core/commitment_scheme/mod.rs b/src/core/commitment_scheme/mod.rs index 8b7a24569..66fb39c36 100644 --- a/src/core/commitment_scheme/mod.rs +++ b/src/core/commitment_scheme/mod.rs @@ -5,6 +5,8 @@ //! the unique decoding regime. This is enough for a STARK proof though, where we only want to imply //! the existence of such polynomials, and are ok with having a small decoding list. //! Note: Opened points cannot come from the commitment domain. + +pub mod quotients; pub mod utils; use std::iter::zip; use std::ops::Deref; diff --git a/src/core/commitment_scheme/quotients.rs b/src/core/commitment_scheme/quotients.rs new file mode 100644 index 000000000..a0792de6c --- /dev/null +++ b/src/core/commitment_scheme/quotients.rs @@ -0,0 +1,30 @@ +use crate::core::backend::Backend; +use crate::core::circle::CirclePoint; +use crate::core::fields::m31::BaseField; +use crate::core::fields::qm31::SecureField; +use crate::core::fields::secure_column::SecureColumn; +use crate::core::poly::circle::{CircleDomain, CircleEvaluation}; +use crate::core::poly::BitReversedOrder; + +pub trait QuotientOps: Backend { + /// Accumulates the quotients of the columns at the given domain. + /// For a column f(x), and a point sample (p,v), the quotient is + /// (f(x) - V0(x))/V1(x) + /// where V0(p)=v, V0(conj(p))=conj(v), and V1 is a vanishing polynomial for p,conj(p). + /// This ensures that if f(p)=v, then the quotient is a polynomial. + /// The result is a linear combination of the quotients using powers of random_coeff. + fn accumulate_quotients( + domain: CircleDomain, + columns: &[&CircleEvaluation], + random_coeff: SecureField, + samples: &[ColumnSampleBatch], + ) -> SecureColumn; +} + +/// A batch of column samplings at a point. +pub struct ColumnSampleBatch { + /// The point at which the columns are sampled. + pub point: CirclePoint, + /// The sampled column indices and their values at the point. + pub column_indices_and_values: Vec<(usize, SecureField)>, +}