Skip to content

Commit

Permalink
Line evaluation with backend
Browse files Browse the repository at this point in the history
  • Loading branch information
spapinistarkware committed Feb 24, 2024
1 parent 3b76818 commit 3e0f1b0
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 111 deletions.
15 changes: 0 additions & 15 deletions src/core/backend/cpu/poly.rs → src/core/backend/cpu/circle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ use crate::core::poly::circle::{
CanonicCoset, CircleDomain, CircleEvaluation, CirclePoly, PolyOps,
};
use crate::core::poly::utils::fold;
use crate::core::poly::{BitReversedOrder, NaturalOrder};
use crate::core::utils::bit_reverse;

impl<F: ExtensionOf<BaseField>> PolyOps<F> for CPUBackend {
fn new_canonical_ordered(
Expand Down Expand Up @@ -56,19 +54,6 @@ impl<F: ExtensionOf<BaseField>> PolyOps<F> for CPUBackend {
CirclePoly::new(values)
}

// TODO(spapini): Remove
fn bit_reverse_natural(
eval: CircleEvaluation<Self, F, NaturalOrder>,
) -> CircleEvaluation<Self, F, BitReversedOrder> {
CircleEvaluation::new(eval.domain, bit_reverse(eval.values))
}

fn bit_reverse_reversed(
eval: CircleEvaluation<Self, F, BitReversedOrder>,
) -> CircleEvaluation<Self, F, NaturalOrder> {
CircleEvaluation::new(eval.domain, bit_reverse(eval.values))
}

fn eval_at_point<E: ExtensionOf<F>>(poly: &CirclePoly<Self, F>, point: CirclePoint<E>) -> E {
// TODO(Andrew): Allocation here expensive for small polynomials.
let mut mappings = vec![point.y, point.x];
Expand Down
19 changes: 16 additions & 3 deletions src/core/backend/cpu/mod.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
mod circle;

use std::fmt::Debug;

use super::{Backend, Column, FieldOps};
use crate::core::fields::Field;
use crate::core::poly::circle::{CircleEvaluation, CirclePoly};
use crate::core::poly::line::LineEvaluation;
use crate::core::poly::NaturalOrder;

mod poly;
use crate::core::utils::bit_reverse;

#[derive(Copy, Clone, Debug)]
pub struct CPUBackend;
Expand All @@ -14,9 +16,19 @@ impl Backend for CPUBackend {}

impl<F: Field> FieldOps<F> for CPUBackend {
type Column = Vec<F>;

fn bit_reverse_column(column: Self::Column) -> Self::Column {
bit_reverse(column)
}
}

impl<F: Clone + Debug> Column<F> for Vec<F> {
impl<F: Clone + Debug + Field> Column<F> for Vec<F> {
fn zeros(len: usize) -> Self {
vec![F::zero(); len]
}
fn to_vec(&self) -> Vec<F> {
self.clone()
}
fn len(&self) -> usize {
self.len()
}
Expand All @@ -25,3 +37,4 @@ impl<F: Clone + Debug> Column<F> for Vec<F> {
pub type CPUCirclePoly<F> = CirclePoly<CPUBackend, F>;
pub type CPUCircleEvaluation<F, EvalOrder = NaturalOrder> =
CircleEvaluation<CPUBackend, F, EvalOrder>;
pub type CPULineEvaluation<F, EvalOrder = NaturalOrder> = LineEvaluation<CPUBackend, F, EvalOrder>;
3 changes: 3 additions & 0 deletions src/core/backend/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,14 @@ pub trait Backend:

pub trait FieldOps<F: Field> {
type Column: Column<F>;
fn bit_reverse_column(column: Self::Column) -> Self::Column;
}

pub type Col<B, F> = <B as FieldOps<F>>::Column;

pub trait Column<F>: Clone + Debug + Index<usize, Output = F> + FromIterator<F> {
fn zeros(len: usize) -> Self;
fn to_vec(&self) -> Vec<F>;
fn len(&self) -> usize;
fn is_empty(&self) -> bool {
self.len() == 0
Expand Down
58 changes: 30 additions & 28 deletions src/core/fri.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use itertools::Itertools;
use num_traits::Zero;
use thiserror::Error;

use super::backend::cpu::CPULineEvaluation;
use super::backend::{CPUBackend, FieldOps};
use super::channel::Channel;
use super::fields::m31::BaseField;
Expand Down Expand Up @@ -141,7 +142,7 @@ impl<H: Hasher<NativeType = u8>> FriProver<H> {
columns: &[CircleEvaluation<B, F, BitReversedOrder>],
) -> (
Vec<FriLayerProver<H>>,
LineEvaluation<SecureField, BitReversedOrder>,
LineEvaluation<B, SecureField, BitReversedOrder>,
)
where
F: ExtensionOf<BaseField>,
Expand Down Expand Up @@ -195,11 +196,11 @@ impl<H: Hasher<NativeType = u8>> FriProver<H> {
fn commit_last_layer(
channel: &mut impl Channel<Digest = H::Hash>,
config: FriConfig,
evaluation: LineEvaluation<SecureField, BitReversedOrder>,
evaluation: LineEvaluation<B, SecureField, BitReversedOrder>,
) -> LinePoly<SecureField> {
assert_eq!(evaluation.len(), config.last_layer_domain_size());

let evaluation = evaluation.bit_reverse();
let evaluation = evaluation.bit_reverse().to_cpu();
let mut coeffs = evaluation.interpolate().into_ordered_coefficients();

let last_layer_degree_bound = 1 << config.log_last_layer_degree_bound;
Expand Down Expand Up @@ -380,7 +381,6 @@ impl<H: Hasher<NativeType = u8>> FriVerifier<H> {
where
F: ExtensionOf<BaseField>,
SecureField: ExtensionOf<F>,
B: FieldOps<F>,
{
assert_eq!(queries.log_domain_size, self.expected_query_log_domain_size);
assert_eq!(decommitted_values.len(), self.column_bounds.len());
Expand Down Expand Up @@ -649,8 +649,10 @@ impl<H: Hasher<NativeType = u8>> FriLayerVerifier<H> {
}
}

let actual_decommitment_evals =
sparse_evaluation.subline_evals.iter().flat_map(|e| &**e);
let actual_decommitment_evals = sparse_evaluation
.subline_evals
.iter()
.flat_map(|e| &e.values);

if !actual_decommitment_evals.eq(&expected_decommitment_evals) {
return Err(VerificationError::InnerLayerCommitmentInvalid {
Expand Down Expand Up @@ -752,14 +754,14 @@ impl<H: Hasher<NativeType = u8>> FriLayerVerifier<H> {
/// of size two. Each leaf of the merkle tree commits to a single coset evaluation.
// TODO(andrew): Support different step sizes.
struct FriLayerProver<H: Hasher> {
evaluation: LineEvaluation<SecureField, BitReversedOrder>,
evaluation: LineEvaluation<B, SecureField, BitReversedOrder>,
merkle_tree: MerkleTree<SecureField, H>,
}

impl<H: Hasher<NativeType = u8>> FriLayerProver<H> {
fn new(evaluation: LineEvaluation<SecureField, BitReversedOrder>) -> Self {
fn new(evaluation: LineEvaluation<B, SecureField, BitReversedOrder>) -> Self {
// TODO: Commit on slice.
let merkle_tree = MerkleTree::commit(vec![evaluation.to_vec()]);
let merkle_tree = MerkleTree::commit(vec![evaluation.values.to_vec()]);
#[allow(unreachable_code)]
FriLayerProver {
evaluation,
Expand Down Expand Up @@ -789,7 +791,7 @@ impl<H: Hasher<NativeType = u8>> FriLayerProver<H> {
continue;
}

let eval = self.evaluation[eval_position];
let eval = self.evaluation.values[eval_position];
evals_subset.push(eval);
}
}
Expand Down Expand Up @@ -831,7 +833,7 @@ impl<F: ExtensionOf<BaseField>> SparseCircleEvaluation<F> {
let buffer_domain = LineDomain::new(e.domain.half_coset);
let mut buffer = LineEvaluation::new(buffer_domain, vec![SecureField::zero()]);
fold_circle_into_line(&mut buffer, &e, alpha);
buffer[0]
buffer.values[0]
})
.collect()
}
Expand All @@ -840,47 +842,47 @@ impl<F: ExtensionOf<BaseField>> SparseCircleEvaluation<F> {
/// Holds a foldable subset of univariate polynomial evaluations.
#[derive(Debug, Clone)]
struct SparseLineEvaluation<F: ExtensionOf<BaseField>> {
subline_evals: Vec<LineEvaluation<F, BitReversedOrder>>,
subline_evals: Vec<CPULineEvaluation<F, BitReversedOrder>>,
}

impl<F: ExtensionOf<BaseField>> SparseLineEvaluation<F> {
/// # Panics
///
/// Panics if the evaluation domain sizes don't equal the folding factor.
fn new(subline_evals: Vec<LineEvaluation<F, BitReversedOrder>>) -> Self {
fn new(subline_evals: Vec<CPULineEvaluation<F, BitReversedOrder>>) -> Self {
let folding_factor = 1 << FOLD_STEP;
assert!(subline_evals.iter().all(|e| e.len() == folding_factor));
Self { subline_evals }
}

#[allow(dead_code)]
fn fold(self, alpha: F) -> Vec<F> {
self.subline_evals
.into_iter()
.map(|e| fold_line(&e, alpha)[0])
.map(|e| fold_line(&e, alpha).values[0])
.collect()
}
}

/// Folds a degree `d` polynomial into a degree `d/2` polynomial.
///
/// Let `evals` be a polynomial evaluated on a [LineDomain] `E`, `alpha` be a random field element
/// Let `eval` be a polynomial evaluated on a [LineDomain] `E`, `alpha` be a random field element
/// and `pi(x) = 2x^2 - 1` be the circle's x-coordinate doubling map. This function returns
/// `f' = f0 + alpha * f1` evaluated on `pi(E)` such that `2f(x) = f0(pi(x)) + x * f1(pi(x))`.
///
/// # Panics
///
/// Panics if there are less than two evaluations.
pub fn fold_line<F: ExtensionOf<BaseField>>(
evals: &LineEvaluation<F, BitReversedOrder>,
eval: &LineEvaluation<B, F, BitReversedOrder>,
alpha: F,
) -> LineEvaluation<F, BitReversedOrder> {
let n = evals.len();
assert!(n >= 2, "too few evals");
) -> LineEvaluation<B, F, BitReversedOrder> {
let n = eval.len();
assert!(n >= 2, "Evaluation too small");

let domain = evals.domain();
let domain = eval.domain();

let folded_evals = evals
let folded_values = eval
.values
.array_chunks()
.enumerate()
.map(|(i, &[f_x, f_neg_x])| {
Expand All @@ -893,7 +895,7 @@ pub fn fold_line<F: ExtensionOf<BaseField>>(
})
.collect();

LineEvaluation::new(domain.double(), folded_evals)
LineEvaluation::new(domain.double(), folded_values)
}

/// Folds and accumulates a degree `d` circle polynomial into a degree `d/2` univariate polynomial.
Expand All @@ -909,7 +911,7 @@ pub fn fold_line<F: ExtensionOf<BaseField>>(
// TODO(andrew): Make folding factor generic.
// TODO(andrew): Fold directly into FRI layer to prevent allocation.
fn fold_circle_into_line<F>(
dst: &mut LineEvaluation<SecureField, BitReversedOrder>,
dst: &mut LineEvaluation<B, SecureField, BitReversedOrder>,
src: &CircleEvaluation<B, F, BitReversedOrder>,
alpha: SecureField,
) where
Expand All @@ -921,7 +923,7 @@ fn fold_circle_into_line<F>(
let domain = src.domain;
let alpha_sq = alpha * alpha;

zip(&mut **dst, src.array_chunks())
zip(&mut dst.values, src.array_chunks())
.enumerate()
.for_each(|(i, (dst, &[f_p, f_neg_p]))| {
// TODO(andrew): Inefficient. Update when domain twiddles get stored in a buffer.
Expand All @@ -947,7 +949,7 @@ mod tests {

use super::{get_opening_positions, SparseCircleEvaluation, VerificationError};
use crate::commitment_scheme::blake2_hash::{Blake2sHash, Blake2sHasher};
use crate::core::backend::cpu::{CPUCircleEvaluation, CPUCirclePoly};
use crate::core::backend::cpu::{CPUCircleEvaluation, CPUCirclePoly, CPULineEvaluation};
use crate::core::channel::{Blake2sChannel, Channel};
use crate::core::circle::{CirclePointIndex, Coset};
use crate::core::constraints::{EvalByEvaluation, PolyOracle};
Expand Down Expand Up @@ -986,7 +988,7 @@ mod tests {

assert_eq!(drp_evals.len(), DEGREE / 2);
let drp_evals = drp_evals.bit_reverse();
for (i, (&drp_eval, x)) in zip(&*drp_evals, drp_domain).enumerate() {
for (i, (&drp_eval, x)) in zip(&drp_evals.values, drp_domain).enumerate() {
let f_e = even_poly.eval_at_point(x);
let f_o = odd_poly.eval_at_point(x);
assert_eq!(drp_eval, two * (f_e + alpha * f_o), "mismatch at {i}");
Expand Down Expand Up @@ -1255,7 +1257,7 @@ mod tests {

/// Returns the log degree bound of a polynomial.
fn log_degree_bound<F: ExtensionOf<BaseField>>(
polynomial: LineEvaluation<F, BitReversedOrder>,
polynomial: CPULineEvaluation<F, BitReversedOrder>,
) -> u32 {
let polynomial = polynomial.bit_reverse();
let coeffs = polynomial.interpolate().into_ordered_coefficients();
Expand Down
13 changes: 2 additions & 11 deletions src/core/poly/circle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,15 +221,6 @@ pub trait PolyOps<F: ExtensionOf<BaseField>>: FieldOps<F> + Sized {
/// Used by the [`CircleEvaluation::interpolate()`] function.
fn interpolate(eval: CircleEvaluation<Self, F>) -> CirclePoly<Self, F>;

// TODO(spapini): Remove these.
fn bit_reverse_natural(
eval: CircleEvaluation<Self, F>,
) -> CircleEvaluation<Self, F, BitReversedOrder>;

fn bit_reverse_reversed(
eval: CircleEvaluation<Self, F, BitReversedOrder>,
) -> CircleEvaluation<Self, F, NaturalOrder>;

/// Evaluates the polynomial at a single point.
/// Used by the [`CirclePoly::eval_at_point()`] function.
fn eval_at_point<E: ExtensionOf<F>>(poly: &CirclePoly<Self, F>, point: CirclePoint<E>) -> E;
Expand Down Expand Up @@ -265,7 +256,7 @@ impl<F: ExtensionOf<BaseField>, B: PolyOps<F>> CircleEvaluation<B, F> {
}

pub fn bit_reverse(self) -> CircleEvaluation<B, F, BitReversedOrder> {
B::bit_reverse_natural(self)
CircleEvaluation::new(self.domain, B::bit_reverse_column(self.values))
}
}

Expand All @@ -292,7 +283,7 @@ impl<F: ExtensionOf<BaseField>> CPUCircleEvaluation<F> {

impl<B: PolyOps<F>, F: ExtensionOf<BaseField>> CircleEvaluation<B, F, BitReversedOrder> {
pub fn bit_reverse(self) -> CircleEvaluation<B, F, NaturalOrder> {
B::bit_reverse_reversed(self)
CircleEvaluation::new(self.domain, B::bit_reverse_column(self.values))
}

pub fn get_at(&self, point_index: CirclePointIndex) -> F {
Expand Down
Loading

0 comments on commit 3e0f1b0

Please sign in to comment.