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 18, 2024
1 parent 06846de commit a3eac92
Show file tree
Hide file tree
Showing 9 changed files with 2,987 additions and 131 deletions.
1,361 changes: 1,361 additions & 0 deletions a.txt

Large diffs are not rendered by default.

1,361 changes: 1,361 additions & 0 deletions b.txt

Large diffs are not rendered by default.

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
}
44 changes: 22 additions & 22 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::{TreeColumns, 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 @@ -71,35 +71,35 @@ impl CommitmentSchemeProver {
channel: &mut ProofChannel,
) -> CommitmentSchemeProof {
// Evaluate polynomials on open points.
let opened_values = self.polys().zip_cols(&open_points).map(|(poly, points)| {
let openings = self.polys().zip_cols(&open_points).map(|(poly, points)| {
points
.iter()
.map(|point| poly.eval_at_point(*point))
.map(|&point| PointOpening {
point,
value: poly.eval_at_point(point),
})
.collect_vec()
});
let opened_values = openings
.as_ref()
.map(|x| x.iter().map(|o| o.value).collect());

// Compute oods quotients for boundary constraints on open_points.
let quotients = self
.evaluations()
.zip_cols(&opened_values)
.zip_cols(&open_points)
.map(|((evaluation, values), points)| {
zip(points, values)
.map(|(&point, &value)| {
get_pair_oods_quotient(point, value, evaluation).bit_reverse()
})
.collect_vec()
});
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_all_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
142 changes: 136 additions & 6 deletions src/core/commitment_scheme/quotients.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,151 @@
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::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>],
) -> Vec<SparseCircleEvaluation<SecureField>> {
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>],
) -> SparseCircleEvaluation<SecureField> {
let commitment_domain = CanonicCoset::new(log_size).circle_domain();
let batched_openings = BatchedColumnOpenings::new(openings);
for x in queried_values_per_column {
assert_eq!(x.len(), query_domain.flatten().len());
}
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(),
);
assert!(queried_values_per_column.iter().all(|x| x.is_empty()));
res
}
Loading

0 comments on commit a3eac92

Please sign in to comment.