Skip to content

Commit

Permalink
Add line folding
Browse files Browse the repository at this point in the history
  • Loading branch information
atgrosso committed Oct 1, 2024
1 parent 81823af commit 9258b96
Show file tree
Hide file tree
Showing 6 changed files with 178 additions and 7 deletions.
1 change: 1 addition & 0 deletions stwo_cairo_verifier/src/fri.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod folding;
70 changes: 70 additions & 0 deletions stwo_cairo_verifier/src/fri/folding.cairo
Original file line number Diff line number Diff line change
@@ -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<QM31> = 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);
}
}
1 change: 1 addition & 0 deletions stwo_cairo_verifier/src/lib.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ mod fields;
mod poly;
mod utils;
mod vcs;
mod fri;

pub use fields::{BaseField, SecureField};

Expand Down
5 changes: 3 additions & 2 deletions stwo_cairo_verifier/src/poly.cairo
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod circle;
mod line;
mod utils;
pub mod line;
pub mod utils;

95 changes: 90 additions & 5 deletions stwo_cairo_verifier/src/poly/line.cairo
Original file line number Diff line number Diff line change
@@ -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<SecureField>,
/// 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;
Expand All @@ -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<QM31>,
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<QM31>) -> 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<LineEvaluation>,
}

#[generate_trait]
pub impl SparseLineEvaluationImpl of SparseLineEvaluationTrait {
fn fold(self: @SparseLineEvaluation, alpha: QM31) -> Array<QM31> {
let mut i = 0;
let mut res: Array<QM31> = 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 {
Expand Down
13 changes: 13 additions & 0 deletions stwo_cairo_verifier/src/utils.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit 9258b96

Please sign in to comment.