From 9258b96acdb5743819cc37e660b3cee4006287b6 Mon Sep 17 00:00:00 2001 From: Tom Grosso Date: Tue, 1 Oct 2024 16:12:43 -0300 Subject: [PATCH] Add line folding --- stwo_cairo_verifier/src/fri.cairo | 1 + stwo_cairo_verifier/src/fri/folding.cairo | 70 +++++++++++++++++ stwo_cairo_verifier/src/lib.cairo | 1 + stwo_cairo_verifier/src/poly.cairo | 5 +- stwo_cairo_verifier/src/poly/line.cairo | 95 +++++++++++++++++++++-- stwo_cairo_verifier/src/utils.cairo | 13 ++++ 6 files changed, 178 insertions(+), 7 deletions(-) create mode 100644 stwo_cairo_verifier/src/fri.cairo create mode 100644 stwo_cairo_verifier/src/fri/folding.cairo diff --git a/stwo_cairo_verifier/src/fri.cairo b/stwo_cairo_verifier/src/fri.cairo new file mode 100644 index 00000000..df23e97e --- /dev/null +++ b/stwo_cairo_verifier/src/fri.cairo @@ -0,0 +1 @@ +pub mod folding; diff --git a/stwo_cairo_verifier/src/fri/folding.cairo b/stwo_cairo_verifier/src/fri/folding.cairo new file mode 100644 index 00000000..b76bf78e --- /dev/null +++ b/stwo_cairo_verifier/src/fri/folding.cairo @@ -0,0 +1,70 @@ +use stwo_cairo_verifier::fields::m31::M31Trait; +use stwo_cairo_verifier::circle::{Coset, CosetImpl}; +use stwo_cairo_verifier::poly::line::{LineDomain, LineDomainImpl}; +use stwo_cairo_verifier::fields::qm31::{QM31, qm31}; +use stwo_cairo_verifier::fields::m31::M31; +use stwo_cairo_verifier::utils::{bit_reverse_index, pow}; +use stwo_cairo_verifier::poly::line::{LineEvaluation, LineEvaluationImpl}; +pub const CIRCLE_TO_LINE_FOLD_STEP: u32 = 1; +pub const FOLD_STEP: u32 = 1; + +/// Folds a degree `d` polynomial into a degree `d/2` polynomial. +pub fn fold_line(eval: @LineEvaluation, alpha: QM31) -> LineEvaluation { + let domain = eval.domain; + let mut values: Array = array![]; + let mut i = 0; + while i < eval.values.len() / 2 { + let x = domain.at(bit_reverse_index(i * pow(2, FOLD_STEP), domain.log_size())); + let f_x = eval.values[2 * i]; + let f_neg_x = eval.values[2 * i + 1]; + let (f0, f1) = ibutterfly(*f_x, *f_neg_x, x.inverse()); + values.append(f0 + alpha * f1); + i += 1; + }; + LineEvaluationImpl::new(domain.double(), values) +} +pub fn ibutterfly(v0: QM31, v1: QM31, itwid: M31) -> (QM31, QM31) { + (v0 + v1, (v0 - v1) * itwid.into()) +} +mod test { + use stwo_cairo_verifier::poly::line::{ + LineEvaluation, SparseLineEvaluation, SparseLineEvaluationImpl + }; + use stwo_cairo_verifier::fields::m31::M31Trait; + use stwo_cairo_verifier::circle::{Coset, CosetImpl}; + use stwo_cairo_verifier::poly::line::{LineDomain, LineDomainImpl}; + use stwo_cairo_verifier::fields::qm31::{QM31, qm31}; + use stwo_cairo_verifier::fields::m31::M31; + use stwo_cairo_verifier::utils::{bit_reverse_index, pow}; + use stwo_cairo_verifier::fri::folding::{FOLD_STEP, CIRCLE_TO_LINE_FOLD_STEP}; + #[test] + fn test_fold_line_1() { + let domain = LineDomainImpl::new(CosetImpl::new(67108864, 1)); + let values = array![ + qm31(440443058, 1252794525, 1129773609, 1309365757), + qm31(974589897, 1592795796, 649052897, 863407657) + ]; + let sparse_line_evaluation = SparseLineEvaluation { + subline_evals: array![LineEvaluation { values, domain }] + }; + let alpha = qm31(1047716961, 506143067, 1065078409, 990356067); + let result = sparse_line_evaluation.fold(alpha); + let expected_result = array![qm31(515899232, 1030391528, 1006544539, 11142505)]; + assert_eq!(expected_result, result); + } + #[test] + fn test_fold_line_2() { + let domain = LineDomainImpl::new(CosetImpl::new(553648128, 1)); + let values = array![ + qm31(730692421, 1363821003, 2146256633, 106012305), + qm31(1387266930, 149259209, 1148988082, 1930518101) + ]; + let sparse_line_evaluation = SparseLineEvaluation { + subline_evals: array![LineEvaluation { values, domain }] + }; + let alpha = qm31(2084521793, 1326105747, 548635876, 1532708504); + let result = sparse_line_evaluation.fold(alpha); + let expected_result = array![qm31(1379727866, 1083096056, 1409020369, 1977903500)]; + assert_eq!(expected_result, result); + } +} diff --git a/stwo_cairo_verifier/src/lib.cairo b/stwo_cairo_verifier/src/lib.cairo index 35fa7705..8aa28a12 100644 --- a/stwo_cairo_verifier/src/lib.cairo +++ b/stwo_cairo_verifier/src/lib.cairo @@ -4,6 +4,7 @@ mod fields; mod poly; mod utils; mod vcs; +mod fri; pub use fields::{BaseField, SecureField}; diff --git a/stwo_cairo_verifier/src/poly.cairo b/stwo_cairo_verifier/src/poly.cairo index 41c6f0c7..0d5a1c72 100644 --- a/stwo_cairo_verifier/src/poly.cairo +++ b/stwo_cairo_verifier/src/poly.cairo @@ -1,3 +1,4 @@ pub mod circle; -mod line; -mod utils; +pub mod line; +pub mod utils; + diff --git a/stwo_cairo_verifier/src/poly/line.cairo b/stwo_cairo_verifier/src/poly/line.cairo index 74dfaba4..8493b0c6 100644 --- a/stwo_cairo_verifier/src/poly/line.cairo +++ b/stwo_cairo_verifier/src/poly/line.cairo @@ -1,21 +1,33 @@ -use stwo_cairo_verifier::fields::SecureField; -use stwo_cairo_verifier::fields::m31::m31; +use core::option::OptionTrait; +use core::clone::Clone; +use core::result::ResultTrait; use stwo_cairo_verifier::poly::utils::fold; +use stwo_cairo_verifier::fields::SecureField; +use stwo_cairo_verifier::fields::m31::{M31, m31, M31Trait}; +use stwo_cairo_verifier::fields::qm31::{QM31, qm31}; +use stwo_cairo_verifier::utils::pow; +use stwo_cairo_verifier::circle::{Coset, CosetImpl}; +use stwo_cairo_verifier::fri::folding::fold_line; -/// A univariate polynomial represented by its coefficients in the line part of the FFT-basis -/// in bit reversed order. -#[derive(Drop, Clone)] +/// A univariate polynomial defined on a [LineDomain]. +#[derive(Debug, Drop, Clone)] pub struct LinePoly { + /// Coefficients of the polynomial in [line_ifft] algorithm's basis. + /// + /// The coefficients are stored in bit-reversed order. pub coeffs: Array, + /// The number of coefficients stored as `log2(len(coeffs))`. pub log_size: u32, } #[generate_trait] pub impl LinePolyImpl of LinePolyTrait { + /// Returns the number of coefficients. fn len(self: @LinePoly) -> usize { self.coeffs.len() } + /// Evaluates the polynomial at a single point. fn eval_at_point(self: @LinePoly, mut x: SecureField) -> SecureField { let mut doublings = array![]; let mut i = 0; @@ -30,6 +42,79 @@ pub impl LinePolyImpl of LinePolyTrait { } } +/// Domain comprising of the x-coordinates of points in a [Coset]. +/// +/// For use with univariate polynomials. +#[derive(Copy, Drop, Debug)] +pub struct LineDomain { + pub coset: Coset, +} + +#[generate_trait] +pub impl LineDomainImpl of LineDomainTrait { + /// Returns a domain comprising of the x-coordinates of points in a coset. + fn new(coset: Coset) -> LineDomain { + LineDomain { coset: coset } + } + + /// Returns the `i`th domain element. + fn at(self: @LineDomain, index: usize) -> M31 { + self.coset.at(index).x + } + + /// Returns the size of the domain. + fn size(self: @LineDomain) -> usize { + self.coset.size() + } + + /// Returns the log size of the domain. + fn log_size(self: @LineDomain) -> usize { + *self.coset.log_size + } + + /// Returns a new domain comprising of all points in current domain doubled. + fn double(self: @LineDomain) -> LineDomain { + LineDomain { coset: self.coset.double() } + } +} + +/// Evaluations of a univariate polynomial on a [LineDomain]. +#[derive(Drop)] +pub struct LineEvaluation { + pub values: Array, + pub domain: LineDomain +} + +#[generate_trait] +pub impl LineEvaluationImpl of LineEvaluationTrait { + /// Creates new [LineEvaluation] from a set of polynomial evaluations over a [LineDomain]. + fn new(domain: LineDomain, values: Array) -> LineEvaluation { + assert_eq!(values.len(), domain.size()); + LineEvaluation { values: values, domain: domain } + } +} + +/// Holds a small foldable subset of univariate SecureField polynomial evaluations. +/// Evaluation is held at the CPU backend. +#[derive(Drop)] +pub struct SparseLineEvaluation { + pub subline_evals: Array, +} + +#[generate_trait] +pub impl SparseLineEvaluationImpl of SparseLineEvaluationTrait { + fn fold(self: @SparseLineEvaluation, alpha: QM31) -> Array { + let mut i = 0; + let mut res: Array = array![]; + while i < self.subline_evals.len() { + let line_evaluation = fold_line(self.subline_evals[i], alpha); + res.append(*line_evaluation.values.at(0)); + i += 1; + }; + return res; + } +} + #[cfg(test)] mod tests { diff --git a/stwo_cairo_verifier/src/utils.cairo b/stwo_cairo_verifier/src/utils.cairo index b444247c..66b3ecd9 100644 --- a/stwo_cairo_verifier/src/utils.cairo +++ b/stwo_cairo_verifier/src/utils.cairo @@ -97,6 +97,19 @@ pub fn pow(base: u32, mut exponent: u32) -> u32 { result } +pub fn bit_reverse_index(mut index: usize, mut bits: u32) -> usize { + assert!(bits < 32); + let mut result = 0; + let mut pow_of_two = 1; + while bits > 0 { + result *= 2; + result = result | ((index / pow_of_two) & 1); + pow_of_two *= 2; + bits -= 1; + }; + result +} + #[cfg(test)] mod tests { use super::pow;