Skip to content

Commit

Permalink
Commitment Scheme evaluation per size
Browse files Browse the repository at this point in the history
  • Loading branch information
spapinistarkware committed Mar 21, 2024
1 parent ab770f3 commit 30519a6
Show file tree
Hide file tree
Showing 7 changed files with 300 additions and 170 deletions.
66 changes: 41 additions & 25 deletions src/core/backend/cpu/quotients.rs
Original file line number Diff line number Diff line change
@@ -1,45 +1,61 @@
use num_traits::Zero;

use super::CPUBackend;
use crate::core::air::accumulation::ColumnAccumulator;
use crate::core::backend::Col;
use crate::core::circle::CirclePoint;
use crate::core::commitment_scheme::quotients::{BatchedColumnOpenings, QuotientOps};
use crate::core::constraints::pair_vanishing;
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::SecureColumn;
use crate::core::fields::{ComplexConjugate, FieldExpOps};
use crate::core::poly::circle::CircleDomain;
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,
mut accum: ColumnAccumulator<'_, Self>,
columns: &[Col<Self, BaseField>],
columns: &[&CircleEvaluation<Self, BaseField, BitReversedOrder>],
random_coeff: SecureField,
openings: &[BatchedColumnOpenings],
) {
) -> SecureColumn<Self> {
let mut res = SecureColumn::zeros(domain.size());
for row in 0..domain.size() {
let domain_point = domain.at(bit_reverse_index(row, domain.log_size()));
let mut row_accumlator = SecureField::zero();
for opening in openings {
let mut numerator = SecureField::zero();
for (column_index, open_value) in &opening.column_indices_and_values {
let column = &columns[*column_index];
let value = column[row];
numerator = numerator * random_coeff + (value - *open_value);
}

let denominator = pair_vanishing(
opening.point,
opening.point.complex_conjugate(),
domain_point.into_ef(),
);
let row_accumlator =
accumulate_row_quotients(openings, columns, row, random_coeff, domain_point);
res.set(row, row_accumlator);
}
res
}
}

row_accumlator *= random_coeff.pow(opening.column_indices_and_values.len() as u128)
+ numerator / denominator;
}
accum.accumulate(row, row_accumlator);
pub fn accumulate_row_quotients(
openings: &[BatchedColumnOpenings],
columns: &[&CircleEvaluation<CPUBackend, BaseField, BitReversedOrder>],
row: usize,
random_coeff: SecureField,
domain_point: CirclePoint<BaseField>,
) -> SecureField {
let mut row_accumlator = SecureField::zero();
for opening in openings {
let mut numerator = SecureField::zero();
for (column_index, open_value) in &opening.column_indices_and_values {
let column = &columns[*column_index];
let value = column[row];
let linear_term = complex_conjugate_line(opening.point, *open_value, domain_point);
numerator = numerator * random_coeff + value - linear_term;
}

let denominator = pair_vanishing(
opening.point,
opening.point.complex_conjugate(),
domain_point.into_ef(),
);

row_accumlator = row_accumlator
* random_coeff.pow(opening.column_indices_and_values.len() as u128)
+ numerator / denominator;
}
row_accumlator
}
56 changes: 28 additions & 28 deletions src/core/commitment_scheme/prover.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
//! the unique decoding regime. This is enough for a STARK proof though, where we onyl want to imply
//! the existence of such polynomials, and re ok with having a small decoding list.
use std::iter::zip;
use std::ops::Deref;

use itertools::Itertools;
Expand All @@ -17,19 +16,20 @@ use super::super::circle::CirclePoint;
use super::super::fields::m31::BaseField;
use super::super::fields::qm31::SecureField;
use super::super::fri::{FriConfig, FriProof, FriProver};
use super::super::oods::get_pair_oods_quotient;
use super::super::poly::circle::CanonicCoset;
use super::super::poly::BitReversedOrder;
use super::super::proof_of_work::{ProofOfWork, ProofOfWorkProof};
use super::super::prover::{
LOG_BLOWUP_FACTOR, LOG_LAST_LAYER_DEGREE_BOUND, N_QUERIES, PROOF_OF_WORK_BITS,
};
use super::super::ColumnVec;
use super::quotients::{compute_fri_quotients, PointOpening};
use super::utils::TreeVec;
use crate::commitment_scheme::blake2_hash::{Blake2sHash, Blake2sHasher};
use crate::commitment_scheme::merkle_decommitment::MerkleDecommitment;
use crate::commitment_scheme::merkle_tree::MerkleTree;
use crate::core::channel::Channel;
use crate::core::fields::secure::SecureEvaluation;

