Skip to content

Commit

Permalink
Add circle evaluatio with fold_circle_into_line
Browse files Browse the repository at this point in the history
  • Loading branch information
atgrosso committed Oct 9, 2024
1 parent 0a98ad5 commit 7a03c1b
Show file tree
Hide file tree
Showing 4 changed files with 218 additions and 5 deletions.
3 changes: 3 additions & 0 deletions stwo_cairo_verifier/src/fri.cairo
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
pub mod folding;
pub mod verifier;

use stwo_cairo_verifier::fields::m31::M31Trait;
use stwo_cairo_verifier::circle::{Coset, CosetImpl};
use stwo_cairo_verifier::poly::line::{LineDomain, LineDomainImpl};
Expand Down
68 changes: 68 additions & 0 deletions stwo_cairo_verifier/src/fri/folding.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
use stwo_cairo_verifier::fields::m31::M31Trait;
use stwo_cairo_verifier::circle::{Coset, CosetImpl};
use stwo_cairo_verifier::poly::circle::{CircleDomain, CircleDomainImpl};
use stwo_cairo_verifier::poly::line::{LineDomain, LineDomainImpl};
use stwo_cairo_verifier::fields::qm31::{QM31, qm31, QM31Trait};
use stwo_cairo_verifier::fields::m31::M31;
use stwo_cairo_verifier::utils::{bit_reverse_index, pow};
use stwo_cairo_verifier::fri::verifier::{FOLD_STEP, CIRCLE_TO_LINE_FOLD_STEP};
use stwo_cairo_verifier::poly::circle::{
CircleEvaluation, SparseCircleEvaluation, SparseCircleEvaluationImpl
};
use stwo_cairo_verifier::poly::line::{LineEvaluation, LineEvaluationImpl};
use stwo_cairo_verifier::fri::ibutterfly;

/// Folds and accumulates a degree `d` circle polynomial into a degree `d/2` univariate
/// polynomial.
///
/// Let `src` be the evaluation of a circle polynomial `f` on a
/// [`CircleDomain`] `E`. This function computes evaluations of `f' = f0
/// + alpha * f1` on the x-coordinates of `E` such that `2f(p) = f0(px) + py * f1(px)`. The
/// evaluations of `f'` are accumulated into `dst` by the formula `dst = dst * alpha^2 +
/// f'`.
pub fn fold_circle_into_line(eval: @CircleEvaluation, alpha: QM31) -> LineEvaluation {
let domain = eval.domain;
let mut values = array![];
let mut i = 0;
while i < eval.values.len() / 2 {
let p = domain
.at(bit_reverse_index(i * pow(2, CIRCLE_TO_LINE_FOLD_STEP), domain.log_size()));
let f_p = eval.values[2 * i];
let f_neg_p = eval.values[2 * i + 1];
let (f0, f1) = ibutterfly(*f_p, *f_neg_p, p.y.inverse());
values.append(f0 + alpha * f1);
i += 1;
};
LineEvaluation { values, domain: LineDomainImpl::new(*domain.half_coset) }
}

#[cfg(test)]
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::circle::{CircleDomain, CircleDomainImpl};
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::verifier::{FOLD_STEP, CIRCLE_TO_LINE_FOLD_STEP};
use stwo_cairo_verifier::poly::circle::{
CircleEvaluation, CircleEvaluationImpl, SparseCircleEvaluation, SparseCircleEvaluationImpl
};

