From c74280ab9113a99e1094267c6e7e7b681a47284c Mon Sep 17 00:00:00 2001 From: Osama Alkhodairy Date: Fri, 20 Dec 2024 03:08:31 +0200 Subject: [PATCH 01/12] initial version working when log_final_poly_len=1 --- fri/Cargo.toml | 1 + fri/src/config.rs | 9 ++++ fri/src/proof.rs | 8 +-- fri/src/prover.rs | 92 ++++++++++++++++++++++++------- fri/src/two_adic_pcs.rs | 15 ++++-- fri/src/verifier.rs | 117 ++++++++++++++++++++++++++++++++-------- fri/tests/fri.rs | 11 +++- fri/tests/pcs.rs | 4 ++ merkle-tree/src/mmcs.rs | 4 ++ 9 files changed, 212 insertions(+), 49 deletions(-) diff --git a/fri/Cargo.toml b/fri/Cargo.toml index 6c633adb7..c62bae7bf 100644 --- a/fri/Cargo.toml +++ b/fri/Cargo.toml @@ -17,6 +17,7 @@ itertools.workspace = true rand.workspace = true tracing.workspace = true serde = { workspace = true, features = ["derive", "alloc"] } +tracing-subscriber.workspace = true [dev-dependencies] p3-baby-bear.workspace = true diff --git a/fri/src/config.rs b/fri/src/config.rs index 2524ea8a2..664a021e5 100644 --- a/fri/src/config.rs +++ b/fri/src/config.rs @@ -8,9 +8,11 @@ use p3_matrix::Matrix; pub struct FriConfig { pub log_blowup: usize, // TODO: This parameter and FRI early stopping are not yet implemented in `CirclePcs`. + /// log of the number of coefficients in the final polynomial pub log_final_poly_len: usize, pub num_queries: usize, pub proof_of_work_bits: usize, + pub arity_bits: usize, pub mmcs: M, } @@ -23,6 +25,10 @@ impl FriConfig { 1 << self.log_final_poly_len } + pub const fn arity(&self) -> usize { + 1 << self.arity_bits + } + /// Returns the soundness bits of this FRI instance based on the /// [ethSTARK](https://eprint.iacr.org/2021/582) conjecture. /// @@ -66,6 +72,7 @@ pub fn create_test_fri_config(mmcs: Mmcs) -> FriConfig { log_final_poly_len: 0, num_queries: 2, proof_of_work_bits: 1, + arity_bits: 1, mmcs, } } @@ -78,6 +85,8 @@ pub fn create_benchmark_fri_config(mmcs: Mmcs) -> FriConfig { log_final_poly_len: 0, num_queries: 100, proof_of_work_bits: 16, + // TODO[osama]: consider increasing this + arity_bits: 1, mmcs, } } diff --git a/fri/src/proof.rs b/fri/src/proof.rs index 584faee2a..c75013625 100644 --- a/fri/src/proof.rs +++ b/fri/src/proof.rs @@ -31,10 +31,10 @@ pub struct QueryProof, InputProof> { #[derive(Debug, Serialize, Deserialize, Clone)] #[serde(bound = "")] pub struct CommitPhaseProofStep> { - /// The opening of the commit phase codeword at the sibling location. - // This may change to Vec if the library is generalized to support other FRI - // folding arities besides 2, meaning that there can be multiple siblings. - pub sibling_value: F, + /// The opened rows of the commit phase. The first element is the evaluation of the folded + /// polynomials so far, and the other elements are evaluations of polynomials of smaller size + /// that enter before the next commitment round. See prover::commit_phase for more details + pub opened_rows: Vec>, pub opening_proof: M::Proof, } diff --git a/fri/src/prover.rs b/fri/src/prover.rs index 8ed92d967..55af318d7 100644 --- a/fri/src/prover.rs +++ b/fri/src/prover.rs @@ -7,9 +7,12 @@ use p3_challenger::{CanObserve, FieldChallenger, GrindingChallenger}; use p3_commit::Mmcs; use p3_dft::{Radix2Dit, TwoAdicSubgroupDft}; use p3_field::{ExtensionField, Field, TwoAdicField}; -use p3_matrix::dense::RowMajorMatrix; +use p3_matrix::{ + dense::{DenseMatrix, RowMajorMatrix}, + Matrix, +}; use p3_util::{log2_strict_usize, reverse_slice_index_bits}; -use tracing::{debug_span, info_span, instrument}; +use tracing::{debug, debug_span, info_span, instrument}; use crate::{CommitPhaseProofStep, FriConfig, FriGenericConfig, FriProof, QueryProof}; @@ -95,20 +98,66 @@ where let mut data = vec![]; while folded.len() > config.blowup() * config.final_poly_len() { - let leaves = RowMajorMatrix::new(folded, 2); - let (commit, prover_data) = config.mmcs.commit_matrix(leaves); + let arity = config.arity(); + debug!("arity: {}", arity); + + debug!("proving commit layer: folded.len(): {}, config.blowup(): {}, config.final_poly_len(): {}", folded.len(), config.blowup(), config.final_poly_len()); + + let next_folded_len = folded.len() / arity; + + // First, we collect the polynomial evaluations that will be committed this round. + // Those are `folded` and polynomials in `inputs` not consumed yet with number of + // evaluations more than `next_folded_len` + let mut polys_before_next_round = vec![]; + + let mut cur_folded_len = folded.len(); + while cur_folded_len > next_folded_len { + if let Some(poly_eval) = inputs_iter.next_if(|v| v.len() == cur_folded_len) { + let width = poly_eval.len() / next_folded_len; + let poly_eval_matrix = RowMajorMatrix::new(poly_eval, width); + polys_before_next_round.push(poly_eval_matrix); + } + + cur_folded_len /= 2; + } + + let folded_matrix = RowMajorMatrix::new(folded.clone(), arity); + let matrices_to_commit: Vec> = iter::once(folded_matrix) + .chain(polys_before_next_round) + .collect(); + + debug!("committing matrices with the following dimensions:"); + for matrix in &matrices_to_commit { + debug!("width: {}, height: {}", matrix.width, matrix.height()); + } + + let (commit, prover_data) = config.mmcs.commit(matrices_to_commit); challenger.observe(commit.clone()); + // Next, we fold `folded` and `polys_before_next_round` to prepare for the next round let beta: Challenge = challenger.sample_ext_element(); - // We passed ownership of `current` to the MMCS, so get a reference to it - let leaves = config.mmcs.get_matrices(&prover_data).pop().unwrap(); - folded = g.fold_matrix(beta, leaves.as_view()); + + // Get a reference to the committed matrices + let leaves = config.mmcs.get_matrices(&prover_data); + let mut leaves_iter = leaves.into_iter().peekable(); + // Skip `folded` + leaves_iter.next(); + + while folded.len() > next_folded_len { + let matrix_to_fold = RowMajorMatrix::new(folded, 2); + folded = g.fold_matrix(beta, matrix_to_fold); + + if let Some(poly_eval) = leaves_iter.next_if(|v| v.values.len() == folded.len()) { + izip!(&mut folded, &poly_eval.values).for_each(|(f, v)| *f += *v); + } + } commits.push(commit); data.push(prover_data); - if let Some(v) = inputs_iter.next_if(|v| v.len() == folded.len()) { - izip!(&mut folded, v).for_each(|(c, x)| *c += x); + // We directly add the next polynomial's evaluations into `folded` in case their lengths match + if let Some(poly_eval) = inputs_iter.next_if(|v| v.len() == folded.len()) { + izip!(&mut folded, poly_eval).for_each(|(c, x)| *c += x); } } @@ -129,7 +178,7 @@ where debug_assert!( final_poly .iter() - .skip(1 << config.log_final_poly_len) + .skip(config.final_poly_len()) .all(|x| x.is_zero()), "All coefficients beyond final_poly_len must be zero" ); @@ -155,22 +204,27 @@ where F: Field, M: Mmcs, { + debug!("answer query with index: {}", index); commit_phase_commits .iter() .enumerate() .map(|(i, commit)| { - let index_i = index >> i; - let index_i_sibling = index_i ^ 1; - let index_pair = index_i >> 1; + let index_row = index >> ((i + 1) * config.arity_bits); + + debug!( + "query commit iteration: commit_i: {}, index_row: {}", + i, index_row + ); - let (mut opened_rows, opening_proof) = config.mmcs.open_batch(index_pair, commit); - assert_eq!(opened_rows.len(), 1); - let opened_row = opened_rows.pop().unwrap(); - assert_eq!(opened_row.len(), 2, "Committed data should be in pairs"); - let sibling_value = opened_row[index_i_sibling % 2]; + let (opened_rows, opening_proof) = config.mmcs.open_batch(index_row, commit); + assert_eq!( + opened_rows[0].len(), + config.arity(), + "The folded array should be of size arity" + ); CommitPhaseProofStep { - sibling_value, + opened_rows, opening_proof, } }) diff --git a/fri/src/two_adic_pcs.rs b/fri/src/two_adic_pcs.rs index 993183514..378731eb2 100644 --- a/fri/src/two_adic_pcs.rs +++ b/fri/src/two_adic_pcs.rs @@ -20,7 +20,7 @@ use p3_maybe_rayon::prelude::*; use p3_util::linear_map::LinearMap; use p3_util::{log2_strict_usize, reverse_bits_len, reverse_slice_index_bits, VecExt}; use serde::{Deserialize, Serialize}; -use tracing::{info_span, instrument}; +use tracing::{debug, info_span, instrument}; use crate::verifier::{self, FriError}; use crate::{prover, FriConfig, FriGenericConfig, FriProof}; @@ -79,7 +79,7 @@ impl FriGenericConfig let log_arity = 1; let (e0, e1) = evals .collect_tuple() - .expect("TwoAdicFriFolder only supports arity=2"); + .expect("TwoAdicFriGenericConfig only supports folding rows of size 2"); // If performance critical, make this API stateful to avoid this // This is a bit more math than is necessary, but leaving it here // in case we want higher arity in the future @@ -96,6 +96,13 @@ impl FriGenericConfig } fn fold_matrix>(&self, beta: F, m: M) -> Vec { + // TODO[osama]: remove extra info + debug!( + "folding matrix with dimensions: width: {}, height: {}", + m.width(), + m.height() + ); + // We use the fact that // p_e(x^2) = (p(x) + p(-x)) / 2 // p_o(x^2) = (p(x) - p(-x)) / (2 x) @@ -122,7 +129,9 @@ impl FriGenericConfig m.par_rows() .zip(powers) .map(|(mut row, power)| { - let (lo, hi) = row.next_tuple().unwrap(); + let (lo, hi) = row + .next_tuple() + .expect("TwoAdicFriGenericConfig only supports folding rows of size 2"); (one_half + power) * lo + (one_half - power) * hi }) .collect() diff --git a/fri/src/verifier.rs b/fri/src/verifier.rs index d721ce4dc..877cd1dd0 100644 --- a/fri/src/verifier.rs +++ b/fri/src/verifier.rs @@ -1,12 +1,12 @@ -use alloc::vec; use alloc::vec::Vec; use itertools::{izip, Itertools}; use p3_challenger::{CanObserve, FieldChallenger, GrindingChallenger}; use p3_commit::Mmcs; use p3_field::{ExtensionField, Field, TwoAdicField}; -use p3_matrix::Dimensions; +use p3_matrix::{dense::RowMajorMatrix, Dimensions, Matrix}; use p3_util::reverse_bits_len; +use tracing::debug; use crate::{CommitPhaseProofStep, FriConfig, FriGenericConfig, FriProof}; @@ -57,8 +57,12 @@ where return Err(FriError::InvalidPowWitness); } - let log_max_height = - proof.commit_phase_commits.len() + config.log_blowup + config.log_final_poly_len; + // TODO[osama]: make sure this is correct + let log_max_height = proof.commit_phase_commits.len() * config.arity_bits + + config.log_blowup + + config.log_final_poly_len; + + debug!("in verifier::verify, log_max_height: {:?}", log_max_height); for qp in &proof.query_proofs { let index = challenger.sample_bits(log_max_height + g.extra_query_index_bits()); @@ -82,7 +86,7 @@ where log_max_height, )?; - let final_poly_index = index >> (proof.commit_phase_commits.len()); + let final_poly_index = index >> (proof.commit_phase_commits.len() * config.arity_bits); let mut eval = Challenge::ZERO; @@ -117,7 +121,7 @@ fn verify_query<'a, G, F, M>( g: &G, config: &FriConfig, mut index: usize, - steps: impl Iterator>, + mut steps: impl Iterator>, reduced_openings: Vec<(usize, F)>, log_max_height: usize, ) -> Result> @@ -126,38 +130,109 @@ where M: Mmcs + 'a, G: FriGenericConfig, { + debug!( + "verifying query proof, index: {:?}, reduced_openings: {:#?}", + index, reduced_openings + ); + let mut folded_eval = F::ZERO; let mut ro_iter = reduced_openings.into_iter().peekable(); - for (log_folded_height, (&beta, comm, opening)) in izip!((0..log_max_height).rev(), steps) { - if let Some((_, ro)) = ro_iter.next_if(|(lh, _)| *lh == log_folded_height + 1) { + let mut log_folded_height = log_max_height; + + while log_folded_height > config.log_blowup + config.log_final_poly_len { + debug!( + "query iteration, log_folded_height: {:?}", + log_folded_height + ); + + if let Some((lh, ro)) = ro_iter.next_if(|(lh, _)| *lh == log_folded_height) { + debug!("adding directly from reduced openings at lh: {:?}", lh); folded_eval += ro; } - let index_sibling = index ^ 1; - let index_pair = index >> 1; + let (&beta, comm, opening) = steps.next().unwrap(); + + let index_row = index >> config.arity_bits; + + // Verify that `folded_eval` and evals from reduced_openings match opened_rows + assert_eq!(folded_eval, opening.opened_rows[0][index % config.arity()]); + for row in opening.opened_rows.iter().skip(1) { + // TODO[osama]: consider adding an assert for the length of row to make sure the right thing is being read + let (lh, ro) = ro_iter.next().unwrap(); + debug!("lh: {:?}", lh); + debug!("row.len(): {:?}", row.len()); + let current_index = index >> (log_folded_height - lh); + assert_eq!( + ro, + row[current_index % row.len()], + "reduced opening mismatch at level {:?}", + lh + ); + } + + let dims: Vec<_> = opening + .opened_rows + .iter() + .map(|opened_row| Dimensions { + width: opened_row.len(), + height: 1 << (log_folded_height - config.arity_bits), // TODO[osama]: revisit + }) + .collect(); - let mut evals = vec![folded_eval; 2]; - evals[index_sibling % 2] = opening.sibling_value; + debug!("dims: {:#?}", dims); - let dims = &[Dimensions { - width: 2, - height: 1 << log_folded_height, - }]; config .mmcs .verify_batch( comm, - dims, - index_pair, - &[evals.clone()], + &dims, + index_row, + &opening.opened_rows, &opening.opening_proof, ) .map_err(FriError::CommitPhaseMmcsError)?; - index = index_pair; + // Do the folding logic + + let mut opened_rows_iter = opening.opened_rows.iter().peekable(); + let mut folded_row = opened_rows_iter.next().unwrap().clone(); + let mut cnt = 1; + while folded_row.len() > 1 { + index >>= 1; + log_folded_height -= 1; + + debug!("folding iteration: folded.len(): {:?}", folded_row.len()); + + debug!("cnt: {:?}", cnt); + // TODO[osama]: factor this out + let folded_matrix = RowMajorMatrix::new(folded_row, 2); + folded_row = folded_matrix + .par_rows() + .enumerate() + .map(|(i, row)| { + debug!( + "folding row at index: {:?}", + (index_row << (config.arity_bits - cnt)) + i + ); + g.fold_row( + (index_row << (config.arity_bits - cnt)) + i, + log_folded_height, + beta, + row.into_iter(), + ) + }) + .collect(); + + cnt += 1; + + if let Some(poly_eval) = opened_rows_iter.next_if(|v| v.len() == folded_row.len()) { + izip!(&mut folded_row, poly_eval).for_each(|(f, v)| *f += *v); + } + } - folded_eval = g.fold_row(index, log_folded_height, beta, evals.into_iter()); + folded_eval = folded_row.remove(0); + assert!(folded_row.is_empty()); } debug_assert!( diff --git a/fri/tests/fri.rs b/fri/tests/fri.rs index 04a860099..febedf46a 100644 --- a/fri/tests/fri.rs +++ b/fri/tests/fri.rs @@ -39,6 +39,8 @@ fn get_ldt_for_testing(rng: &mut R, log_final_poly_len: usize) -> (Perm, log_final_poly_len, num_queries: 10, proof_of_work_bits: 8, + // TODO[osama]: have tests with higher fri arity + arity_bits: 2, mmcs, }; (perm, fri_config) @@ -129,10 +131,15 @@ fn do_test_fri_ldt(rng: &mut R, log_final_poly_len: usize) { #[test] fn test_fri_ldt() { + // TODO[osama]: delete this and delete form cargo.toml + let _ = tracing_subscriber::fmt() + .with_max_level(tracing::Level::DEBUG) + .try_init(); + // FRI is kind of flaky depending on indexing luck - for i in 0..4 { + for i in 0..2 { let mut rng = ChaCha20Rng::seed_from_u64(i as u64); - do_test_fri_ldt(&mut rng, i + 1); + do_test_fri_ldt(&mut rng, i + 1); // TODO[osama]: can log_final_poly_len be 0? } } diff --git a/fri/tests/pcs.rs b/fri/tests/pcs.rs index 49d42ed7d..2886fab50 100644 --- a/fri/tests/pcs.rs +++ b/fri/tests/pcs.rs @@ -181,6 +181,8 @@ mod babybear_fri_pcs { log_final_poly_len: 0, num_queries: 10, proof_of_work_bits: 8, + // TODO[osama]: have tests with higher fri arity + arity_bits: 1, mmcs: challenge_mmcs, }; @@ -234,6 +236,8 @@ mod m31_fri_pcs { log_final_poly_len: 0, num_queries: 10, proof_of_work_bits: 8, + // TODO[osama]: add tests with higher fri arity + arity_bits: 1, mmcs: challenge_mmcs, }; let pcs = Pcs { diff --git a/merkle-tree/src/mmcs.rs b/merkle-tree/src/mmcs.rs index e723fa9bf..8a8a20fc9 100644 --- a/merkle-tree/src/mmcs.rs +++ b/merkle-tree/src/mmcs.rs @@ -1,6 +1,7 @@ use alloc::vec::Vec; use core::cmp::Reverse; use core::marker::PhantomData; +use tracing::debug; use itertools::Itertools; use p3_commit::Mmcs; @@ -81,6 +82,9 @@ where index: usize, prover_data: &MerkleTree, ) -> (Vec>, Vec<[PW::Value; DIGEST_ELEMS]>) { + // TODO[osama]: delete this + debug!("opening batch with index: {}", index); + let max_height = self.get_max_height(prover_data); let log_max_height = log2_ceil_usize(max_height); From 740e643fb2d5b591cacd40b19e8c8e2f5b29f112 Mon Sep 17 00:00:00 2001 From: Osama Alkhodairy Date: Fri, 20 Dec 2024 18:24:56 +0200 Subject: [PATCH 02/12] fix: fixed log_final_poly_len > 1 --- fri/src/config.rs | 1 - fri/src/proof.rs | 1 + fri/src/prover.rs | 1 + fri/src/two_adic_pcs.rs | 9 +-------- fri/src/verifier.rs | 27 +++++---------------------- fri/tests/fri.rs | 28 ++++++++++++++-------------- fri/tests/pcs.rs | 18 ++++++++++++------ merkle-tree/src/mmcs.rs | 3 --- uni-stark/tests/mul_air.rs | 2 ++ 9 files changed, 36 insertions(+), 54 deletions(-) diff --git a/fri/src/config.rs b/fri/src/config.rs index 664a021e5..1234d98bf 100644 --- a/fri/src/config.rs +++ b/fri/src/config.rs @@ -85,7 +85,6 @@ pub fn create_benchmark_fri_config(mmcs: Mmcs) -> FriConfig { log_final_poly_len: 0, num_queries: 100, proof_of_work_bits: 16, - // TODO[osama]: consider increasing this arity_bits: 1, mmcs, } diff --git a/fri/src/proof.rs b/fri/src/proof.rs index c75013625..0a060ae76 100644 --- a/fri/src/proof.rs +++ b/fri/src/proof.rs @@ -13,6 +13,7 @@ pub struct FriProof, Witness, InputProof> { pub commit_phase_commits: Vec, pub query_proofs: Vec>, pub final_poly: Vec, + pub log_max_height: usize, pub pow_witness: Witness, } diff --git a/fri/src/prover.rs b/fri/src/prover.rs index 55af318d7..5d23a3c41 100644 --- a/fri/src/prover.rs +++ b/fri/src/prover.rs @@ -68,6 +68,7 @@ where commit_phase_commits: commit_phase_result.commits, query_proofs, final_poly: commit_phase_result.final_poly, + log_max_height, pow_witness, } } diff --git a/fri/src/two_adic_pcs.rs b/fri/src/two_adic_pcs.rs index 378731eb2..dc592b097 100644 --- a/fri/src/two_adic_pcs.rs +++ b/fri/src/two_adic_pcs.rs @@ -20,7 +20,7 @@ use p3_maybe_rayon::prelude::*; use p3_util::linear_map::LinearMap; use p3_util::{log2_strict_usize, reverse_bits_len, reverse_slice_index_bits, VecExt}; use serde::{Deserialize, Serialize}; -use tracing::{debug, info_span, instrument}; +use tracing::{info_span, instrument}; use crate::verifier::{self, FriError}; use crate::{prover, FriConfig, FriGenericConfig, FriProof}; @@ -96,13 +96,6 @@ impl FriGenericConfig } fn fold_matrix>(&self, beta: F, m: M) -> Vec { - // TODO[osama]: remove extra info - debug!( - "folding matrix with dimensions: width: {}, height: {}", - m.width(), - m.height() - ); - // We use the fact that // p_e(x^2) = (p(x) + p(-x)) / 2 // p_o(x^2) = (p(x) - p(-x)) / (2 x) diff --git a/fri/src/verifier.rs b/fri/src/verifier.rs index 877cd1dd0..f4baf236e 100644 --- a/fri/src/verifier.rs +++ b/fri/src/verifier.rs @@ -57,12 +57,7 @@ where return Err(FriError::InvalidPowWitness); } - // TODO[osama]: make sure this is correct - let log_max_height = proof.commit_phase_commits.len() * config.arity_bits - + config.log_blowup - + config.log_final_poly_len; - - debug!("in verifier::verify, log_max_height: {:?}", log_max_height); + let log_max_height = proof.log_max_height; for qp in &proof.query_proofs { let index = challenger.sample_bits(log_max_height + g.extra_query_index_bits()); @@ -160,8 +155,6 @@ where for row in opening.opened_rows.iter().skip(1) { // TODO[osama]: consider adding an assert for the length of row to make sure the right thing is being read let (lh, ro) = ro_iter.next().unwrap(); - debug!("lh: {:?}", lh); - debug!("row.len(): {:?}", row.len()); let current_index = index >> (log_folded_height - lh); assert_eq!( ro, @@ -176,12 +169,10 @@ where .iter() .map(|opened_row| Dimensions { width: opened_row.len(), - height: 1 << (log_folded_height - config.arity_bits), // TODO[osama]: revisit + height: 1 << (log_folded_height - config.arity_bits), }) .collect(); - debug!("dims: {:#?}", dims); - config .mmcs .verify_batch( @@ -197,26 +188,20 @@ where let mut opened_rows_iter = opening.opened_rows.iter().peekable(); let mut folded_row = opened_rows_iter.next().unwrap().clone(); - let mut cnt = 1; + let mut index_folded_row = index_row << config.arity_bits; while folded_row.len() > 1 { index >>= 1; log_folded_height -= 1; + index_folded_row >>= 1; - debug!("folding iteration: folded.len(): {:?}", folded_row.len()); - - debug!("cnt: {:?}", cnt); // TODO[osama]: factor this out let folded_matrix = RowMajorMatrix::new(folded_row, 2); folded_row = folded_matrix .par_rows() .enumerate() .map(|(i, row)| { - debug!( - "folding row at index: {:?}", - (index_row << (config.arity_bits - cnt)) + i - ); g.fold_row( - (index_row << (config.arity_bits - cnt)) + i, + index_folded_row + i, log_folded_height, beta, row.into_iter(), @@ -224,8 +209,6 @@ where }) .collect(); - cnt += 1; - if let Some(poly_eval) = opened_rows_iter.next_if(|v| v.len() == folded_row.len()) { izip!(&mut folded_row, poly_eval).for_each(|(f, v)| *f += *v); } diff --git a/fri/tests/fri.rs b/fri/tests/fri.rs index febedf46a..db6de6841 100644 --- a/fri/tests/fri.rs +++ b/fri/tests/fri.rs @@ -29,7 +29,11 @@ type ChallengeMmcs = ExtensionMmcs; type Challenger = DuplexChallenger; type MyFriConfig = FriConfig; -fn get_ldt_for_testing(rng: &mut R, log_final_poly_len: usize) -> (Perm, MyFriConfig) { +fn get_ldt_for_testing( + rng: &mut R, + arity_bits: usize, + log_final_poly_len: usize, +) -> (Perm, MyFriConfig) { let perm = Perm::new_from_rng_128(rng); let hash = MyHash::new(perm.clone()); let compress = MyCompress::new(perm.clone()); @@ -39,15 +43,14 @@ fn get_ldt_for_testing(rng: &mut R, log_final_poly_len: usize) -> (Perm, log_final_poly_len, num_queries: 10, proof_of_work_bits: 8, - // TODO[osama]: have tests with higher fri arity - arity_bits: 2, + arity_bits, mmcs, }; (perm, fri_config) } -fn do_test_fri_ldt(rng: &mut R, log_final_poly_len: usize) { - let (perm, fc) = get_ldt_for_testing(rng, log_final_poly_len); +fn do_test_fri_ldt(rng: &mut R, arity_bits: usize, log_final_poly_len: usize) { + let (perm, fc) = get_ldt_for_testing(rng, arity_bits, log_final_poly_len); let dft = Radix2Dit::default(); let shift = Val::GENERATOR; @@ -131,15 +134,12 @@ fn do_test_fri_ldt(rng: &mut R, log_final_poly_len: usize) { #[test] fn test_fri_ldt() { - // TODO[osama]: delete this and delete form cargo.toml - let _ = tracing_subscriber::fmt() - .with_max_level(tracing::Level::DEBUG) - .try_init(); - // FRI is kind of flaky depending on indexing luck - for i in 0..2 { - let mut rng = ChaCha20Rng::seed_from_u64(i as u64); - do_test_fri_ldt(&mut rng, i + 1); // TODO[osama]: can log_final_poly_len be 0? + for arity_bits in 1..3 { + for log_final_poly_len in 0..5 { + let mut rng = ChaCha20Rng::seed_from_u64(log_final_poly_len as u64); + do_test_fri_ldt(&mut rng, arity_bits, log_final_poly_len); + } } } @@ -150,6 +150,6 @@ fn test_fri_ldt_should_panic() { // FRI is kind of flaky depending on indexing luck for i in 0..4 { let mut rng = ChaCha20Rng::seed_from_u64(i); - do_test_fri_ldt(&mut rng, 5); + do_test_fri_ldt(&mut rng, 1, 5); } } diff --git a/fri/tests/pcs.rs b/fri/tests/pcs.rs index 2886fab50..1e5f49634 100644 --- a/fri/tests/pcs.rs +++ b/fri/tests/pcs.rs @@ -181,7 +181,6 @@ mod babybear_fri_pcs { log_final_poly_len: 0, num_queries: 10, proof_of_work_bits: 8, - // TODO[osama]: have tests with higher fri arity arity_bits: 1, mmcs: challenge_mmcs, }; @@ -225,7 +224,7 @@ mod m31_fri_pcs { type Pcs = CirclePcs; - fn get_pcs(log_blowup: usize) -> (Pcs, Challenger) { + fn get_pcs(arity_bits: usize, log_blowup: usize) -> (Pcs, Challenger) { let byte_hash = ByteHash {}; let field_hash = FieldHash::new(byte_hash); let compress = MyCompress::new(byte_hash); @@ -236,8 +235,7 @@ mod m31_fri_pcs { log_final_poly_len: 0, num_queries: 10, proof_of_work_bits: 8, - // TODO[osama]: add tests with higher fri arity - arity_bits: 1, + arity_bits, mmcs: challenge_mmcs, }; let pcs = Pcs { @@ -249,9 +247,17 @@ mod m31_fri_pcs { } mod blowup_1 { - make_tests_for_pcs!(super::get_pcs(1)); + make_tests_for_pcs!(super::get_pcs(1, 1)); } mod blowup_2 { - make_tests_for_pcs!(super::get_pcs(2)); + make_tests_for_pcs!(super::get_pcs(1, 2)); + } + + mod arity_4 { + make_tests_for_pcs!(super::get_pcs(2, 2)); + } + + mod arity_8 { + make_tests_for_pcs!(super::get_pcs(3, 2)); } } diff --git a/merkle-tree/src/mmcs.rs b/merkle-tree/src/mmcs.rs index 8a8a20fc9..6a1bfb478 100644 --- a/merkle-tree/src/mmcs.rs +++ b/merkle-tree/src/mmcs.rs @@ -82,9 +82,6 @@ where index: usize, prover_data: &MerkleTree, ) -> (Vec>, Vec<[PW::Value; DIGEST_ELEMS]>) { - // TODO[osama]: delete this - debug!("opening batch with index: {}", index); - let max_height = self.get_max_height(prover_data); let log_max_height = log2_ceil_usize(max_height); diff --git a/uni-stark/tests/mul_air.rs b/uni-stark/tests/mul_air.rs index 16f868441..4351de135 100644 --- a/uni-stark/tests/mul_air.rs +++ b/uni-stark/tests/mul_air.rs @@ -219,6 +219,7 @@ fn do_test_bb_twoadic(log_blowup: usize, degree: u64, log_n: usize) -> Result<() log_final_poly_len: 5, num_queries: 40, proof_of_work_bits: 8, + arity_bits: 1, mmcs: challenge_mmcs, }; type Pcs = TwoAdicFriPcs; @@ -280,6 +281,7 @@ fn do_test_m31_circle(log_blowup: usize, degree: u64, log_n: usize) -> Result<() log_final_poly_len: 0, num_queries: 40, proof_of_work_bits: 8, + arity_bits: 1, mmcs: challenge_mmcs, }; From a08c2e296d3836971bef2844f8e77a2171d0a1df Mon Sep 17 00:00:00 2001 From: Osama Alkhodairy Date: Fri, 20 Dec 2024 18:27:23 +0200 Subject: [PATCH 03/12] factored out folding --- fri/src/verifier.rs | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/fri/src/verifier.rs b/fri/src/verifier.rs index f4baf236e..160503aea 100644 --- a/fri/src/verifier.rs +++ b/fri/src/verifier.rs @@ -194,20 +194,7 @@ where log_folded_height -= 1; index_folded_row >>= 1; - // TODO[osama]: factor this out - let folded_matrix = RowMajorMatrix::new(folded_row, 2); - folded_row = folded_matrix - .par_rows() - .enumerate() - .map(|(i, row)| { - g.fold_row( - index_folded_row + i, - log_folded_height, - beta, - row.into_iter(), - ) - }) - .collect(); + folded_row = fold_partial_row(g, index_folded_row, log_folded_height, beta, folded_row); if let Some(poly_eval) = opened_rows_iter.next_if(|v| v.len() == folded_row.len()) { izip!(&mut folded_row, poly_eval).for_each(|(f, v)| *f += *v); @@ -230,3 +217,16 @@ where Ok(folded_eval) } + +fn fold_partial_row(g: &G, index: usize, log_height: usize, beta: F, evals: Vec) -> Vec +where + G: FriGenericConfig, + F: Field, +{ + let folded_matrix = RowMajorMatrix::new(evals, 2); + folded_matrix + .par_rows() + .enumerate() + .map(|(i, row)| g.fold_row(index + i, log_height, beta, row.into_iter())) + .collect() +} From b299ada94b4d9bbe9e076bb06e99fad65b0a43c1 Mon Sep 17 00:00:00 2001 From: Osama Alkhodairy Date: Fri, 20 Dec 2024 18:32:38 +0200 Subject: [PATCH 04/12] fix lint --- fri/Cargo.toml | 1 - fri/src/prover.rs | 6 ++---- fri/src/verifier.rs | 3 ++- merkle-tree/src/mmcs.rs | 1 - 4 files changed, 4 insertions(+), 7 deletions(-) diff --git a/fri/Cargo.toml b/fri/Cargo.toml index c62bae7bf..6c633adb7 100644 --- a/fri/Cargo.toml +++ b/fri/Cargo.toml @@ -17,7 +17,6 @@ itertools.workspace = true rand.workspace = true tracing.workspace = true serde = { workspace = true, features = ["derive", "alloc"] } -tracing-subscriber.workspace = true [dev-dependencies] p3-baby-bear.workspace = true diff --git a/fri/src/prover.rs b/fri/src/prover.rs index 5d23a3c41..eecbaedd6 100644 --- a/fri/src/prover.rs +++ b/fri/src/prover.rs @@ -7,10 +7,8 @@ use p3_challenger::{CanObserve, FieldChallenger, GrindingChallenger}; use p3_commit::Mmcs; use p3_dft::{Radix2Dit, TwoAdicSubgroupDft}; use p3_field::{ExtensionField, Field, TwoAdicField}; -use p3_matrix::{ - dense::{DenseMatrix, RowMajorMatrix}, - Matrix, -}; +use p3_matrix::dense::{DenseMatrix, RowMajorMatrix}; +use p3_matrix::Matrix; use p3_util::{log2_strict_usize, reverse_slice_index_bits}; use tracing::{debug, debug_span, info_span, instrument}; diff --git a/fri/src/verifier.rs b/fri/src/verifier.rs index 160503aea..8864560f7 100644 --- a/fri/src/verifier.rs +++ b/fri/src/verifier.rs @@ -4,7 +4,8 @@ use itertools::{izip, Itertools}; use p3_challenger::{CanObserve, FieldChallenger, GrindingChallenger}; use p3_commit::Mmcs; use p3_field::{ExtensionField, Field, TwoAdicField}; -use p3_matrix::{dense::RowMajorMatrix, Dimensions, Matrix}; +use p3_matrix::dense::RowMajorMatrix; +use p3_matrix::{Dimensions, Matrix}; use p3_util::reverse_bits_len; use tracing::debug; diff --git a/merkle-tree/src/mmcs.rs b/merkle-tree/src/mmcs.rs index 6a1bfb478..e723fa9bf 100644 --- a/merkle-tree/src/mmcs.rs +++ b/merkle-tree/src/mmcs.rs @@ -1,7 +1,6 @@ use alloc::vec::Vec; use core::cmp::Reverse; use core::marker::PhantomData; -use tracing::debug; use itertools::Itertools; use p3_commit::Mmcs; From 982e54e1afc234eda828e280bb977f00beb2c6d6 Mon Sep 17 00:00:00 2001 From: Osama Alkhodairy Date: Fri, 20 Dec 2024 18:40:05 +0200 Subject: [PATCH 05/12] remove par for now --- fri/src/verifier.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fri/src/verifier.rs b/fri/src/verifier.rs index 8864560f7..384a2c31d 100644 --- a/fri/src/verifier.rs +++ b/fri/src/verifier.rs @@ -226,7 +226,7 @@ where { let folded_matrix = RowMajorMatrix::new(evals, 2); folded_matrix - .par_rows() + .rows() .enumerate() .map(|(i, row)| g.fold_row(index + i, log_height, beta, row.into_iter())) .collect() From 16627d79f2053ad35a9b6a64549d95829b1eefeb Mon Sep 17 00:00:00 2001 From: Osama Alkhodairy Date: Fri, 20 Dec 2024 18:53:28 +0200 Subject: [PATCH 06/12] remove debug! statements --- fri/src/prover.rs | 18 +----------------- fri/src/verifier.rs | 14 +------------- 2 files changed, 2 insertions(+), 30 deletions(-) diff --git a/fri/src/prover.rs b/fri/src/prover.rs index eecbaedd6..867f994b2 100644 --- a/fri/src/prover.rs +++ b/fri/src/prover.rs @@ -8,9 +8,8 @@ use p3_commit::Mmcs; use p3_dft::{Radix2Dit, TwoAdicSubgroupDft}; use p3_field::{ExtensionField, Field, TwoAdicField}; use p3_matrix::dense::{DenseMatrix, RowMajorMatrix}; -use p3_matrix::Matrix; use p3_util::{log2_strict_usize, reverse_slice_index_bits}; -use tracing::{debug, debug_span, info_span, instrument}; +use tracing::{debug_span, info_span, instrument}; use crate::{CommitPhaseProofStep, FriConfig, FriGenericConfig, FriProof, QueryProof}; @@ -98,10 +97,6 @@ where while folded.len() > config.blowup() * config.final_poly_len() { let arity = config.arity(); - debug!("arity: {}", arity); - - debug!("proving commit layer: folded.len(): {}, config.blowup(): {}, config.final_poly_len(): {}", folded.len(), config.blowup(), config.final_poly_len()); - let next_folded_len = folded.len() / arity; // First, we collect the polynomial evaluations that will be committed this round. @@ -125,11 +120,6 @@ where .chain(polys_before_next_round) .collect(); - debug!("committing matrices with the following dimensions:"); - for matrix in &matrices_to_commit { - debug!("width: {}, height: {}", matrix.width, matrix.height()); - } - let (commit, prover_data) = config.mmcs.commit(matrices_to_commit); challenger.observe(commit.clone()); @@ -203,18 +193,12 @@ where F: Field, M: Mmcs, { - debug!("answer query with index: {}", index); commit_phase_commits .iter() .enumerate() .map(|(i, commit)| { let index_row = index >> ((i + 1) * config.arity_bits); - debug!( - "query commit iteration: commit_i: {}, index_row: {}", - i, index_row - ); - let (opened_rows, opening_proof) = config.mmcs.open_batch(index_row, commit); assert_eq!( opened_rows[0].len(), diff --git a/fri/src/verifier.rs b/fri/src/verifier.rs index 384a2c31d..56bba2d61 100644 --- a/fri/src/verifier.rs +++ b/fri/src/verifier.rs @@ -7,7 +7,6 @@ use p3_field::{ExtensionField, Field, TwoAdicField}; use p3_matrix::dense::RowMajorMatrix; use p3_matrix::{Dimensions, Matrix}; use p3_util::reverse_bits_len; -use tracing::debug; use crate::{CommitPhaseProofStep, FriConfig, FriGenericConfig, FriProof}; @@ -126,24 +125,13 @@ where M: Mmcs + 'a, G: FriGenericConfig, { - debug!( - "verifying query proof, index: {:?}, reduced_openings: {:#?}", - index, reduced_openings - ); - let mut folded_eval = F::ZERO; let mut ro_iter = reduced_openings.into_iter().peekable(); let mut log_folded_height = log_max_height; while log_folded_height > config.log_blowup + config.log_final_poly_len { - debug!( - "query iteration, log_folded_height: {:?}", - log_folded_height - ); - - if let Some((lh, ro)) = ro_iter.next_if(|(lh, _)| *lh == log_folded_height) { - debug!("adding directly from reduced openings at lh: {:?}", lh); + if let Some((_, ro)) = ro_iter.next_if(|(lh, _)| *lh == log_folded_height) { folded_eval += ro; } From 26f1257e4ecb47c438ed100f4bd6174d9c831757 Mon Sep 17 00:00:00 2001 From: Osama Alkhodairy Date: Sun, 22 Dec 2024 09:01:58 +0200 Subject: [PATCH 07/12] fix: when arity is more than folded.len() --- fri/Cargo.toml | 1 + fri/src/prover.rs | 32 ++++++++++++++++++++++++-------- fri/src/verifier.rs | 30 ++++++++++++++++++------------ fri/tests/fri.rs | 33 ++++++++++++++++++++++++++------- 4 files changed, 69 insertions(+), 27 deletions(-) diff --git a/fri/Cargo.toml b/fri/Cargo.toml index 6c633adb7..c62bae7bf 100644 --- a/fri/Cargo.toml +++ b/fri/Cargo.toml @@ -17,6 +17,7 @@ itertools.workspace = true rand.workspace = true tracing.workspace = true serde = { workspace = true, features = ["derive", "alloc"] } +tracing-subscriber.workspace = true [dev-dependencies] p3-baby-bear.workspace = true diff --git a/fri/src/prover.rs b/fri/src/prover.rs index 867f994b2..7296f6c35 100644 --- a/fri/src/prover.rs +++ b/fri/src/prover.rs @@ -90,14 +90,35 @@ where Challenger: FieldChallenger + CanObserve, G: FriGenericConfig, { + // To illustrate the folding logic with arity > 2, let's go through an example. + // Suppose `inputs` consists of three polynomials with degrees 8, 4, and 2. + // There will be two FRI commitment layers: one at height 8 and one at height 2. + + // The first commitment layer will consist of two matrices: + // - one of dimensions 2x4 corresponding to the first polynomial's evaluations + // - one of dimensions 2x2 corresponding to the second polynomial's evaluations + + // The polynomial folding happens incrementally as follows: the first polynomial is folded + // once so its number of evaluations is halved, the second polynomial's evaluations are added + // to that, and then the sum is folded further to reduce the number of evaluations to 2. + + // At that point, the third polynomial's evaluations are added to the running sum, and that sum + // is committed to form the second FRI commitment layer. The only matrix in this layer is of + // dimensions 2x1 + + // TODO[osama]: update all heights above ^ + let mut inputs_iter = inputs.into_iter().peekable(); let mut folded = inputs_iter.next().unwrap(); let mut commits = vec![]; let mut data = vec![]; + let arity = config.arity(); + while folded.len() > config.blowup() * config.final_poly_len() { - let arity = config.arity(); - let next_folded_len = folded.len() / arity; + let cur_arity = arity.min(folded.len()); + + let next_folded_len = folded.len() / cur_arity; // First, we collect the polynomial evaluations that will be committed this round. // Those are `folded` and polynomials in `inputs` not consumed yet with number of @@ -115,7 +136,7 @@ where cur_folded_len /= 2; } - let folded_matrix = RowMajorMatrix::new(folded.clone(), arity); + let folded_matrix = RowMajorMatrix::new(folded.clone(), cur_arity); let matrices_to_commit: Vec> = iter::once(folded_matrix) .chain(polys_before_next_round) .collect(); @@ -200,11 +221,6 @@ where let index_row = index >> ((i + 1) * config.arity_bits); let (opened_rows, opening_proof) = config.mmcs.open_batch(index_row, commit); - assert_eq!( - opened_rows[0].len(), - config.arity(), - "The folded array should be of size arity" - ); CommitPhaseProofStep { opened_rows, diff --git a/fri/src/verifier.rs b/fri/src/verifier.rs index 56bba2d61..c7ed05747 100644 --- a/fri/src/verifier.rs +++ b/fri/src/verifier.rs @@ -14,6 +14,7 @@ use crate::{CommitPhaseProofStep, FriConfig, FriGenericConfig, FriProof}; pub enum FriError { InvalidProofShape, CommitPhaseMmcsError(CommitMmcsErr), + OpenedRowMismatch, InputError(InputError), FinalPolyMismatch, InvalidPowWitness, @@ -131,26 +132,31 @@ where let mut log_folded_height = log_max_height; while log_folded_height > config.log_blowup + config.log_final_poly_len { + let cur_arity_bits = config.arity_bits.min(log_folded_height); + if let Some((_, ro)) = ro_iter.next_if(|(lh, _)| *lh == log_folded_height) { folded_eval += ro; } let (&beta, comm, opening) = steps.next().unwrap(); - let index_row = index >> config.arity_bits; + let index_row = index >> cur_arity_bits; // Verify that `folded_eval` and evals from reduced_openings match opened_rows - assert_eq!(folded_eval, opening.opened_rows[0][index % config.arity()]); + if folded_eval != opening.opened_rows[0][index % config.arity()] { + return Err(FriError::OpenedRowMismatch); + } for row in opening.opened_rows.iter().skip(1) { - // TODO[osama]: consider adding an assert for the length of row to make sure the right thing is being read let (lh, ro) = ro_iter.next().unwrap(); + // Make sure the read row is of the correct length + if row.len() != 1 << (lh + cur_arity_bits - log_folded_height) { + return Err(FriError::OpenedRowMismatch); + } + let current_index = index >> (log_folded_height - lh); - assert_eq!( - ro, - row[current_index % row.len()], - "reduced opening mismatch at level {:?}", - lh - ); + if ro != row[current_index % row.len()] { + return Err(FriError::OpenedRowMismatch); + } } let dims: Vec<_> = opening @@ -158,7 +164,7 @@ where .iter() .map(|opened_row| Dimensions { width: opened_row.len(), - height: 1 << (log_folded_height - config.arity_bits), + height: 1 << (log_folded_height - cur_arity_bits), }) .collect(); @@ -177,7 +183,7 @@ where let mut opened_rows_iter = opening.opened_rows.iter().peekable(); let mut folded_row = opened_rows_iter.next().unwrap().clone(); - let mut index_folded_row = index_row << config.arity_bits; + let mut index_folded_row = index_row << cur_arity_bits; while folded_row.len() > 1 { index >>= 1; log_folded_height -= 1; @@ -190,7 +196,7 @@ where } } - folded_eval = folded_row.remove(0); + folded_eval = folded_row.pop().unwrap(); assert!(folded_row.is_empty()); } diff --git a/fri/tests/fri.rs b/fri/tests/fri.rs index db6de6841..481175165 100644 --- a/fri/tests/fri.rs +++ b/fri/tests/fri.rs @@ -16,6 +16,7 @@ use p3_symmetric::{PaddingFreeSponge, TruncatedPermutation}; use p3_util::log2_strict_usize; use rand::{Rng, SeedableRng}; use rand_chacha::ChaCha20Rng; +use tracing::Level; type Val = BabyBear; type Challenge = BinomialExtensionField; @@ -31,6 +32,7 @@ type MyFriConfig = FriConfig; fn get_ldt_for_testing( rng: &mut R, + log_blowup: usize, arity_bits: usize, log_final_poly_len: usize, ) -> (Perm, MyFriConfig) { @@ -39,7 +41,7 @@ fn get_ldt_for_testing( let compress = MyCompress::new(perm.clone()); let mmcs = ChallengeMmcs::new(ValMmcs::new(hash, compress)); let fri_config = FriConfig { - log_blowup: 1, + log_blowup, log_final_poly_len, num_queries: 10, proof_of_work_bits: 8, @@ -49,16 +51,23 @@ fn get_ldt_for_testing( (perm, fri_config) } -fn do_test_fri_ldt(rng: &mut R, arity_bits: usize, log_final_poly_len: usize) { - let (perm, fc) = get_ldt_for_testing(rng, arity_bits, log_final_poly_len); +fn do_test_fri_ldt( + rng: &mut R, + log_blowup: usize, + arity_bits: usize, + log_final_poly_len: usize, + deg_low: usize, + deg_high: usize, +) { + let (perm, fc) = get_ldt_for_testing(rng, log_blowup, arity_bits, log_final_poly_len); let dft = Radix2Dit::default(); let shift = Val::GENERATOR; - let ldes: Vec> = (5..10) + let ldes: Vec> = (deg_low..deg_high) .map(|deg_bits| { let evals = RowMajorMatrix::::rand_nonzero(rng, 1 << deg_bits, 16); - let mut lde = dft.coset_lde_batch(evals, 1, shift); + let mut lde = dft.coset_lde_batch(evals, fc.log_blowup, shift); reverse_matrix_index_bits(&mut lde); lde }) @@ -138,7 +147,7 @@ fn test_fri_ldt() { for arity_bits in 1..3 { for log_final_poly_len in 0..5 { let mut rng = ChaCha20Rng::seed_from_u64(log_final_poly_len as u64); - do_test_fri_ldt(&mut rng, arity_bits, log_final_poly_len); + do_test_fri_ldt(&mut rng, 1, arity_bits, log_final_poly_len, 5, 10); } } } @@ -150,6 +159,16 @@ fn test_fri_ldt_should_panic() { // FRI is kind of flaky depending on indexing luck for i in 0..4 { let mut rng = ChaCha20Rng::seed_from_u64(i); - do_test_fri_ldt(&mut rng, 1, 5); + do_test_fri_ldt(&mut rng, 1, 1, 5, 5, 10); } } + +#[test] +fn test_fri_arity_4() { + tracing_subscriber::fmt() + .with_max_level(Level::DEBUG) + .init(); + + let mut rng = ChaCha20Rng::seed_from_u64(0); + do_test_fri_ldt(&mut rng, 0, 2, 0, 1, 4); +} From b3f916e1ecb7aceed410f55bde3542d60eb0cdcc Mon Sep 17 00:00:00 2001 From: Osama Alkhodairy Date: Sun, 22 Dec 2024 09:07:26 +0200 Subject: [PATCH 08/12] update example --- fri/src/prover.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/fri/src/prover.rs b/fri/src/prover.rs index 7296f6c35..358b896e3 100644 --- a/fri/src/prover.rs +++ b/fri/src/prover.rs @@ -91,22 +91,22 @@ where G: FriGenericConfig, { // To illustrate the folding logic with arity > 2, let's go through an example. - // Suppose `inputs` consists of three polynomials with degrees 8, 4, and 2. - // There will be two FRI commitment layers: one at height 8 and one at height 2. + // Suppose `inputs` consists of three polynomials with degrees 16, 8, and 4, and + // suppose that arity = 4 and final_poly_len = 1. + // There will be two FRI commitment layers: one at height 16 and one at height 4. // The first commitment layer will consist of two matrices: - // - one of dimensions 2x4 corresponding to the first polynomial's evaluations - // - one of dimensions 2x2 corresponding to the second polynomial's evaluations + // - one of dimensions 4x4 corresponding to the first polynomial's evaluations + // - one of dimensions 4x2 corresponding to the second polynomial's evaluations // The polynomial folding happens incrementally as follows: the first polynomial is folded // once so its number of evaluations is halved, the second polynomial's evaluations are added - // to that, and then the sum is folded further to reduce the number of evaluations to 2. + // to that, and then the sum is folded by 2 further to reduce the number of evaluations to 4. // At that point, the third polynomial's evaluations are added to the running sum, and that sum - // is committed to form the second FRI commitment layer. The only matrix in this layer is of - // dimensions 2x1 - - // TODO[osama]: update all heights above ^ + // is committed to form the second FRI commitment layer. The only matrix in that layer is of + // dimensions 4x1. The final polynomial's evaluation can then be computed through those 4 + // evaluations. let mut inputs_iter = inputs.into_iter().peekable(); let mut folded = inputs_iter.next().unwrap(); From 1d9f871b43477e46ce8f67a76ae1e4a6e618eaa8 Mon Sep 17 00:00:00 2001 From: Osama Alkhodairy Date: Sun, 22 Dec 2024 09:25:54 +0200 Subject: [PATCH 09/12] fix: square beta --- fri/src/prover.rs | 3 ++- fri/src/verifier.rs | 7 ++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/fri/src/prover.rs b/fri/src/prover.rs index 358b896e3..86aaf423d 100644 --- a/fri/src/prover.rs +++ b/fri/src/prover.rs @@ -145,7 +145,7 @@ where challenger.observe(commit.clone()); // Next, we fold `folded` and `polys_before_next_round` to prepare for the next round - let beta: Challenge = challenger.sample_ext_element(); + let mut beta: Challenge = challenger.sample_ext_element(); // Get a reference to the committed matrices let leaves = config.mmcs.get_matrices(&prover_data); @@ -156,6 +156,7 @@ where while folded.len() > next_folded_len { let matrix_to_fold = RowMajorMatrix::new(folded, 2); folded = g.fold_matrix(beta, matrix_to_fold); + beta = beta.square(); if let Some(poly_eval) = leaves_iter.next_if(|v| v.values.len() == folded.len()) { izip!(&mut folded, &poly_eval.values).for_each(|(f, v)| *f += *v); diff --git a/fri/src/verifier.rs b/fri/src/verifier.rs index c7ed05747..cb5c2d3ee 100644 --- a/fri/src/verifier.rs +++ b/fri/src/verifier.rs @@ -74,7 +74,7 @@ where config, index >> g.extra_query_index_bits(), izip!( - &betas, + betas.clone(), &proof.commit_phase_commits, &qp.commit_phase_openings ), @@ -108,7 +108,7 @@ where } type CommitStep<'a, F, M> = ( - &'a F, + F, &'a >::Commitment, &'a CommitPhaseProofStep, ); @@ -138,7 +138,7 @@ where folded_eval += ro; } - let (&beta, comm, opening) = steps.next().unwrap(); + let (mut beta, comm, opening) = steps.next().unwrap(); let index_row = index >> cur_arity_bits; @@ -190,6 +190,7 @@ where index_folded_row >>= 1; folded_row = fold_partial_row(g, index_folded_row, log_folded_height, beta, folded_row); + beta = beta.square(); if let Some(poly_eval) = opened_rows_iter.next_if(|v| v.len() == folded_row.len()) { izip!(&mut folded_row, poly_eval).for_each(|(f, v)| *f += *v); From 9605de05cabecf42d4b28af9c079eec7053c3ec8 Mon Sep 17 00:00:00 2001 From: Osama Alkhodairy Date: Sat, 11 Jan 2025 13:00:54 -0500 Subject: [PATCH 10/12] fix: use correct arity --- fri/src/verifier.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fri/src/verifier.rs b/fri/src/verifier.rs index cb5c2d3ee..bd2c7bdf0 100644 --- a/fri/src/verifier.rs +++ b/fri/src/verifier.rs @@ -143,7 +143,7 @@ where let index_row = index >> cur_arity_bits; // Verify that `folded_eval` and evals from reduced_openings match opened_rows - if folded_eval != opening.opened_rows[0][index % config.arity()] { + if folded_eval != opening.opened_rows[0][index % (1 << cur_arity_bits)] { return Err(FriError::OpenedRowMismatch); } for row in opening.opened_rows.iter().skip(1) { From 99082313d126398c07081ce352d32331488bda6e Mon Sep 17 00:00:00 2001 From: Osama Alkhodairy Date: Sat, 11 Jan 2025 13:01:13 -0500 Subject: [PATCH 11/12] fix comment --- fri/src/prover.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fri/src/prover.rs b/fri/src/prover.rs index 86aaf423d..0a9a01ff8 100644 --- a/fri/src/prover.rs +++ b/fri/src/prover.rs @@ -105,7 +105,7 @@ where // At that point, the third polynomial's evaluations are added to the running sum, and that sum // is committed to form the second FRI commitment layer. The only matrix in that layer is of - // dimensions 4x1. The final polynomial's evaluation can then be computed through those 4 + // dimensions 1x4. The final polynomial's evaluation can then be computed through those 4 // evaluations. let mut inputs_iter = inputs.into_iter().peekable(); From ad264ca6b7939e4165b336c7a4cfea3be4d56380 Mon Sep 17 00:00:00 2001 From: Osama Alkhodairy Date: Sun, 12 Jan 2025 17:18:41 -0500 Subject: [PATCH 12/12] add comment --- fri/src/verifier.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fri/src/verifier.rs b/fri/src/verifier.rs index bd2c7bdf0..1e7e3f0bf 100644 --- a/fri/src/verifier.rs +++ b/fri/src/verifier.rs @@ -82,6 +82,8 @@ where log_max_height, )?; + // Even though we might do a partial fold at the end, it's okay to shift by arity_bits since in that case + // final_poly_index is zero let final_poly_index = index >> (proof.commit_phase_commits.len() * config.arity_bits); let mut eval = Challenge::ZERO;