type MerkleHasher = Blake2sHasher;
type ProofChannel = Blake2sChannel;
Expand Down Expand Up @@ -75,39 +75,39 @@ impl CommitmentSchemeProver {
channel: &mut ProofChannel,
) -> CommitmentSchemeProof {
// Evaluate polynomials on open points.
let proved_values =
self.polynomials()
.zip_cols(&prove_points)
.map_cols(|(poly, points)| {
points
.iter()
.map(|point| poly.eval_at_point(*point))
.collect_vec()
});
channel.mix_felts(&proved_values.clone().flatten_cols());

// Compute oods quotients for boundary constraints on prove_points.
let quotients = self
.evaluations()
.zip_cols(&proved_values)
let openings = self
.polynomials()
.zip_cols(&prove_points)
.map_cols(|((evaluation, values), points)| {
zip(points, values)
.map(|(&point, &value)| {
get_pair_oods_quotient(point, value, evaluation).bit_reverse()
.map_cols(|(poly, points)| {
points
.iter()
.map(|&point| PointOpening {
point,
value: poly.eval_at_point(point),
})
.collect_vec()
});
let proved_values = openings
.as_cols_ref()
.map_cols(|x| x.iter().map(|o| o.value).collect());
channel.mix_felts(&proved_values.clone().flatten_cols());

// Compute oods quotients for boundary constraints on prove_points.
let columns = self.evaluations().flatten();
let quotients =
compute_fri_quotients(&columns[..], &openings.flatten(), channel.draw_felt());

// TODO(spapini): Conversion to CircleEvaluation can be removed when FRI supports
// SecureColumn.
let quotients = quotients
.into_iter()
.map(SecureEvaluation::to_cpu)
.collect_vec();

// Run FRI commitment phase on the oods quotients.
let fri_config = FriConfig::new(LOG_LAST_LAYER_DEGREE_BOUND, LOG_BLOWUP_FACTOR, N_QUERIES);
// TODO(spapini): Remove rev() when we start accumulating by size.
// This is only done because fri demands descending sizes.
let fri_prover = FriProver::<CPUBackend, MerkleHasher>::commit(
channel,
fri_config,
&quotients.flatten_cols_rev(),
);
let fri_prover =
FriProver::<CPUBackend, MerkleHasher>::commit(channel, fri_config, &quotients);

// Proof of work.
let proof_of_work = ProofOfWork::new(PROOF_OF_WORK_BITS).prove(channel);
Expand Down
174 changes: 168 additions & 6 deletions src/core/commitment_scheme/quotients.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,183 @@
use crate::core::air::accumulation::ColumnAccumulator;
use crate::core::backend::{Backend, Col};
use std::cmp::Reverse;
use std::collections::BTreeMap;

use itertools::{izip, multiunzip, Itertools};

use crate::core::backend::cpu::quotients::accumulate_row_quotients;
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::poly::circle::CircleDomain;
use crate::core::fields::secure::{SecureColumn, SecureEvaluation};
use crate::core::fri::SparseCircleEvaluation;
use crate::core::poly::circle::{CanonicCoset, CircleDomain, CircleEvaluation};
use crate::core::poly::BitReversedOrder;
use crate::core::prover::VerificationError;
use crate::core::queries::SparseSubCircleDomain;
use crate::core::utils::bit_reverse_index;

pub trait QuotientOps: Backend {
fn accumulate_quotients(
domain: CircleDomain,
accum: ColumnAccumulator<'_, Self>,
columns: &[Col<Self, BaseField>],
columns: &[&CircleEvaluation<Self, BaseField, BitReversedOrder>],
random_coeff: SecureField,
openings: &[BatchedColumnOpenings],
);
) -> SecureColumn<Self>;
}

pub struct BatchedColumnOpenings {
pub point: CirclePoint<SecureField>,
pub column_indices_and_values: Vec<(usize, SecureField)>,
}
impl BatchedColumnOpenings {
/// Groups column opening by opening point.
/// # Arguments
/// opening: For each column, a vector of openings.
pub fn new(openings: &[&Vec<PointOpening>]) -> Vec<Self> {
openings
.iter()
.enumerate()
.flat_map(|(column_index, openings)| {
openings.iter().map(move |opening| (column_index, opening))
})
.group_by(|(_, opening)| opening.point)
.into_iter()
.map(|(point, column_openings)| BatchedColumnOpenings {
point,
column_indices_and_values: column_openings
.map(|(column_index, opening)| (column_index, opening.value))
.collect(),
})
.collect()
}
}

pub struct PointOpening {
pub point: CirclePoint<SecureField>,
pub value: SecureField,
}

pub fn compute_fri_quotients<B: QuotientOps>(
columns: &[&CircleEvaluation<B, BaseField, BitReversedOrder>],
openings: &[Vec<PointOpening>],
random_coeff: SecureField,
) -> Vec<SecureEvaluation<B>> {
izip!(columns, openings)
.group_by(|(c, _)| c.domain.log_size())
.into_iter()
.sorted_by_key(|(log_size, _)| Reverse(*log_size))
.map(|(log_size, tuples)| {
let (columns, openings): (Vec<_>, Vec<_>) = multiunzip(tuples);
let domain = CanonicCoset::new(log_size).circle_domain();
// TODO: slice.
let batched_openings = BatchedColumnOpenings::new(&openings);
let values = B::accumulate_quotients(domain, &columns, random_coeff, &batched_openings);
SecureEvaluation { domain, values }
})
.collect()
}

pub fn fri_answers(
column_log_sizes: Vec<u32>,
openings: &[Vec<PointOpening>],
random_coeff: SecureField,
query_domain_per_log_size: BTreeMap<u32, SparseSubCircleDomain>,
queried_values_per_column: &[Vec<BaseField>],
) -> Result<Vec<SparseCircleEvaluation<SecureField>>, VerificationError> {
izip!(column_log_sizes, openings, queried_values_per_column)
.group_by(|(c, ..)| *c)
.into_iter()
.sorted_by_key(|(log_size, _)| Reverse(*log_size))
.map(|(log_size, tuples)| {
let (_, openings, queried_valued_per_column): (Vec<_>, Vec<_>, Vec<_>) =
multiunzip(tuples);
fri_answers_for_log_size(
log_size,
&openings,
random_coeff,
&query_domain_per_log_size[&log_size],
&queried_valued_per_column,
)
})
.collect()
}

pub fn fri_answers_for_log_size(
log_size: u32,
openings: &[&Vec<PointOpening>],
random_coeff: SecureField,
query_domain: &SparseSubCircleDomain,
queried_values_per_column: &[&Vec<BaseField>],
) -> Result<SparseCircleEvaluation<SecureField>, VerificationError> {
let commitment_domain = CanonicCoset::new(log_size).circle_domain();
let batched_openings = BatchedColumnOpenings::new(openings);
for x in queried_values_per_column {
if x.len() != query_domain.flatten().len() {
return Err(VerificationError::InvalidStructure);
}
}
let mut queried_values_per_column = queried_values_per_column
.iter()
.map(|q| q.iter())
.collect_vec();

let res = SparseCircleEvaluation::new(
query_domain
.iter()
.map(|subdomain| {
let domain = subdomain.to_circle_domain(&commitment_domain);
let column_evals = queried_values_per_column
.iter_mut()
.map(|q| {
CircleEvaluation::new(domain, q.take(domain.size()).copied().collect_vec())
})
.collect_vec();
// TODO(spapini): bit reverse iterator.
let values = (0..domain.size())
.map(|row| {
let domain_point = domain.at(bit_reverse_index(row, log_size));
accumulate_row_quotients(
&batched_openings,
&column_evals.iter().collect_vec(),
row,
random_coeff,
domain_point,
)
})
.collect();
CircleEvaluation::new(domain, values)
})
.collect(),
);
if !queried_values_per_column.iter().all(|x| x.is_empty()) {
return Err(VerificationError::InvalidStructure);
}
Ok(res)
}

#[cfg(test)]
mod tests {
use crate::core::backend::cpu::{CPUCircleEvaluation, CPUCirclePoly};
use crate::core::circle::SECURE_FIELD_CIRCLE_GEN;
use crate::core::commitment_scheme::quotients::{compute_fri_quotients, PointOpening};
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 =
compute_fri_quotients(&[&eval], &[vec![PointOpening { point, value }]], coeff)
.pop()
.unwrap();
let quot_poly_base_field =
CPUCircleEvaluation::new(eval_domain, quot_eval.values.cols[0].clone()).interpolate();
assert!(quot_poly_base_field.is_in_fft_space(LOG_SIZE));
}
}
Loading

0 comments on commit 30519a6

Please sign in to comment.