#[test]
fn test_fold_circle_into_line_1() {
let domain = CircleDomain { half_coset: CosetImpl::new(553648128, 0) };
let values = array![qm31(807167738, 0, 0, 0), qm31(1359821401, 0, 0, 0)];
let sparse_circle_evaluation: SparseCircleEvaluation = SparseCircleEvaluation {
subcircle_evals: array![CircleEvaluationImpl::new(domain, values)]
};
let alpha = qm31(260773061, 362745443, 1347591543, 1084609991);
let result = sparse_circle_evaluation.fold(alpha);
let expected_result = array![qm31(730692421, 1363821003, 2146256633, 106012305)];
assert_eq!(expected_result, result);
}
}
2 changes: 2 additions & 0 deletions stwo_cairo_verifier/src/fri/verifier.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub const CIRCLE_TO_LINE_FOLD_STEP: u32 = 1;
pub const FOLD_STEP: u32 = 1;
150 changes: 145 additions & 5 deletions stwo_cairo_verifier/src/poly/circle.cairo
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
use stwo_cairo_verifier::circle::CirclePointTrait;
use core::option::OptionTrait;
use core::clone::Clone;
use core::result::ResultTrait;
use stwo_cairo_verifier::fields::qm31::{QM31, qm31};
use stwo_cairo_verifier::fields::m31::{M31, m31};
use stwo_cairo_verifier::utils::pow;
use stwo_cairo_verifier::circle::{
Coset, CosetImpl, CirclePoint, CirclePointM31Impl, M31_CIRCLE_GEN, CIRCLE_ORDER
};
use stwo_cairo_verifier::fri::folding::fold_circle_into_line;
use core::num::traits::ops::wrapping::WrappingSub;

