use hashbrown::HashMap;
use itertools::Itertools;
#[cfg(feature = "parallel")]
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
use tracing::{info, span, Level};
use crate::core::channel::{Channel, MerkleChannel};
use crate::core::circle::CirclePoint;
use crate::core::fields::m31::BaseField;
use crate::core::fields::qm31::SecureField;
use crate::core::pcs::quotients::{
CommitmentSchemeProof, CommitmentSchemeProofAux, ExtendedCommitmentSchemeProof, PointSample,
};
use crate::core::pcs::utils::prepare_preprocessed_query_positions;
use crate::core::pcs::{PcsConfig, TreeSubspan, TreeVec};
use crate::core::poly::circle::CanonicCoset;
use crate::core::vcs_lifted::merkle_hasher::MerkleHasherLifted;
use crate::core::vcs_lifted::verifier::ExtendedMerkleDecommitmentLifted;
use crate::core::ColumnVec;
use crate::prover::air::component_prover::{Poly, Trace, WeightsHashMap};
use crate::prover::backend::{BackendForChannel, Col};
use crate::prover::fri::{FriDecommitResult, FriProver};
use crate::prover::pcs::quotient_ops::compute_fri_quotients;
use crate::prover::poly::circle::{CircleCoefficients, CircleEvaluation};
use crate::prover::poly::twiddles::TwiddleTree;
use crate::prover::poly::BitReversedOrder;
use crate::prover::vcs_lifted::prover::MerkleProverLifted;
pub mod quotient_ops;
#[cfg(feature = "gpu")]
#[inline]
fn use_gpu_folding_oods_eval<B>() -> bool {
core::any::type_name::<B>() == core::any::type_name::<crate::prover::backend::gpu::GpuBackend>()
}
#[cfg(not(feature = "gpu"))]
#[inline]
fn use_gpu_folding_oods_eval<B>() -> bool {
false
}
pub struct CommitmentSchemeProver<'a, B: BackendForChannel<MC>, MC: MerkleChannel> {
pub trees: TreeVec<CommitmentTreeProver<B, MC>>,
pub config: PcsConfig,
twiddles: &'a TwiddleTree<B>,
pub store_polynomials_coefficients: bool,
}
impl<'a, B: BackendForChannel<MC>, MC: MerkleChannel> CommitmentSchemeProver<'a, B, MC> {
pub fn new(config: PcsConfig, twiddles: &'a TwiddleTree<B>) -> Self {
CommitmentSchemeProver {
trees: TreeVec::default(),
config,
twiddles,
store_polynomials_coefficients: false,
}
}
pub const fn set_store_polynomials_coefficients(&mut self) {
self.store_polynomials_coefficients = true;
}
fn commit(&mut self, polynomials: ColumnVec<CircleCoefficients<B>>, channel: &mut MC::C) {
let _span = span!(Level::INFO, "Commitment").entered();
let tree = CommitmentTreeProver::new(
polynomials,
self.config.fri_config.log_blowup_factor,
channel,
self.twiddles,
self.store_polynomials_coefficients,
);
self.trees.push(tree);
}
pub fn tree_builder(&mut self) -> TreeBuilder<'_, 'a, B, MC> {
TreeBuilder {
tree_index: self.trees.len(),
commitment_scheme: self,
polys: Vec::default(),
}
}
pub fn roots(&self) -> TreeVec<<MC::H as MerkleHasherLifted>::Hash> {
self.trees.as_ref().map(|tree| tree.commitment.root())
}
pub fn polynomials(&self) -> TreeVec<ColumnVec<&Poly<B>>> {
self.trees
.as_ref()
.map(|tree| tree.polynomials.iter().collect())
}
pub fn evaluations(
&self,
) -> TreeVec<ColumnVec<&CircleEvaluation<B, BaseField, BitReversedOrder>>> {
self.trees
.as_ref()
.map(|tree| tree.polynomials.iter().map(|poly| &poly.evals).collect())
}
pub fn trace(&self) -> Trace<'_, B> {
let polys = self.polynomials();
Trace { polys }
}
pub fn build_weights_hash_map(
&self,
sampled_points: &TreeVec<ColumnVec<Vec<CirclePoint<SecureField>>>>,
max_log_size: u32,
) -> WeightsHashMap<B>
where
Col<B, SecureField>: Send + Sync,
{
let weights_dashmap = WeightsHashMap::<B>::new();
self.polynomials()
.zip_cols(sampled_points)
.map_cols(|(poly, points)| {
let compute_weights = |(log_size, point): (u32, CirclePoint<SecureField>)| {
weights_dashmap.entry((log_size, point)).or_insert_with(|| {
CircleEvaluation::<B, BaseField, BitReversedOrder>::barycentric_weights(
CanonicCoset::new(log_size),
point,
)
});
};
let log_size = poly.evals.domain.log_size();
#[cfg(not(feature = "parallel"))]
points.iter().for_each(|&point| {
compute_weights((log_size, point.repeated_double(max_log_size - log_size)))
});
#[cfg(feature = "parallel")]
points.par_iter().for_each(|&point| {
compute_weights((log_size, point.repeated_double(max_log_size - log_size)))
});
});
weights_dashmap
}
pub fn prove_values(
self,
sampled_points: TreeVec<ColumnVec<Vec<CirclePoint<SecureField>>>>,
channel: &mut MC::C,
) -> ExtendedCommitmentSchemeProof<MC::H> {
let span = span!(
Level::INFO,
"Evaluate columns out of domain",
class = "EvaluateOutOfDomain"
)
.entered();
let lifting_log_size = self.trees.last().unwrap().commitment.layers.len() as u32 - 1;
let weights_hash_map = if self.store_polynomials_coefficients {
None
} else {
Some(self.build_weights_hash_map(&sampled_points, lifting_log_size))
};
let use_folding_oods_eval =
self.store_polynomials_coefficients && use_gpu_folding_oods_eval::<B>();
let eval_at_points = |(poly, points): (&Poly<B>, &Vec<CirclePoint<SecureField>>)| {
points
.iter()
.map(|&point| PointSample {
point,
value: {
let folded_point =
point.repeated_double(lifting_log_size - poly.evals.domain.log_size());
if use_folding_oods_eval {
poly.evals
.eval_at_point_by_folding(folded_point, self.twiddles)
} else {
poly.eval_at_point(folded_point, weights_hash_map.as_ref())
}
},
})
.collect_vec()
};
#[cfg(not(feature = "parallel"))]
let samples: TreeVec<Vec<Vec<PointSample>>> = self
.polynomials()
.zip_cols(&sampled_points)
.map_cols(eval_at_points);
#[cfg(feature = "parallel")]
let samples: TreeVec<Vec<Vec<PointSample>>> = self
.polynomials()
.zip_cols(&sampled_points)
.par_map_cols(eval_at_points);
span.exit();
let sampled_values = samples
.as_cols_ref()
.map_cols(|x| x.iter().map(|o| o.value).collect());
channel.mix_felts(&sampled_values.clone().flatten_cols());
let columns = self.evaluations();
print_column_size_histogram::<B, MC>(&columns);
let quotients = compute_fri_quotients(
&columns,
&samples,
channel.draw_secure_felt(),
lifting_log_size,
self.config.fri_config.log_blowup_factor,
);
let fri_prover =
FriProver::<B, MC>::commit(channel, self.config.fri_config, "ients, self.twiddles);
let span1 = span!(Level::INFO, "Grind", class = "Queries POW").entered();
let proof_of_work = B::grind(channel, self.config.pow_bits);
span1.exit();
channel.mix_u64(proof_of_work);
let FriDecommitResult {
fri_proof,
query_positions,
unsorted_query_locations,
} = fri_prover.decommit(channel);
let preprocessed_query_positions = prepare_preprocessed_query_positions(
&query_positions,
lifting_log_size,
self.trees[0].commitment.layers.len() as u32 - 1,
);
let query_positions_tree = TreeVec::new(
self.trees
.iter()
.enumerate()
.map(|(i, _)| {
if i == 0 {
preprocessed_query_positions.as_slice()
} else {
query_positions.as_slice()
}
})
.collect::<Vec<_>>(),
);
let (queried_values, decommitments, aux): (Vec<_>, Vec<_>, Vec<_>) = self
.trees
.as_ref()
.zip_eq(query_positions_tree)
.map(|(tree, query_positions)| tree.decommit(query_positions))
.0
.into_iter()
.map(|(v, x)| (v, x.decommitment, x.aux))
.multiunzip();
ExtendedCommitmentSchemeProof {
proof: CommitmentSchemeProof {
commitments: self.roots(),
sampled_values,
decommitments: TreeVec(decommitments),
queried_values: TreeVec(queried_values),
proof_of_work,
fri_proof: fri_proof.proof,
config: self.config,
},
aux: CommitmentSchemeProofAux {
unsorted_query_locations,
trace_decommitment: TreeVec(aux),
fri: fri_proof.aux,
},
}
}
}
pub struct TreeBuilder<'a, 'b, B: BackendForChannel<MC>, MC: MerkleChannel> {
tree_index: usize,
commitment_scheme: &'a mut CommitmentSchemeProver<'b, B, MC>,
polys: ColumnVec<CircleCoefficients<B>>,
}
impl<B: BackendForChannel<MC>, MC: MerkleChannel> TreeBuilder<'_, '_, B, MC> {
pub fn extend_evals(
&mut self,
columns: Vec<CircleEvaluation<B, BaseField, BitReversedOrder>>,
) -> TreeSubspan {
let span = span!(Level::INFO, "Interpolation for commitment").entered();
let polys = B::interpolate_columns(columns, self.commitment_scheme.twiddles);
span.exit();
self.extend_polys(polys)
}
pub fn extend_polys(
&mut self,
columns: impl IntoIterator<Item = CircleCoefficients<B>>,
) -> TreeSubspan {
let col_start = self.polys.len();
self.polys.extend(columns);
let col_end = self.polys.len();
TreeSubspan {
tree_index: self.tree_index,
col_start,
col_end,
}
}
pub fn commit(self, channel: &mut MC::C) {
let _span = span!(Level::INFO, "Commitment").entered();
self.commitment_scheme.commit(self.polys, channel);
}
}
pub struct CommitmentTreeProver<B: BackendForChannel<MC>, MC: MerkleChannel> {
pub polynomials: ColumnVec<Poly<B>>,
pub commitment: MerkleProverLifted<B, MC::H>,
}
impl<B: BackendForChannel<MC>, MC: MerkleChannel> CommitmentTreeProver<B, MC> {
pub fn new(
polynomials: ColumnVec<CircleCoefficients<B>>,
log_blowup_factor: u32,
channel: &mut MC::C,
twiddles: &TwiddleTree<B>,
store_polynomials_coefficients: bool,
) -> Self {
let span = span!(Level::INFO, "Extension").entered();
let polynomials = B::evaluate_polynomials(
polynomials,
log_blowup_factor,
twiddles,
store_polynomials_coefficients,
);
span.exit();
let _span = span!(Level::INFO, "Merkle").entered();
let tree = MerkleProverLifted::commit(
polynomials
.iter()
.map(|poly: &Poly<B>| &poly.evals.values)
.collect(),
);
MC::mix_root(channel, tree.root());
CommitmentTreeProver {
polynomials,
commitment: tree,
}
}
fn decommit(
&self,
queries: &[usize],
) -> (
ColumnVec<Vec<BaseField>>,
ExtendedMerkleDecommitmentLifted<MC::H>,
) {
let eval_vec = self
.polynomials
.iter()
.map(|poly| &poly.evals.values)
.collect_vec();
self.commitment.decommit(queries, eval_vec)
}
}
fn print_column_size_histogram<B: BackendForChannel<MC>, MC: MerkleChannel>(
columns_per_tree: &TreeVec<ColumnVec<&CircleEvaluation<B, BaseField, BitReversedOrder>>>,
) {
let mut log_size_histogram = HashMap::new();
for columns in columns_per_tree.iter() {
for column in columns {
*log_size_histogram
.entry(column.domain.log_size())
.or_insert(0) += 1;
}
}
for (log_size, count) in log_size_histogram {
info!("Log size {log_size}: {count}");
}
}