Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Save inverse on quotient numerator #484

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/core/backend/cpu/quotients.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,9 @@ pub fn accumulate_row_quotients(
for (column_index, sampled_value) in &sample.column_indices_and_values {
let column = &columns[*column_index];
let value = column[row];
let linear_term = complex_conjugate_line(sample.point, *sampled_value, domain_point);
numerator = numerator * random_coeff + value - linear_term;
let current_numerator =
complex_conjugate_line(domain_point, value, sample.point, *sampled_value);
numerator = numerator * random_coeff + current_numerator;
}

let denominator = pair_vanishing(
Expand Down
65 changes: 33 additions & 32 deletions src/core/commitment_scheme/prover.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,21 @@ 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, PointSample};
use super::utils::TreeVec;
use crate::commitment_scheme::blake2_hash::{Blake2sHash, Blake2sHasher};
use crate::commitment_scheme::merkle_input::{MerkleTreeColumnLayout, MerkleTreeInput};
use crate::commitment_scheme::mixed_degree_decommitment::MixedDecommitment;
use crate::commitment_scheme::mixed_degree_merkle_tree::MixedDegreeMerkleTree;
use crate::core::channel::Channel;
use crate::core::poly::circle::SecureEvaluation;

type MerkleHasher = Blake2sHasher;
type ProofChannel = Blake2sChannel;
Expand Down Expand Up @@ -65,43 +66,43 @@ impl CommitmentSchemeProver {

pub fn prove_values(
&self,
prove_points: TreeVec<ColumnVec<Vec<CirclePoint<SecureField>>>>,
sampled_points: TreeVec<ColumnVec<Vec<CirclePoint<SecureField>>>>,
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)
.zip_cols(&prove_points)
.map_cols(|((evaluation, values), points)| {
zip(points, values)
.map(|(&point, &value)| {
get_pair_oods_quotient(point, value, evaluation).bit_reverse()
// Evaluate polynomials on samples points.
let samples = self
.polynomials()
.zip_cols(&sampled_points)
.map_cols(|(poly, points)| {
points
.iter()
.map(|&point| PointSample {
point,
value: poly.eval_at_point(point),
})
.collect_vec()
});
let proved_values = samples
.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 sampled_points.
let columns = self.evaluations().flatten();
let quotients =
compute_fri_quotients(&columns[..], &samples.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 All @@ -125,7 +126,7 @@ impl CommitmentSchemeProver {
let decommitments = decommitment_results.map(|(_, d)| d);

CommitmentSchemeProof {
proved_values,
sampled_values: proved_values,
decommitments,
queried_values,
proof_of_work,
Expand All @@ -136,7 +137,7 @@ impl CommitmentSchemeProver {

#[derive(Debug)]
pub struct CommitmentSchemeProof {
pub proved_values: TreeVec<ColumnVec<Vec<SecureField>>>,
pub sampled_values: TreeVec<ColumnVec<Vec<SecureField>>>,
pub decommitments: TreeVec<MixedDecommitment<BaseField, MerkleHasher>>,
pub queried_values: TreeVec<ColumnVec<Vec<BaseField>>>,
pub proof_of_work: ProofOfWorkProof,
Expand Down
165 changes: 164 additions & 1 deletion src/core/commitment_scheme/quotients.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
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::fields::secure_column::SecureColumn;
use crate::core::poly::circle::{CircleDomain, CircleEvaluation};
use crate::core::fri::SparseCircleEvaluation;
use crate::core::poly::circle::{CanonicCoset, CircleDomain, CircleEvaluation, SecureEvaluation};
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 {
/// Accumulates the quotients of the columns at the given domain.
Expand All @@ -28,3 +38,156 @@ pub struct ColumnSampleBatch {
/// The sampled column indices and their values at the point.
pub column_indices_and_values: Vec<(usize, SecureField)>,
}
impl ColumnSampleBatch {
/// Groups column opening by opening point.
/// # Arguments
/// opening: For each column, a vector of openings.
pub fn new(openings: &[&Vec<PointSample>]) -> 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)| Self {
point,
column_indices_and_values: column_openings
.map(|(column_index, opening)| (column_index, opening.value))
.collect(),
})
.collect()
}
}

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

pub fn compute_fri_quotients<B: QuotientOps>(
columns: &[&CircleEvaluation<B, BaseField, BitReversedOrder>],
samples: &[Vec<PointSample>],
random_coeff: SecureField,
) -> Vec<SecureEvaluation<B>> {
izip!(columns, samples)
.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 = ColumnSampleBatch::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<PointSample>],
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<PointSample>],
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 = ColumnSampleBatch::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, PointSample};
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![PointSample { point, value }]], coeff)
.pop()
.unwrap();
let quot_poly_base_field =
CPUCircleEvaluation::new(eval_domain, quot_eval.values.columns[0].clone())
.interpolate();
assert!(quot_poly_base_field.is_in_fft_space(LOG_SIZE));
}
}
Loading
Loading