/// A valid domain for circle polynomial interpolation and evaluation.
///
Expand All @@ -18,32 +22,118 @@ pub struct CircleDomain {

#[generate_trait]
pub impl CircleDomainImpl of CircleDomainTrait {
/// Given a coset `C + <G_n>`, constructs the circle domain `+-C + <G_n>` (i.e.
/// this coset and its conjugate).
fn new(half_coset: Coset) -> CircleDomain {
CircleDomain { half_coset }
}

/// Returns the size of the domain.
fn size(self: @CircleDomain) -> usize {
pow(2, self.log_size())
}

/// Returns the log size of the domain.
fn log_size(self: @CircleDomain) -> usize {
*self.half_coset.log_size + 1
}

/// Returns the circle point index of the `i`th domain element.
fn index_at(self: @CircleDomain, index: usize) -> usize {
if index < self.half_coset.size() {
self.half_coset.index_at(index)
} else {
CIRCLE_ORDER - self.half_coset.index_at(index - self.half_coset.size())
CIRCLE_ORDER.wrapping_sub(self.half_coset.index_at(index - self.half_coset.size()))
}
}

/// Returns the `i` th domain element.
fn at(self: @CircleDomain, index: usize) -> CirclePoint<M31> {
let mut scalar = self.index_at(index).into();
M31_CIRCLE_GEN.mul(ref scalar)
}

fn new_with_log_size(log_size: u32) -> CircleDomain {
CircleDomain { half_coset: CosetImpl::half_odds(log_size - 1) }
}
}

/// An evaluation defined on a [CircleDomain].
///
/// The values are ordered according to the [CircleDomain] ordering.
#[derive(Debug, Drop, Clone, PartialEq, Eq)]
pub struct CircleEvaluation {
pub values: Array<QM31>,
pub domain: CircleDomain,
_eval_order: BitReversedOrder
}

#[derive(Debug, Drop, Clone, PartialEq)]
pub struct BitReversedOrder {}

#[generate_trait]
pub impl CircleEvaluationImpl of CircleEvaluationTrait {
fn new(domain: CircleDomain, values: Array<QM31>) -> CircleEvaluation {
CircleEvaluation { values: values, domain: domain, _eval_order: BitReversedOrder {} }
}
}

/// Holds a foldable subset of circle polynomial evaluations.
#[derive(Drop, Clone, Debug, PartialEq)]
pub struct SparseCircleEvaluation {
pub subcircle_evals: Array<CircleEvaluation>
}

#[generate_trait]
pub impl SparseCircleEvaluationImpl of SparseCircleEvaluationImplTrait {
fn new(subcircle_evals: Array<CircleEvaluation>) -> SparseCircleEvaluation {
SparseCircleEvaluation { subcircle_evals: subcircle_evals }
}

fn accumulate(
self: @SparseCircleEvaluation, rhs: @SparseCircleEvaluation, alpha: QM31
) -> SparseCircleEvaluation {
assert_eq!(self.subcircle_evals.len(), rhs.subcircle_evals.len());
let mut subcircle_evals = array![];
let mut i = 0;
while i < self.subcircle_evals.len() {
let lhs = self.subcircle_evals[i];
let rhs = rhs.subcircle_evals[i];
let mut values = array![];
assert_eq!(lhs.values.len(), rhs.values.len());
let mut j = 0;
while j < lhs.values.len() {
values.append(*lhs.values[j] * alpha + *rhs.values[j]);
j += 1;
};
subcircle_evals.append(CircleEvaluationImpl::new(*lhs.domain, values));
i += 1;
};

SparseCircleEvaluation { subcircle_evals }
}

fn fold(self: @SparseCircleEvaluation, alpha: QM31) -> Array<QM31> {
let mut i = 0;
let mut res: Array<QM31> = array![];
while i < self.subcircle_evals.len() {
let circle_evaluation = fold_circle_into_line(self.subcircle_evals[i], alpha);
res.append(*circle_evaluation.values.at(0));
i += 1;
};
return res;
}
}

#[cfg(test)]
mod tests {
use super::{CircleDomain, CircleDomainTrait};
use stwo_cairo_verifier::circle::{
Coset, CosetImpl, CirclePoint, CirclePointM31Impl, M31_CIRCLE_GEN, CIRCLE_ORDER
use super::{
CircleDomain, CircleDomainTrait, CircleEvaluation, CircleEvaluationImpl, SparseCircleEvaluation,
SparseCircleEvaluationImplTrait
};
use stwo_cairo_verifier::fields::m31::{M31, m31};
use stwo_cairo_verifier::circle::{Coset, CosetImpl, CirclePoint};
use stwo_cairo_verifier::fields::m31::m31;
use stwo_cairo_verifier::fields::qm31::qm31;

#[test]
fn test_circle_domain_at_1() {
Expand All @@ -64,4 +154,54 @@ mod tests {

assert_eq!(result, CirclePoint { x: m31(9803698), y: m31(2079025011) });
}

#[test]
fn test_accumulate_1() {
let alpha = qm31(984186742, 463356387, 533637878, 1417871498);
let lhs = SparseCircleEvaluation {
subcircle_evals: array![
CircleEvaluationImpl::new(
CircleDomain { half_coset: CosetImpl::new(16777216, 0) },
array![qm31(28672, 0, 0, 0), qm31(36864, 0, 0, 0)]
),
CircleEvaluationImpl::new(
CircleDomain { half_coset: CosetImpl::new(2030043136, 0) },
array![qm31(28672, 0, 0, 0), qm31(36864, 0, 0, 0)]
),
CircleEvaluationImpl::new(
CircleDomain { half_coset: CosetImpl::new(2097152000, 0) },
array![qm31(2147454975, 0, 0, 0), qm31(2147446783, 0, 0, 0)]
),
]
};
let rhs = lhs.clone();
let result = lhs.accumulate(@rhs, alpha);

assert_eq!(result, SparseCircleEvaluation {
subcircle_evals: array![
CircleEvaluationImpl::new(
CircleDomain { half_coset: CosetImpl::new(16777216, 0) },
array![
qm31(667173716, 1020487722, 1791736788, 1346152946),
qm31(1471361534, 84922130, 1076528072, 810417939)
]
),
CircleEvaluationImpl::new(
CircleDomain { half_coset: CosetImpl::new(2030043136, 0) },
array![
qm31(667173716, 1020487722, 1791736788, 1346152946),
qm31(1471361534, 84922130, 1076528072, 810417939)
]
),
CircleEvaluationImpl::new(
CircleDomain { half_coset: CosetImpl::new(2097152000, 0) },
array![
qm31(1480309931, 1126995925, 355746859, 801330701),
qm31(676122113, 2062561517, 1070955575, 1337065708)
]
),
]
});

}
}

0 comments on commit 7a03c1b

Please sign in to comment.