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 [](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)>,
+}