From 38009b4bb7de3ead5401974c400f92c8c97cf79c Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Mon, 27 Oct 2025 12:44:19 +0100 Subject: [PATCH 01/11] fix typo, chng comment --- src/multilinear/provers/time/core.rs | 2 +- src/streams/stream.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/multilinear/provers/time/core.rs b/src/multilinear/provers/time/core.rs index 26464668..a6591f76 100644 --- a/src/multilinear/provers/time/core.rs +++ b/src/multilinear/provers/time/core.rs @@ -14,7 +14,7 @@ pub struct TimeProver> { pub claim: F, pub current_round: usize, pub evaluations: Option>, - pub evaluation_stream: S, // Keep this for now, case we can do some small optimizations of first round etc + pub evaluation_stream: S, // TODO (z-tech): this can be released after the first call to vsbw_reduce_evaluations pub num_variables: usize, } diff --git a/src/streams/stream.rs b/src/streams/stream.rs index 4655ef9a..c08bc9c8 100644 --- a/src/streams/stream.rs +++ b/src/streams/stream.rs @@ -7,7 +7,7 @@ pub fn multivariate_claim>(stream: S) -> F { for i in 0..num_evaluations { let eval = stream.evaluation(i); - claim += eval * eval; + claim += eval; } claim From af56b5eb76fdca7015d901491cd11db7bb34e582 Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Mon, 27 Oct 2025 17:05:45 +0100 Subject: [PATCH 02/11] chkpt --- src/multilinear/provers/time/core.rs | 40 ++++++++--------- src/multilinear/provers/time/prover.rs | 4 +- src/multilinear/sumcheck.rs | 11 +++-- src/tests/multilinear/mod.rs | 62 ++++++++++++++++++++++++++ 4 files changed, 90 insertions(+), 27 deletions(-) diff --git a/src/multilinear/provers/time/core.rs b/src/multilinear/provers/time/core.rs index a6591f76..bec52e47 100644 --- a/src/multilinear/provers/time/core.rs +++ b/src/multilinear/provers/time/core.rs @@ -23,9 +23,6 @@ impl> TimeProver { self.num_variables - self.current_round } pub fn vsbw_evaluate(&self) -> (F, F) { - // Calculate the bitmask for the number of free variables - let bitmask: usize = 1 << (self.num_free_variables() - 1); - // Determine the length of evaluations to iterate through let evaluations_len = match &self.evaluations { Some(evaluations) => evaluations.len(), @@ -34,22 +31,26 @@ impl> TimeProver { #[cfg(feature = "parallel")] let (sum_0, sum_1) = cfg_into_iter!(0..evaluations_len) - .map(|i| { - // Get the point evaluation - let val = if let Some(evals) = &self.evaluations { - evals[i] + .step_by(2) + .map(|i0| { + let i1 = i0 + 1; + + // Get evaluations for this pair + let v0 = if let Some(evals) = &self.evaluations { + evals[i0] } else { - self.evaluation_stream.evaluation(i) + self.evaluation_stream.evaluation(i0) }; - // Route value into the proper bucket - if (i & bitmask) == 0 { - (val, F::zero()) // contributes to sum_0 + let v1 = if let Some(evals) = &self.evaluations { + evals[i1] } else { - (F::zero(), val) // contributes to sum_1 - } + self.evaluation_stream.evaluation(i1) + }; + + // Return as (sum0 contribution, sum1 contribution) + (v0, v1) }) - // Combine partial (sum0, sum1) pairs from each worker/thread. .reduce( || (F::zero(), F::zero()), |(a0, a1), (b0, b1)| (a0 + b0, a1 + b1), @@ -103,9 +104,6 @@ impl> TimeProver { None => evaluations.len(), }; - // Calculate what bit needs to be set to index the second half of the last round's evaluations - let setbit: usize = 1 << self.num_free_variables(); - #[cfg(feature = "parallel")] { // We'll write to the first half only. @@ -117,8 +115,8 @@ impl> TimeProver { dest.par_iter_mut() .enumerate() .for_each(|(i0, slot): (usize, &mut F)| { - let i1 = i0 | setbit; - let v0 = src[i0]; + let i1 = (i0 * 2) + 1; + let v0 = src[i0 * 2]; let v1 = src[i1]; *slot = v0 * verifier_message_hat + v1 * verifier_message; }); @@ -128,8 +126,8 @@ impl> TimeProver { dest.par_iter_mut() .enumerate() .for_each(|(i0, slot): (usize, &mut F)| { - let i1 = i0 | setbit; - let v0 = stream.evaluation(i0); + let i1 = (i0 * 2) + 1; + let v0 = stream.evaluation(i0 * 2); let v1 = stream.evaluation(i1); *slot = v0 * verifier_message_hat + v1 * verifier_message; }); diff --git a/src/multilinear/provers/time/prover.rs b/src/multilinear/provers/time/prover.rs index 9c342581..0a486b6c 100644 --- a/src/multilinear/provers/time/prover.rs +++ b/src/multilinear/provers/time/prover.rs @@ -56,11 +56,11 @@ mod tests { use crate::{ multilinear::TimeProver, streams::MemoryStream, - tests::{multilinear::sanity_test, F19}, + tests::{multilinear::pairwise_sanity_test, F19}, }; #[test] fn sumcheck() { - sanity_test::, TimeProver>>(); + pairwise_sanity_test::, TimeProver>>(); } } diff --git a/src/multilinear/sumcheck.rs b/src/multilinear/sumcheck.rs index affb44c5..cfdc78d5 100644 --- a/src/multilinear/sumcheck.rs +++ b/src/multilinear/sumcheck.rs @@ -94,9 +94,12 @@ mod tests { TimeProver>, >(&mut time_prover, &mut ark_std::test_rng()); // ensure the transcript is identical - assert_eq!( - time_prover_transcript.prover_messages, - blendy_prover_transcript.prover_messages - ); + // assert_eq!( + // time_prover_transcript.prover_messages, + // blendy_prover_transcript.prover_messages + // ); + // blendy is variable compression time prover pairwise + assert!(blendy_prover_transcript.is_accepted); + assert!(time_prover_transcript.is_accepted); } } diff --git a/src/tests/multilinear/mod.rs b/src/tests/multilinear/mod.rs index 98beda55..dda0ebc8 100644 --- a/src/tests/multilinear/mod.rs +++ b/src/tests/multilinear/mod.rs @@ -84,3 +84,65 @@ where F::from(1_u32), ); } + +pub fn pairwise_sanity_test() +where + F: Field, + S: Stream + From>, + P: Prover, ProverMessage = Option<(F, F)>>, + P::ProverConfig: ProverConfig, +{ + let s: S = MemoryStream::new(three_variable_polynomial_evaluations()).into(); + let mut p = P::new(ProverConfig::default(F::from(6_u32), 3, s)); + /* + * Zeroth Round: All variables are free + * + * Evaluations at different input points: + * (0,0,0) → 0 + * (0,0,1) → 0 + * (0,1,0) → 13 + * (0,1,1) → 1 + * (1,0,0) → 2 + * (1,0,1) → 2 + * (1,1,0) → 0 + * (1,1,1) → 7 + * + * Sum evens: g₀(0) ≡ 15 + * Sum odds: g₀(1) ≡ 10 + */ + multilinear_round_sanity::(&mut p, None, F::from(15_u32), F::from(10_u32)); + /* + * First Round: x₀ fixed to 3 + * + * Evaluations at different input points (adjacent points compressed): + * (3,0,0) → 0 + * (3,0,1) → 15 + * (3,1,0) → 2 + * (3,1,1) → 2 + * + * Sum evens: g₀(0) ≡ 2 + * Sum odds: g₀(1) ≡ 17 + */ + multilinear_round_sanity::( + &mut p, + Some(F::from(3_u32)), + F::from(2_u32), + F::from(17_u32), + ); + /* + * Last Round: x₁ fixed to 4 + * + * Evaluations at different input points: + * (3,4,0) → 3 + * (3,4,1) → 2 + * + * Sum evens: g₀(0) ≡ 3 + * Sum odds: g₀(1) ≡ 2 + */ + multilinear_round_sanity::( + &mut p, + Some(F::from(4_u32)), + F::from(3_u32), + F::from(2_u32), + ); +} From 914c9e2e6bc55640ad057a1d89ad65f274d680be Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Mon, 27 Oct 2025 17:07:03 +0100 Subject: [PATCH 03/11] clippy --- src/multilinear/provers/time/core.rs | 3 --- src/tests/multilinear/mod.rs | 6 +++--- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/multilinear/provers/time/core.rs b/src/multilinear/provers/time/core.rs index bec52e47..7a5b8a27 100644 --- a/src/multilinear/provers/time/core.rs +++ b/src/multilinear/provers/time/core.rs @@ -19,9 +19,6 @@ pub struct TimeProver> { } impl> TimeProver { - fn num_free_variables(&self) -> usize { - self.num_variables - self.current_round - } pub fn vsbw_evaluate(&self) -> (F, F) { // Determine the length of evaluations to iterate through let evaluations_len = match &self.evaluations { diff --git a/src/tests/multilinear/mod.rs b/src/tests/multilinear/mod.rs index dda0ebc8..7ee03ece 100644 --- a/src/tests/multilinear/mod.rs +++ b/src/tests/multilinear/mod.rs @@ -106,7 +106,7 @@ where * (1,0,1) → 2 * (1,1,0) → 0 * (1,1,1) → 7 - * + * * Sum evens: g₀(0) ≡ 15 * Sum odds: g₀(1) ≡ 10 */ @@ -119,7 +119,7 @@ where * (3,0,1) → 15 * (3,1,0) → 2 * (3,1,1) → 2 - * + * * Sum evens: g₀(0) ≡ 2 * Sum odds: g₀(1) ≡ 17 */ @@ -135,7 +135,7 @@ where * Evaluations at different input points: * (3,4,0) → 3 * (3,4,1) → 2 - * + * * Sum evens: g₀(0) ≡ 3 * Sum odds: g₀(1) ≡ 2 */ From eb4c9d8479ea4a517714bfa766365e9a41ea9c0e Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Tue, 28 Oct 2025 14:27:02 +0100 Subject: [PATCH 04/11] support n streams multilinear TimeProver --- src/multilinear/provers/space/config.rs | 21 +++++++++++++++++---- src/multilinear/provers/space/core.rs | 14 ++++++++------ src/multilinear/provers/space/prover.rs | 2 +- src/prover/core.rs | 4 ++++ src/prover/mod.rs | 2 +- 5 files changed, 31 insertions(+), 12 deletions(-) diff --git a/src/multilinear/provers/space/config.rs b/src/multilinear/provers/space/config.rs index 4aaba1ee..049106f1 100644 --- a/src/multilinear/provers/space/config.rs +++ b/src/multilinear/provers/space/config.rs @@ -1,6 +1,9 @@ use ark_ff::Field; -use crate::{prover::ProverConfig, streams::Stream}; +use crate::{ + prover::{BatchProverConfig, ProverConfig}, + streams::Stream, +}; pub struct SpaceProverConfig where @@ -9,7 +12,7 @@ where { pub num_variables: usize, pub claim: F, - pub stream: S, + pub streams: Vec, } impl SpaceProverConfig @@ -21,7 +24,7 @@ where Self { claim, num_variables, - stream, + streams: vec![stream], } } } @@ -31,7 +34,17 @@ impl> ProverConfig for SpaceProverConfig { Self { claim, num_variables, - stream, + streams: vec![stream], + } + } +} + +impl> BatchProverConfig for SpaceProverConfig { + fn default(claim: F, num_variables: usize, streams: Vec) -> Self { + Self { + claim, + num_variables, + streams, } } } diff --git a/src/multilinear/provers/space/core.rs b/src/multilinear/provers/space/core.rs index f9d5f902..825664da 100644 --- a/src/multilinear/provers/space/core.rs +++ b/src/multilinear/provers/space/core.rs @@ -8,7 +8,7 @@ use crate::{ pub struct SpaceProver> { pub claim: F, pub current_round: usize, - pub evaluation_stream: S, + pub evaluation_streams: Vec, pub num_variables: usize, pub verifier_messages: Vec, pub verifier_message_hats: Vec, @@ -49,11 +49,13 @@ impl> SpaceProver { // Check if the bit at the position specified by the bitmask is set let is_set: bool = (evaluation_index & bitmask) != 0; - // Use match to accumulate the appropriate value based on whether the bit is set or not - let inner_sum = self.evaluation_stream.evaluation(evaluation_index) * lag_poly; - match is_set { - false => sum_0 += inner_sum, - true => sum_1 += inner_sum, + for stream in &self.evaluation_streams { + // Use match to accumulate the appropriate value based on whether the bit is set or not + let inner_sum = stream.evaluation(evaluation_index) * lag_poly; + match is_set { + false => sum_0 += inner_sum, + true => sum_1 += inner_sum, + } } } } diff --git a/src/multilinear/provers/space/prover.rs b/src/multilinear/provers/space/prover.rs index 8a2d850f..e1da9a15 100644 --- a/src/multilinear/provers/space/prover.rs +++ b/src/multilinear/provers/space/prover.rs @@ -18,7 +18,7 @@ impl> Prover for SpaceProver { fn new(prover_config: Self::ProverConfig) -> Self { Self { claim: prover_config.claim, - evaluation_stream: prover_config.stream, + evaluation_streams: prover_config.streams, verifier_messages: Vec::::with_capacity(prover_config.num_variables), verifier_message_hats: Vec::::with_capacity(prover_config.num_variables), current_round: 0, diff --git a/src/prover/core.rs b/src/prover/core.rs index 60239373..48532dcb 100644 --- a/src/prover/core.rs +++ b/src/prover/core.rs @@ -5,6 +5,10 @@ pub trait ProverConfig> { fn default(claim: F, num_variables: usize, stream: S) -> Self; } +pub trait BatchProverConfig> { + fn default(claim: F, num_variables: usize, streams: Vec) -> Self; +} + pub trait ProductProverConfig> { fn default(claim: F, num_variables: usize, steams: Vec) -> Self; } diff --git a/src/prover/mod.rs b/src/prover/mod.rs index 53f91439..4cf9982a 100644 --- a/src/prover/mod.rs +++ b/src/prover/mod.rs @@ -1,2 +1,2 @@ mod core; -pub use core::{ProductProverConfig, Prover, ProverConfig}; +pub use core::{BatchProverConfig, ProductProverConfig, Prover, ProverConfig}; From 40da3e4d7ac168ee59ca7486a0284b192158d082 Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Tue, 28 Oct 2025 16:42:23 +0100 Subject: [PATCH 05/11] refactor vsbw --- src/multilinear/provers/time/config.rs | 21 ++- src/multilinear/provers/time/core.rs | 147 +----------------- src/multilinear/provers/time/mod.rs | 1 + src/multilinear/provers/time/prover.rs | 34 +++- .../provers/time/reductions/mod.rs | 5 + .../provers/time/reductions/pairwise.rs | 87 +++++++++++ .../provers/time/reductions/variable.rs | 0 7 files changed, 137 insertions(+), 158 deletions(-) create mode 100644 src/multilinear/provers/time/reductions/mod.rs create mode 100644 src/multilinear/provers/time/reductions/pairwise.rs create mode 100644 src/multilinear/provers/time/reductions/variable.rs diff --git a/src/multilinear/provers/time/config.rs b/src/multilinear/provers/time/config.rs index b83ea4f3..19021121 100644 --- a/src/multilinear/provers/time/config.rs +++ b/src/multilinear/provers/time/config.rs @@ -1,6 +1,9 @@ use ark_ff::Field; -use crate::{prover::ProverConfig, streams::Stream}; +use crate::{ + prover::{BatchProverConfig, ProverConfig}, + streams::Stream, +}; pub struct TimeProverConfig where @@ -9,7 +12,7 @@ where { pub num_variables: usize, pub claim: F, - pub stream: S, + pub streams: Vec, } impl TimeProverConfig @@ -21,7 +24,7 @@ where Self { claim, num_variables, - stream, + streams: vec![stream], } } } @@ -31,7 +34,17 @@ impl> ProverConfig for TimeProverConfig { Self { claim, num_variables, - stream, + streams: vec![stream], + } + } +} + +impl> BatchProverConfig for TimeProverConfig { + fn default(claim: F, num_variables: usize, streams: Vec) -> Self { + Self { + claim, + num_variables, + streams, } } } diff --git a/src/multilinear/provers/time/core.rs b/src/multilinear/provers/time/core.rs index 7a5b8a27..146d0e17 100644 --- a/src/multilinear/provers/time/core.rs +++ b/src/multilinear/provers/time/core.rs @@ -1,162 +1,17 @@ use ark_ff::Field; use ark_std::vec::Vec; -#[cfg(feature = "parallel")] -use ark_std::cfg_into_iter; -#[cfg(feature = "parallel")] -use rayon::iter::{ - IndexedParallelIterator, IntoParallelIterator, IntoParallelRefMutIterator, ParallelIterator, -}; - use crate::streams::Stream; pub struct TimeProver> { pub claim: F, pub current_round: usize, pub evaluations: Option>, - pub evaluation_stream: S, // TODO (z-tech): this can be released after the first call to vsbw_reduce_evaluations + pub evaluation_streams: Vec, // TODO (z-tech): this can be released after the first call to vsbw_reduce_evaluations pub num_variables: usize, } impl> TimeProver { - pub fn vsbw_evaluate(&self) -> (F, F) { - // Determine the length of evaluations to iterate through - let evaluations_len = match &self.evaluations { - Some(evaluations) => evaluations.len(), - None => 2usize.pow(self.evaluation_stream.num_variables() as u32), - }; - - #[cfg(feature = "parallel")] - let (sum_0, sum_1) = cfg_into_iter!(0..evaluations_len) - .step_by(2) - .map(|i0| { - let i1 = i0 + 1; - - // Get evaluations for this pair - let v0 = if let Some(evals) = &self.evaluations { - evals[i0] - } else { - self.evaluation_stream.evaluation(i0) - }; - - let v1 = if let Some(evals) = &self.evaluations { - evals[i1] - } else { - self.evaluation_stream.evaluation(i1) - }; - - // Return as (sum0 contribution, sum1 contribution) - (v0, v1) - }) - .reduce( - || (F::zero(), F::zero()), - |(a0, a1), (b0, b1)| (a0 + b0, a1 + b1), - ); - - // Initialize accumulators for sum_0 and sum_1 - #[cfg(not(feature = "parallel"))] - let mut sum_0 = F::ZERO; - #[cfg(not(feature = "parallel"))] - let mut sum_1 = F::ZERO; - #[cfg(not(feature = "parallel"))] - { - // Iterate through evaluations - for i in 0..evaluations_len { - // Check if the bit at the position specified by the bitmask is set - let is_set: bool = (i & bitmask) != 0; - - // Get the point evaluation for the current index - let point_evaluation = match &self.evaluations { - Some(evaluations) => evaluations[i], - None => self.evaluation_stream.evaluation(i), - }; - - // Accumulate the value based on whether the bit is set or not - match is_set { - false => sum_0 += point_evaluation, - true => sum_1 += point_evaluation, - } - } - } - - // Return the accumulated sums - (sum_0, sum_1) - } - - pub fn vsbw_reduce_evaluations(&mut self, verifier_message: F, verifier_message_hat: F) { - // Clone or initialize the evaluations vector - #[cfg(feature = "parallel")] - let is_first_go = self.evaluations.is_some(); - let mut evaluations = match &self.evaluations { - Some(evaluations) => evaluations.clone(), - None => vec![ - F::ZERO; - 2usize.pow(self.evaluation_stream.num_variables().try_into().unwrap()) / 2 - ], - }; - - // Determine the length of evaluations to iterate through - let evaluations_len = match &self.evaluations { - Some(evaluations) => evaluations.len() / 2, - None => evaluations.len(), - }; - - #[cfg(feature = "parallel")] - { - // We'll write to the first half only. - let dest = &mut evaluations[..evaluations_len]; - - if is_first_go { - // Read from the old immutable source (borrow, no extra clone). - let src = self.evaluations.as_ref().unwrap(); - dest.par_iter_mut() - .enumerate() - .for_each(|(i0, slot): (usize, &mut F)| { - let i1 = (i0 * 2) + 1; - let v0 = src[i0 * 2]; - let v1 = src[i1]; - *slot = v0 * verifier_message_hat + v1 * verifier_message; - }); - } else { - // Stream-only: compute both endpoints from the stream. - let stream = &self.evaluation_stream; - dest.par_iter_mut() - .enumerate() - .for_each(|(i0, slot): (usize, &mut F)| { - let i1 = (i0 * 2) + 1; - let v0 = stream.evaluation(i0 * 2); - let v1 = stream.evaluation(i1); - *slot = v0 * verifier_message_hat + v1 * verifier_message; - }); - } - } - - // Iterate through pairs of evaluations - #[cfg(not(feature = "parallel"))] - for i0 in 0..evaluations_len { - let i1 = i0 | setbit; - - // Get point evaluations for indices i0 and i1 - let point_evaluation_i0 = match &self.evaluations { - None => self.evaluation_stream.evaluation(i0), - Some(evaluations) => evaluations[i0], - }; - let point_evaluation_i1 = match &self.evaluations { - None => self.evaluation_stream.evaluation(i1), - Some(evaluations) => evaluations[i1], - }; - - // Update the i0-th evaluation based on the reduction operation - evaluations[i0] = - point_evaluation_i0 * verifier_message_hat + point_evaluation_i1 * verifier_message; - } - - // Truncate the evaluations vector to the correct length - evaluations.truncate(evaluations_len); - - // Update the internal state with the new evaluations vector - self.evaluations = Some(evaluations.clone()); - } pub fn total_rounds(&self) -> usize { self.num_variables } diff --git a/src/multilinear/provers/time/mod.rs b/src/multilinear/provers/time/mod.rs index 3af1cebc..826de6a1 100644 --- a/src/multilinear/provers/time/mod.rs +++ b/src/multilinear/provers/time/mod.rs @@ -1,6 +1,7 @@ mod config; mod core; mod prover; +mod reductions; pub use config::TimeProverConfig; pub use core::TimeProver; diff --git a/src/multilinear/provers/time/prover.rs b/src/multilinear/provers/time/prover.rs index 0a486b6c..414ef649 100644 --- a/src/multilinear/provers/time/prover.rs +++ b/src/multilinear/provers/time/prover.rs @@ -1,7 +1,12 @@ use ark_ff::Field; use crate::{ - multilinear::{TimeProver, TimeProverConfig}, + multilinear::{ + provers::time::reductions::{ + evaluate, evaluate_from_stream, reduce_evaluations, reduce_evaluations_from_stream, + }, + TimeProver, TimeProverConfig, + }, prover::Prover, streams::Stream, }; @@ -20,7 +25,7 @@ impl> Prover for TimeProver { claim: prover_config.claim, current_round: 0, evaluations: None, - evaluation_stream: prover_config.stream, + evaluation_streams: prover_config.streams, num_variables: prover_config.num_variables, } } @@ -33,15 +38,28 @@ impl> Prover for TimeProver { // If it's not the first round, reduce the evaluations table if self.current_round != 0 { - // update the evaluations table by absorbing leftmost variable assigned to verifier_message - self.vsbw_reduce_evaluations( - verifier_message.unwrap(), - F::ONE - verifier_message.unwrap(), - ) + if self.current_round > 1 { + reduce_evaluations( + self.evaluations.as_mut().unwrap(), + verifier_message.unwrap(), + F::ONE - verifier_message.unwrap(), + ); + } else { + self.evaluations = Some(vec![]); + reduce_evaluations_from_stream( + &self.evaluation_streams[0], + self.evaluations.as_mut().unwrap(), + verifier_message.unwrap(), + F::ONE - verifier_message.unwrap(), + ); + } } // evaluate using vsbw - let sums = self.vsbw_evaluate(); + let sums = match &self.evaluations { + None => evaluate_from_stream(&self.evaluation_streams[0]), + Some(evaluations) => evaluate(evaluations), + }; // Increment the round counter self.current_round += 1; diff --git a/src/multilinear/provers/time/reductions/mod.rs b/src/multilinear/provers/time/reductions/mod.rs new file mode 100644 index 00000000..f2212cd5 --- /dev/null +++ b/src/multilinear/provers/time/reductions/mod.rs @@ -0,0 +1,5 @@ +mod pairwise; + +pub use pairwise::{ + evaluate, evaluate_from_stream, reduce_evaluations, reduce_evaluations_from_stream, +}; diff --git a/src/multilinear/provers/time/reductions/pairwise.rs b/src/multilinear/provers/time/reductions/pairwise.rs new file mode 100644 index 00000000..94b1fed4 --- /dev/null +++ b/src/multilinear/provers/time/reductions/pairwise.rs @@ -0,0 +1,87 @@ +use ark_ff::Field; +use ark_std::vec::Vec; +use ark_std::{cfg_chunks, cfg_into_iter, cfg_iter}; +use rayon::{ + iter::{ + IndexedParallelIterator, IntoParallelIterator, IntoParallelRefIterator, ParallelIterator, + }, + prelude::ParallelSlice, +}; + +use crate::streams::Stream; + +pub fn evaluate(src: &Vec) -> (F, F) { + // Sum even indices + let even_sum = cfg_iter!(src) + .enumerate() + .filter(|(i, _)| i % 2 == 0) + .map(|(_, val)| *val) + .sum(); + + // Sum odd indices + let odd_sum = cfg_iter!(src) + .enumerate() + .filter(|(i, _)| i % 2 == 1) + .map(|(_, val)| *val) + .sum(); + + (even_sum, odd_sum) +} + +pub fn evaluate_from_stream>(src: &S) -> (F, F) { + let len = 1usize << src.num_variables(); + + cfg_into_iter!(0..len) + .map(|i| (i, src.evaluation(i))) + .fold( + || (F::zero(), F::zero()), + |(mut even, mut odd), (i, val)| { + if i % 2 == 0 { + even += val; + } else { + odd += val; + } + (even, odd) + }, + ) + .reduce( + || (F::zero(), F::zero()), + |(e1, o1), (e2, o2)| (e1 + e2, o1 + o2), + ) +} + +pub fn reduce_evaluations( + src: &mut Vec, + verifier_message: F, + verifier_message_hat: F, +) { + // compute from src + let out: Vec = cfg_chunks!(src, 2) + .map(|chunk| chunk[0] * verifier_message_hat + chunk[1] * verifier_message) + .collect(); + // write back into src + src[..out.len()].copy_from_slice(&out); + src.truncate(out.len()); +} + +pub fn reduce_evaluations_from_stream>( + stream: &S, + dst: &mut Vec, + verifier_message: F, + verifier_message_hat: F, +) { + // compute from stream + let len = 2_i32.pow(stream.num_variables() as u32) as usize; + let out: Vec = cfg_into_iter!(0..len / 2) + .map(|i| { + let a = stream.evaluation(2 * i); + let b = stream.evaluation((2 * i) + 1); + a * verifier_message_hat + b * verifier_message + }) + .collect(); + // write back into dst + if dst.len() < out.len() { + dst.resize(out.len(), F::zero()); + } + dst[..out.len()].copy_from_slice(&out); +} diff --git a/src/multilinear/provers/time/reductions/variable.rs b/src/multilinear/provers/time/reductions/variable.rs new file mode 100644 index 00000000..e69de29b From e61058b34fa023743dfbcde465b39b5289f2b03e Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Tue, 28 Oct 2025 16:50:11 +0100 Subject: [PATCH 06/11] chkpt --- src/multilinear/provers/time/prover.rs | 48 +++++++++++++++++--------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/src/multilinear/provers/time/prover.rs b/src/multilinear/provers/time/prover.rs index 414ef649..de5e4958 100644 --- a/src/multilinear/provers/time/prover.rs +++ b/src/multilinear/provers/time/prover.rs @@ -1,4 +1,7 @@ use ark_ff::Field; +use ark_std::cfg_into_iter; +use rayon::iter::IntoParallelIterator; +use rayon::iter::ParallelIterator; use crate::{ multilinear::{ @@ -11,6 +14,17 @@ use crate::{ streams::Stream, }; +fn combine_streams>(streams: &[S]) -> Vec { + let len = 1usize << streams[0].num_variables(); + cfg_into_iter!(0..len) + .map(|i| { + streams.iter() + .map(|s| s.evaluation(i)) + .fold(F::zero(), |acc, val| acc + val) + }) + .collect() +} + impl> Prover for TimeProver { type ProverConfig = TimeProverConfig; type ProverMessage = Option<(F, F)>; @@ -36,23 +50,23 @@ impl> Prover for TimeProver { return None; } - // If it's not the first round, reduce the evaluations table - if self.current_round != 0 { - if self.current_round > 1 { - reduce_evaluations( - self.evaluations.as_mut().unwrap(), - verifier_message.unwrap(), - F::ONE - verifier_message.unwrap(), - ); - } else { - self.evaluations = Some(vec![]); - reduce_evaluations_from_stream( - &self.evaluation_streams[0], - self.evaluations.as_mut().unwrap(), - verifier_message.unwrap(), - F::ONE - verifier_message.unwrap(), - ); - } + // TODO + if self.current_round == 0 { + self.evaluations = Some(combine_streams(&self.evaluation_streams)); + } else if self.current_round > 1 || self.evaluation_streams.len() > 1 { + reduce_evaluations( + self.evaluations.as_mut().unwrap(), + verifier_message.unwrap(), + F::ONE - verifier_message.unwrap(), + ); + } else { + self.evaluations = Some(vec![]); + reduce_evaluations_from_stream( + &self.evaluation_streams[0], + self.evaluations.as_mut().unwrap(), + verifier_message.unwrap(), + F::ONE - verifier_message.unwrap(), + ); } // evaluate using vsbw From e7529cb5a6ee63828eb6e645d9a7e68e18a25aab Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Tue, 4 Nov 2025 17:04:23 +0100 Subject: [PATCH 07/11] chkpt --- Cargo.toml | 10 +- benches/extension_benches.rs | 233 ++++++++++++++++++ src/multilinear/mod.rs | 2 +- src/multilinear/provers/time/config.rs | 7 +- src/multilinear/provers/time/core.rs | 2 + src/multilinear/provers/time/mod.rs | 1 + src/multilinear/provers/time/prover.rs | 136 ++++++---- .../provers/time/reductions/mod.rs | 11 +- .../provers/time/reductions/variable.rs | 0 .../provers/time/reductions/variablewise.rs | 93 +++++++ src/multilinear/sumcheck.rs | 60 +++-- src/tests/fields.rs | 29 +++ src/tests/mod.rs | 2 +- 13 files changed, 506 insertions(+), 80 deletions(-) create mode 100644 benches/extension_benches.rs delete mode 100644 src/multilinear/provers/time/reductions/variable.rs create mode 100644 src/multilinear/provers/time/reductions/variablewise.rs diff --git a/Cargo.toml b/Cargo.toml index ac147d72..aace4d94 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,9 +8,9 @@ include = ["Cargo.toml", "src", "README.md", "LICENSE-APACHE", "LICENSE-MIT"] edition = "2021" [dependencies] -ark-ff = "0.5.0" -ark-poly = "0.5.0" -ark-serialize = "0.5.0" +ark-ff = { path = "../ben/algebra/ff" } +ark-serialize = { path = "../ben/algebra/serialize" } +ark-poly = { path = "../ben/algebra/poly" } ark-std ="0.5.0" memmap2 = "0.9.5" rayon = { version = "1.10", optional = true } @@ -31,3 +31,7 @@ parallel = [ name = "provers" path = "benches/provers.rs" harness = false + +[[bench]] +name = "extension_benches" +harness = false \ No newline at end of file diff --git a/benches/extension_benches.rs b/benches/extension_benches.rs new file mode 100644 index 00000000..7d1e2585 --- /dev/null +++ b/benches/extension_benches.rs @@ -0,0 +1,233 @@ +use ark_ff::{BigInt, Fp2, Fp2Config, Fp4, Fp4Config, UniformRand}; +use ark_std::test_rng; +use criterion::{criterion_group, criterion_main, Criterion}; +use efficient_sumcheck::tests::{ + F128, + F64 as Goldilocks, + M31, // SmallF128Mont, SmallF32Mont, SmallF64Mont, +}; + +// #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] +// pub struct Fp2SmallGoldilocksConfig; + +// impl Fp2Config for Fp2SmallGoldilocksConfig { +// type Fp = SmallF64Mont; + +// // const context: use new_unchecked(BigInt) +// const NONRESIDUE: SmallF64Mont = SmallF64Mont::new(3); + +// // Arkworks 0.5 expects &'static [Fp] +// const FROBENIUS_COEFF_FP2_C1: &'static [SmallF64Mont] = +// &[SmallF64Mont::new(1), SmallF64Mont::new(3)]; +// } + +// pub type Fp2SmallGoldilocks = Fp2; + +// #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] +// pub struct Fp2SmallM31Config; + +// impl Fp2Config for Fp2SmallM31Config { +// type Fp = SmallF32Mont; + +// // Use const_new to build compile-time constants +// const NONRESIDUE: SmallF32Mont = SmallF32Mont::new(1); + +// // These Frobenius coeffs aren't used for arithmetic benchmarks anyway +// const FROBENIUS_COEFF_FP2_C1: &'static [SmallF32Mont] = &[ +// SmallF32Mont::new(1), +// SmallF32Mont::new(3), +// ]; +// } + +// pub type Fp2SmallM31 = Fp2; + +// #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] +// pub struct Fp4SmallM31Config; + +// impl Fp4Config for Fp4SmallM31Config { +// type Fp2Config = Fp2SmallM31Config; + +// const NONRESIDUE: Fp2 = Fp2::::new( +// SmallF32Mont::new(3), +// SmallF32Mont::new(0), +// ); + +// // 👇 now a slice of base‐field elements, not Fp2 elements +// const FROBENIUS_COEFF_FP4_C1: &'static [SmallF32Mont] = &[ +// SmallF32Mont::new(1), +// SmallF32Mont::new(3), +// SmallF32Mont::new(9), +// SmallF32Mont::new(27), +// ]; +// } + +// pub type Fp4SmallM31 = Fp4; + +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] +pub struct Fp2GoldilocksConfig; + +impl Fp2Config for Fp2GoldilocksConfig { + type Fp = Goldilocks; + + // const context: use new_unchecked(BigInt) + const NONRESIDUE: Goldilocks = Goldilocks::new_unchecked(BigInt::<1>([3u64])); + + // Arkworks 0.5 expects &'static [Fp] + const FROBENIUS_COEFF_FP2_C1: &'static [Goldilocks] = &[ + Goldilocks::new_unchecked(BigInt::<1>([1u64])), + Goldilocks::new_unchecked(BigInt::<1>([3u64])), + ]; +} + +pub type Fp2Goldilocks = Fp2; + +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] +pub struct Fp2M31Config; + +impl Fp2Config for Fp2M31Config { + type Fp = M31; + + // Use const_new to build compile-time constants + const NONRESIDUE: M31 = M31::new_unchecked(BigInt::<1>([3u64])); + + // These Frobenius coeffs aren't used for arithmetic benchmarks anyway + const FROBENIUS_COEFF_FP2_C1: &'static [M31] = &[ + M31::new_unchecked(BigInt::<1>([1u64])), + M31::new_unchecked(BigInt::<1>([3u64])), + ]; +} + +pub type Fp2M31 = Fp2; + +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] +pub struct Fp4M31Config; + +impl Fp4Config for Fp4M31Config { + type Fp2Config = Fp2M31Config; + + const NONRESIDUE: Fp2 = Fp2::::new( + M31::new_unchecked(BigInt::<1>([3u64])), + M31::new_unchecked(BigInt::<1>([0u64])), + ); + + // 👇 now a slice of base‐field elements, not Fp2 elements + const FROBENIUS_COEFF_FP4_C1: &'static [M31] = &[ + M31::new_unchecked(BigInt::<1>([1u64])), + M31::new_unchecked(BigInt::<1>([3u64])), + M31::new_unchecked(BigInt::<1>([9u64])), + M31::new_unchecked(BigInt::<1>([27u64])), + ]; +} + +pub type Fp4M31 = Fp4; + +pub fn bench_fp4_mul(c: &mut Criterion) { + let mut rng = test_rng(); + let a = Fp4M31::rand(&mut rng); + let b = Fp4M31::rand(&mut rng); + + c.bench_function("Fp4 multiply", |bench| { + bench.iter(|| { + let mut acc = a; + // Chain 1000 multiplications to smooth noise + for _ in 0..1000 { + acc *= b; + } + acc + }); + }); +} + +pub fn bench_f128_mul(c: &mut Criterion) { + let mut rng = test_rng(); + let a = F128::rand(&mut rng); + let b = F128::rand(&mut rng); + + c.bench_function("F128 multiply", |bench| { + bench.iter(|| { + let mut acc = a; + for _ in 0..1000 { + acc *= b; + } + acc + }); + }); +} + +pub fn bench_fp2_goldilocks_mul(c: &mut Criterion) { + let mut rng = test_rng(); + let a = Fp2Goldilocks::rand(&mut rng); + let b = Fp2Goldilocks::rand(&mut rng); + + c.bench_function("Fp2 multiply", |bench| { + bench.iter(|| { + let mut acc = a; + for _ in 0..1000 { + acc *= b; + } + acc + }); + }); +} + +// smalls + +// pub fn bench_small_fp4_mul(c: &mut Criterion) { +// let mut rng = test_rng(); +// let a = Fp4SmallM31::rand(&mut rng); +// let b = Fp4SmallM31::rand(&mut rng); + +// c.bench_function("Fp4 multiply", |bench| { +// bench.iter(|| { +// let mut acc = a; +// // Chain 1000 multiplications to smooth noise +// for _ in 0..1000 { +// acc *= b; +// } +// acc +// }); +// }); +// } + +// pub fn bench_small_f128_mul(c: &mut Criterion) { +// let mut rng = test_rng(); +// let a = SmallF128Mont::rand(&mut rng); +// let b = SmallF128Mont::rand(&mut rng); + +// c.bench_function("Small F128 multiply", |bench| { +// bench.iter(|| { +// let mut acc = a; +// for _ in 0..1000 { +// acc *= b; +// } +// acc +// }); +// }); +// } + +// pub fn bench_small_fp2_goldilocks_mul(c: &mut Criterion) { +// let mut rng = test_rng(); +// let a = Fp2SmallGoldilocks::rand(&mut rng); +// let b = Fp2SmallGoldilocks::rand(&mut rng); + +// c.bench_function("Fp2 multiply", |bench| { +// bench.iter(|| { +// let mut acc = a; +// for _ in 0..1000 { +// acc *= b; +// } +// acc +// }); +// }); +// } + +criterion_group!( + benches, + // bench_small_f128_mul, + // bench_small_fp4_mul, + // bench_small_fp2_goldilocks_mul, + bench_f128_mul, + bench_fp4_mul, + bench_fp2_goldilocks_mul, +); +criterion_main!(benches); diff --git a/src/multilinear/mod.rs b/src/multilinear/mod.rs index 48e4ed0f..3e97e7ed 100644 --- a/src/multilinear/mod.rs +++ b/src/multilinear/mod.rs @@ -4,6 +4,6 @@ mod sumcheck; pub use provers::{ blendy::{BlendyProver, BlendyProverConfig}, space::{SpaceProver, SpaceProverConfig}, - time::{TimeProver, TimeProverConfig}, + time::{TimeProver, TimeProverConfig, ReduceMode}, }; pub use sumcheck::Sumcheck; diff --git a/src/multilinear/provers/time/config.rs b/src/multilinear/provers/time/config.rs index 19021121..e27066e9 100644 --- a/src/multilinear/provers/time/config.rs +++ b/src/multilinear/provers/time/config.rs @@ -1,6 +1,7 @@ use ark_ff::Field; use crate::{ + multilinear::provers::time::reductions::ReduceMode, prover::{BatchProverConfig, ProverConfig}, streams::Stream, }; @@ -13,6 +14,7 @@ where pub num_variables: usize, pub claim: F, pub streams: Vec, + pub reduce_mode: ReduceMode, } impl TimeProverConfig @@ -20,11 +22,12 @@ where F: Field, S: Stream, { - pub fn new(claim: F, num_variables: usize, stream: S) -> Self { + pub fn new(claim: F, num_variables: usize, stream: S, reduce_mode: ReduceMode) -> Self { Self { claim, num_variables, streams: vec![stream], + reduce_mode, } } } @@ -35,6 +38,7 @@ impl> ProverConfig for TimeProverConfig { claim, num_variables, streams: vec![stream], + reduce_mode: ReduceMode::Pairwise, } } } @@ -45,6 +49,7 @@ impl> BatchProverConfig for TimeProverConfig claim, num_variables, streams, + reduce_mode: ReduceMode::Pairwise, } } } diff --git a/src/multilinear/provers/time/core.rs b/src/multilinear/provers/time/core.rs index 146d0e17..2c9a361e 100644 --- a/src/multilinear/provers/time/core.rs +++ b/src/multilinear/provers/time/core.rs @@ -1,3 +1,4 @@ +use crate::multilinear::provers::time::reductions::ReduceMode; use ark_ff::Field; use ark_std::vec::Vec; @@ -9,6 +10,7 @@ pub struct TimeProver> { pub evaluations: Option>, pub evaluation_streams: Vec, // TODO (z-tech): this can be released after the first call to vsbw_reduce_evaluations pub num_variables: usize, + pub reduce_mode: ReduceMode, } impl> TimeProver { diff --git a/src/multilinear/provers/time/mod.rs b/src/multilinear/provers/time/mod.rs index 826de6a1..d2132c39 100644 --- a/src/multilinear/provers/time/mod.rs +++ b/src/multilinear/provers/time/mod.rs @@ -3,5 +3,6 @@ mod core; mod prover; mod reductions; +pub use reductions::ReduceMode; pub use config::TimeProverConfig; pub use core::TimeProver; diff --git a/src/multilinear/provers/time/prover.rs b/src/multilinear/provers/time/prover.rs index de5e4958..18d0f682 100644 --- a/src/multilinear/provers/time/prover.rs +++ b/src/multilinear/provers/time/prover.rs @@ -1,28 +1,96 @@ use ark_ff::Field; -use ark_std::cfg_into_iter; -use rayon::iter::IntoParallelIterator; -use rayon::iter::ParallelIterator; +use crate::multilinear::provers::time::reductions::ReduceMode; use crate::{ multilinear::{ - provers::time::reductions::{ - evaluate, evaluate_from_stream, reduce_evaluations, reduce_evaluations_from_stream, - }, + provers::time::reductions::{pairwise, variablewise}, TimeProver, TimeProverConfig, }, prover::Prover, streams::Stream, }; -fn combine_streams>(streams: &[S]) -> Vec { - let len = 1usize << streams[0].num_variables(); - cfg_into_iter!(0..len) - .map(|i| { - streams.iter() - .map(|s| s.evaluation(i)) - .fold(F::zero(), |acc, val| acc + val) - }) - .collect() +impl> TimeProver { + fn num_free_variables(&self) -> usize { + self.num_variables - self.current_round + } + fn next_message_pairwise(&mut self, verifier_message: Option) -> Option<(F, F)> { + // Ensure the current round is within bounds + if self.current_round >= self.total_rounds() { + return None; + } + + if self.current_round != 0 { + if self.current_round > 1 { + pairwise::reduce_evaluations( + self.evaluations.as_mut().unwrap(), + verifier_message.unwrap(), + F::ONE - verifier_message.unwrap(), + ); + } else { + self.evaluations = Some(vec![]); + pairwise::reduce_evaluations_from_stream( + &self.evaluation_streams[0], + self.evaluations.as_mut().unwrap(), + verifier_message.unwrap(), + F::ONE - verifier_message.unwrap(), + ); + } + } + + // evaluate using vsbw + let sums = match &self.evaluations { + None => pairwise::evaluate_from_stream(&self.evaluation_streams[0]), + Some(evaluations) => pairwise::evaluate(evaluations), + }; + + // Increment the round counter + self.current_round += 1; + + // Return the computed polynomial + Some(sums) + } + fn next_message_variablewise(&mut self, verifier_message: Option) -> Option<(F, F)> { + // Ensure the current round is within bounds + if self.current_round >= self.total_rounds() { + return None; + } + let num_free_variables = self.num_free_variables(); + + if self.current_round != 0 { + if self.current_round > 1 { + variablewise::reduce_evaluations( + self.evaluations.as_mut().unwrap(), + num_free_variables, + verifier_message.unwrap(), + F::ONE - verifier_message.unwrap(), + ); + } else { + self.evaluations = Some(vec![]); + variablewise::reduce_evaluations_from_stream( + &self.evaluation_streams[0], + self.evaluations.as_mut().unwrap(), + num_free_variables, + verifier_message.unwrap(), + F::ONE - verifier_message.unwrap(), + ); + } + } + + // evaluate using vsbw + let sums = match &self.evaluations { + None => { + variablewise::evaluate_from_stream(&self.evaluation_streams[0], num_free_variables) + } + Some(evaluations) => variablewise::evaluate(evaluations, num_free_variables), + }; + + // Increment the round counter + self.current_round += 1; + + // Return the computed polynomial + Some(sums) + } } impl> Prover for TimeProver { @@ -41,45 +109,15 @@ impl> Prover for TimeProver { evaluations: None, evaluation_streams: prover_config.streams, num_variables: prover_config.num_variables, + reduce_mode: prover_config.reduce_mode, } } fn next_message(&mut self, verifier_message: Option) -> Option<(F, F)> { - // Ensure the current round is within bounds - if self.current_round >= self.total_rounds() { - return None; + match self.reduce_mode { + ReduceMode::Pairwise => self.next_message_pairwise(verifier_message), + ReduceMode::Variablewise => self.next_message_variablewise(verifier_message), } - - // TODO - if self.current_round == 0 { - self.evaluations = Some(combine_streams(&self.evaluation_streams)); - } else if self.current_round > 1 || self.evaluation_streams.len() > 1 { - reduce_evaluations( - self.evaluations.as_mut().unwrap(), - verifier_message.unwrap(), - F::ONE - verifier_message.unwrap(), - ); - } else { - self.evaluations = Some(vec![]); - reduce_evaluations_from_stream( - &self.evaluation_streams[0], - self.evaluations.as_mut().unwrap(), - verifier_message.unwrap(), - F::ONE - verifier_message.unwrap(), - ); - } - - // evaluate using vsbw - let sums = match &self.evaluations { - None => evaluate_from_stream(&self.evaluation_streams[0]), - Some(evaluations) => evaluate(evaluations), - }; - - // Increment the round counter - self.current_round += 1; - - // Return the computed polynomial - Some(sums) } } diff --git a/src/multilinear/provers/time/reductions/mod.rs b/src/multilinear/provers/time/reductions/mod.rs index f2212cd5..e59f35f2 100644 --- a/src/multilinear/provers/time/reductions/mod.rs +++ b/src/multilinear/provers/time/reductions/mod.rs @@ -1,5 +1,8 @@ -mod pairwise; +pub mod pairwise; +pub mod variablewise; -pub use pairwise::{ - evaluate, evaluate_from_stream, reduce_evaluations, reduce_evaluations_from_stream, -}; +#[derive(Copy, Clone, Debug)] +pub enum ReduceMode { + Pairwise, + Variablewise, +} diff --git a/src/multilinear/provers/time/reductions/variable.rs b/src/multilinear/provers/time/reductions/variable.rs deleted file mode 100644 index e69de29b..00000000 diff --git a/src/multilinear/provers/time/reductions/variablewise.rs b/src/multilinear/provers/time/reductions/variablewise.rs new file mode 100644 index 00000000..b26927e5 --- /dev/null +++ b/src/multilinear/provers/time/reductions/variablewise.rs @@ -0,0 +1,93 @@ +use ark_ff::Field; +use ark_std::vec::Vec; +use ark_std::{cfg_into_iter, cfg_iter_mut}; +use rayon::iter::IntoParallelRefMutIterator; +use rayon::{ + iter::{ + IndexedParallelIterator, IntoParallelIterator, ParallelIterator, + }, +}; + +use crate::streams::Stream; + +pub fn evaluate(src: &[F], num_free_variables: usize) -> (F, F) { + let bitmask: usize = 1 << (num_free_variables - 1); + let (sum_0, sum_1) = cfg_into_iter!(0..src.len()) + .map(|i| { + let val = src[i]; + // Route value into the proper bucket + if (i & bitmask) == 0 { + (val, F::zero()) // contributes to sum_0 + } else { + (F::zero(), val) // contributes to sum_1 + } + }) + // Combine partial (sum0, sum1) pairs from each worker/thread. + .reduce( + || (F::zero(), F::zero()), + |(a0, a1), (b0, b1)| (a0 + b0, a1 + b1), + ); + (sum_0, sum_1) +} + +pub fn evaluate_from_stream>(src: &S, num_free_variables: usize) -> (F, F) { + let len = 1usize << src.num_variables(); + let bitmask: usize = 1 << (num_free_variables - 1); + let (sum_0, sum_1) = cfg_into_iter!(0..len) + .map(|i| { + let val = src.evaluation(i); + // Route value into the proper bucket + if (i & bitmask) == 0 { + (val, F::zero()) // contributes to sum_0 + } else { + (F::zero(), val) // contributes to sum_1 + } + }) + // Combine partial (sum0, sum1) pairs from each worker/thread. + .reduce( + || (F::zero(), F::zero()), + |(a0, a1), (b0, b1)| (a0 + b0, a1 + b1), + ); + (sum_0, sum_1) +} + +pub fn reduce_evaluations( + src: &mut Vec, + num_free_variables: usize, + verifier_message: F, + verifier_message_hat: F, +) { + let setbit: usize = 1 << num_free_variables; + let mut dest = vec![F::zero(); src.len() / 2]; + cfg_iter_mut!(dest).enumerate().for_each(|(i0, slot)| { + let i1 = i0 | setbit; + let v0 = src[i0]; + let v1 = src[i1]; + *slot = v0 * verifier_message_hat + v1 * verifier_message; + }); + *src = dest; +} + +pub fn reduce_evaluations_from_stream>( + src: &S, + dst: &mut Vec, + num_free_variables: usize, + verifier_message: F, + verifier_message_hat: F, +) { + // compute from stream + let len = 2_i32.pow(src.num_variables() as u32) as usize; + let setbit: usize = 1 << num_free_variables; + let mut out = vec![F::zero(); len / 2]; + cfg_iter_mut!(out).enumerate().for_each(|(i0, slot)| { + let i1 = i0 | setbit; + let v0 = src.evaluation(i0); + let v1 = src.evaluation(i1); + *slot = v0 * verifier_message_hat + v1 * verifier_message; + }); + // write back into dst + if dst.len() < out.len() { + dst.resize(out.len(), F::zero()); + } + dst[..out.len()].copy_from_slice(&out); +} diff --git a/src/multilinear/sumcheck.rs b/src/multilinear/sumcheck.rs index cfdc78d5..34923b20 100644 --- a/src/multilinear/sumcheck.rs +++ b/src/multilinear/sumcheck.rs @@ -62,44 +62,62 @@ impl Sumcheck { mod tests { use super::Sumcheck; use crate::{ - multilinear::{BlendyProver, BlendyProverConfig, TimeProver}, + multilinear::{BlendyProver, BlendyProverConfig, ReduceMode, TimeProver}, prover::{Prover, ProverConfig}, tests::{BenchStream, F19}, }; #[test] - fn algorithm_consistency() { + fn sanity() { // take an evaluation stream - let evaluation_stream: BenchStream = BenchStream::new(20); + let evaluation_stream: BenchStream = BenchStream::new(2); let claim = evaluation_stream.claimed_sum; - // initialize the provers + + // blendy let mut blendy_k3_prover = BlendyProver::>::new( BlendyProverConfig::new(claim, 3, 20, evaluation_stream.clone()), ); - let mut time_prover = TimeProver::>::new(::prove::< + BenchStream, + BlendyProver>, + >(&mut blendy_k3_prover, &mut ark_std::test_rng()); + + + // time_prover_variablewise + let mut time_prover_variablewise = TimeProver::>::new(, + > as Prover>::ProverConfig::new( + claim, + 2, + evaluation_stream.clone(), + ReduceMode::Variablewise, + )); + let time_prover_variablewise_transcript = Sumcheck::::prove::< + BenchStream, + TimeProver>, + >(&mut time_prover_variablewise, &mut ark_std::test_rng()); + + // ensure transcripts identical + assert_eq!( + time_prover_variablewise_transcript.prover_messages, + blendy_prover_transcript.prover_messages + ); + + + // time_prover_pairwise: this should pass but I have nothing to compare it with + let mut time_prover_pairwise = TimeProver::>::new(, > as Prover>::ProverConfig::default( claim, - 20, + 2, evaluation_stream, )); - // run them and get the transcript - let blendy_prover_transcript = Sumcheck::::prove::< - BenchStream, - BlendyProver>, - >(&mut blendy_k3_prover, &mut ark_std::test_rng()); - let time_prover_transcript = Sumcheck::::prove::< + let time_prover_pairwise_transcript = Sumcheck::::prove::< BenchStream, TimeProver>, - >(&mut time_prover, &mut ark_std::test_rng()); - // ensure the transcript is identical - // assert_eq!( - // time_prover_transcript.prover_messages, - // blendy_prover_transcript.prover_messages - // ); - // blendy is variable compression time prover pairwise - assert!(blendy_prover_transcript.is_accepted); - assert!(time_prover_transcript.is_accepted); + >(&mut time_prover_pairwise, &mut ark_std::test_rng()); + assert!(time_prover_pairwise_transcript.is_accepted); } } diff --git a/src/tests/fields.rs b/src/tests/fields.rs index 345b54e6..f85675fb 100644 --- a/src/tests/fields.rs +++ b/src/tests/fields.rs @@ -1,4 +1,6 @@ +use ark_ff::ark_ff_macros::SmallFpConfig; use ark_ff::fields::{Fp128, Fp64, MontBackend, MontConfig}; +use ark_ff::{BigInt, SmallFp, SmallFpConfig, SqrtPrecomputation}; #[derive(MontConfig)] #[modulus = "19"] @@ -6,14 +8,41 @@ use ark_ff::fields::{Fp128, Fp64, MontBackend, MontConfig}; pub struct F19Config; pub type F19 = Fp64>; +#[derive(MontConfig)] +#[modulus = "2147483647"] // 2 ^ 31 - 1 +#[generator = "2"] +pub struct M31Config; +pub type M31 = Fp64>; + +#[derive(SmallFpConfig)] +#[modulus = "2147483647"] // m31 +#[generator = "7"] +#[backend = "montgomery"] +pub struct SmallFieldMont; +pub type SmallF32Mont = SmallFp; + #[derive(MontConfig)] #[modulus = "18446744069414584321"] // q = 2^64 - 2^32 + 1 #[generator = "2"] pub struct F64Config; pub type F64 = Fp64>; +#[derive(SmallFpConfig)] +#[modulus = "18446744069414584321"] // Goldilock's prime 2^64 - 2^32 + 1 +#[generator = "7"] +#[backend = "montgomery"] +pub struct SmallF64ConfigMont; +pub type SmallF64Mont = SmallFp; + #[derive(MontConfig)] #[modulus = "143244528689204659050391023439224324689"] // q = 143244528689204659050391023439224324689 #[generator = "2"] pub struct F128Config; pub type F128 = Fp128>; + +#[derive(SmallFpConfig)] +#[modulus = "143244528689204659050391023439224324689"] +#[generator = "3"] +#[backend = "montgomery"] +pub struct SmallF128ConfigMont; +pub type SmallF128Mont = SmallFp; diff --git a/src/tests/mod.rs b/src/tests/mod.rs index a238aae2..edf1119a 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -4,5 +4,5 @@ mod streams; pub mod multilinear; pub mod multilinear_product; pub mod polynomials; -pub use fields::{F128, F19, F64}; +pub use fields::{SmallF128Mont, SmallF32Mont, SmallF64Mont, F128, F19, F64, M31}; pub use streams::BenchStream; From 6863cc604c0cf7a6c7ccdbdee341344fc3401a17 Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Wed, 5 Nov 2025 11:46:42 +0100 Subject: [PATCH 08/11] fix tests --- src/multilinear/mod.rs | 2 +- src/multilinear/provers/time/mod.rs | 2 +- src/multilinear/provers/time/prover.rs | 5 +--- .../provers/time/reductions/variablewise.rs | 6 +--- src/multilinear/sumcheck.rs | 30 ++++++++++--------- 5 files changed, 20 insertions(+), 25 deletions(-) diff --git a/src/multilinear/mod.rs b/src/multilinear/mod.rs index 3e97e7ed..53049925 100644 --- a/src/multilinear/mod.rs +++ b/src/multilinear/mod.rs @@ -4,6 +4,6 @@ mod sumcheck; pub use provers::{ blendy::{BlendyProver, BlendyProverConfig}, space::{SpaceProver, SpaceProverConfig}, - time::{TimeProver, TimeProverConfig, ReduceMode}, + time::{ReduceMode, TimeProver, TimeProverConfig}, }; pub use sumcheck::Sumcheck; diff --git a/src/multilinear/provers/time/mod.rs b/src/multilinear/provers/time/mod.rs index d2132c39..f586f9d5 100644 --- a/src/multilinear/provers/time/mod.rs +++ b/src/multilinear/provers/time/mod.rs @@ -3,6 +3,6 @@ mod core; mod prover; mod reductions; -pub use reductions::ReduceMode; pub use config::TimeProverConfig; pub use core::TimeProver; +pub use reductions::ReduceMode; diff --git a/src/multilinear/provers/time/prover.rs b/src/multilinear/provers/time/prover.rs index 18d0f682..b1981c1e 100644 --- a/src/multilinear/provers/time/prover.rs +++ b/src/multilinear/provers/time/prover.rs @@ -11,9 +11,6 @@ use crate::{ }; impl> TimeProver { - fn num_free_variables(&self) -> usize { - self.num_variables - self.current_round - } fn next_message_pairwise(&mut self, verifier_message: Option) -> Option<(F, F)> { // Ensure the current round is within bounds if self.current_round >= self.total_rounds() { @@ -55,8 +52,8 @@ impl> TimeProver { if self.current_round >= self.total_rounds() { return None; } - let num_free_variables = self.num_free_variables(); + let num_free_variables = self.num_variables - self.current_round; if self.current_round != 0 { if self.current_round > 1 { variablewise::reduce_evaluations( diff --git a/src/multilinear/provers/time/reductions/variablewise.rs b/src/multilinear/provers/time/reductions/variablewise.rs index b26927e5..9ac69ceb 100644 --- a/src/multilinear/provers/time/reductions/variablewise.rs +++ b/src/multilinear/provers/time/reductions/variablewise.rs @@ -2,11 +2,7 @@ use ark_ff::Field; use ark_std::vec::Vec; use ark_std::{cfg_into_iter, cfg_iter_mut}; use rayon::iter::IntoParallelRefMutIterator; -use rayon::{ - iter::{ - IndexedParallelIterator, IntoParallelIterator, ParallelIterator, - }, -}; +use rayon::iter::{IndexedParallelIterator, IntoParallelIterator, ParallelIterator}; use crate::streams::Stream; diff --git a/src/multilinear/sumcheck.rs b/src/multilinear/sumcheck.rs index 34923b20..6aaa896f 100644 --- a/src/multilinear/sumcheck.rs +++ b/src/multilinear/sumcheck.rs @@ -69,34 +69,36 @@ mod tests { #[test] fn sanity() { + const NUM_VARIABLES: usize = 20; + // take an evaluation stream - let evaluation_stream: BenchStream = BenchStream::new(2); + let evaluation_stream: BenchStream = BenchStream::new(NUM_VARIABLES); let claim = evaluation_stream.claimed_sum; // blendy let mut blendy_k3_prover = BlendyProver::>::new( - BlendyProverConfig::new(claim, 3, 20, evaluation_stream.clone()), + BlendyProverConfig::new(claim, 3, NUM_VARIABLES, evaluation_stream.clone()), ); let blendy_prover_transcript = Sumcheck::::prove::< BenchStream, BlendyProver>, >(&mut blendy_k3_prover, &mut ark_std::test_rng()); - // time_prover_variablewise let mut time_prover_variablewise = TimeProver::>::new(, > as Prover>::ProverConfig::new( claim, - 2, + NUM_VARIABLES, evaluation_stream.clone(), ReduceMode::Variablewise, )); - let time_prover_variablewise_transcript = Sumcheck::::prove::< - BenchStream, - TimeProver>, - >(&mut time_prover_variablewise, &mut ark_std::test_rng()); + let time_prover_variablewise_transcript = + Sumcheck::::prove::, TimeProver>>( + &mut time_prover_variablewise, + &mut ark_std::test_rng(), + ); // ensure transcripts identical assert_eq!( @@ -104,20 +106,20 @@ mod tests { blendy_prover_transcript.prover_messages ); - // time_prover_pairwise: this should pass but I have nothing to compare it with let mut time_prover_pairwise = TimeProver::>::new(, > as Prover>::ProverConfig::default( claim, - 2, + NUM_VARIABLES, evaluation_stream, )); - let time_prover_pairwise_transcript = Sumcheck::::prove::< - BenchStream, - TimeProver>, - >(&mut time_prover_pairwise, &mut ark_std::test_rng()); + let time_prover_pairwise_transcript = + Sumcheck::::prove::, TimeProver>>( + &mut time_prover_pairwise, + &mut ark_std::test_rng(), + ); assert!(time_prover_pairwise_transcript.is_accepted); } } From ad361eca492c09bcf56f15c8384c346f6f29042e Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Wed, 5 Nov 2025 11:53:44 +0100 Subject: [PATCH 09/11] remove extension benches --- Cargo.toml | 10 +- benches/extension_benches.rs | 233 ----------------------------------- src/tests/fields.rs | 23 ---- src/tests/mod.rs | 2 +- 4 files changed, 4 insertions(+), 264 deletions(-) delete mode 100644 benches/extension_benches.rs diff --git a/Cargo.toml b/Cargo.toml index aace4d94..ac147d72 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,9 +8,9 @@ include = ["Cargo.toml", "src", "README.md", "LICENSE-APACHE", "LICENSE-MIT"] edition = "2021" [dependencies] -ark-ff = { path = "../ben/algebra/ff" } -ark-serialize = { path = "../ben/algebra/serialize" } -ark-poly = { path = "../ben/algebra/poly" } +ark-ff = "0.5.0" +ark-poly = "0.5.0" +ark-serialize = "0.5.0" ark-std ="0.5.0" memmap2 = "0.9.5" rayon = { version = "1.10", optional = true } @@ -31,7 +31,3 @@ parallel = [ name = "provers" path = "benches/provers.rs" harness = false - -[[bench]] -name = "extension_benches" -harness = false \ No newline at end of file diff --git a/benches/extension_benches.rs b/benches/extension_benches.rs deleted file mode 100644 index 7d1e2585..00000000 --- a/benches/extension_benches.rs +++ /dev/null @@ -1,233 +0,0 @@ -use ark_ff::{BigInt, Fp2, Fp2Config, Fp4, Fp4Config, UniformRand}; -use ark_std::test_rng; -use criterion::{criterion_group, criterion_main, Criterion}; -use efficient_sumcheck::tests::{ - F128, - F64 as Goldilocks, - M31, // SmallF128Mont, SmallF32Mont, SmallF64Mont, -}; - -// #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] -// pub struct Fp2SmallGoldilocksConfig; - -// impl Fp2Config for Fp2SmallGoldilocksConfig { -// type Fp = SmallF64Mont; - -// // const context: use new_unchecked(BigInt) -// const NONRESIDUE: SmallF64Mont = SmallF64Mont::new(3); - -// // Arkworks 0.5 expects &'static [Fp] -// const FROBENIUS_COEFF_FP2_C1: &'static [SmallF64Mont] = -// &[SmallF64Mont::new(1), SmallF64Mont::new(3)]; -// } - -// pub type Fp2SmallGoldilocks = Fp2; - -// #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] -// pub struct Fp2SmallM31Config; - -// impl Fp2Config for Fp2SmallM31Config { -// type Fp = SmallF32Mont; - -// // Use const_new to build compile-time constants -// const NONRESIDUE: SmallF32Mont = SmallF32Mont::new(1); - -// // These Frobenius coeffs aren't used for arithmetic benchmarks anyway -// const FROBENIUS_COEFF_FP2_C1: &'static [SmallF32Mont] = &[ -// SmallF32Mont::new(1), -// SmallF32Mont::new(3), -// ]; -// } - -// pub type Fp2SmallM31 = Fp2; - -// #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] -// pub struct Fp4SmallM31Config; - -// impl Fp4Config for Fp4SmallM31Config { -// type Fp2Config = Fp2SmallM31Config; - -// const NONRESIDUE: Fp2 = Fp2::::new( -// SmallF32Mont::new(3), -// SmallF32Mont::new(0), -// ); - -// // 👇 now a slice of base‐field elements, not Fp2 elements -// const FROBENIUS_COEFF_FP4_C1: &'static [SmallF32Mont] = &[ -// SmallF32Mont::new(1), -// SmallF32Mont::new(3), -// SmallF32Mont::new(9), -// SmallF32Mont::new(27), -// ]; -// } - -// pub type Fp4SmallM31 = Fp4; - -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] -pub struct Fp2GoldilocksConfig; - -impl Fp2Config for Fp2GoldilocksConfig { - type Fp = Goldilocks; - - // const context: use new_unchecked(BigInt) - const NONRESIDUE: Goldilocks = Goldilocks::new_unchecked(BigInt::<1>([3u64])); - - // Arkworks 0.5 expects &'static [Fp] - const FROBENIUS_COEFF_FP2_C1: &'static [Goldilocks] = &[ - Goldilocks::new_unchecked(BigInt::<1>([1u64])), - Goldilocks::new_unchecked(BigInt::<1>([3u64])), - ]; -} - -pub type Fp2Goldilocks = Fp2; - -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] -pub struct Fp2M31Config; - -impl Fp2Config for Fp2M31Config { - type Fp = M31; - - // Use const_new to build compile-time constants - const NONRESIDUE: M31 = M31::new_unchecked(BigInt::<1>([3u64])); - - // These Frobenius coeffs aren't used for arithmetic benchmarks anyway - const FROBENIUS_COEFF_FP2_C1: &'static [M31] = &[ - M31::new_unchecked(BigInt::<1>([1u64])), - M31::new_unchecked(BigInt::<1>([3u64])), - ]; -} - -pub type Fp2M31 = Fp2; - -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] -pub struct Fp4M31Config; - -impl Fp4Config for Fp4M31Config { - type Fp2Config = Fp2M31Config; - - const NONRESIDUE: Fp2 = Fp2::::new( - M31::new_unchecked(BigInt::<1>([3u64])), - M31::new_unchecked(BigInt::<1>([0u64])), - ); - - // 👇 now a slice of base‐field elements, not Fp2 elements - const FROBENIUS_COEFF_FP4_C1: &'static [M31] = &[ - M31::new_unchecked(BigInt::<1>([1u64])), - M31::new_unchecked(BigInt::<1>([3u64])), - M31::new_unchecked(BigInt::<1>([9u64])), - M31::new_unchecked(BigInt::<1>([27u64])), - ]; -} - -pub type Fp4M31 = Fp4; - -pub fn bench_fp4_mul(c: &mut Criterion) { - let mut rng = test_rng(); - let a = Fp4M31::rand(&mut rng); - let b = Fp4M31::rand(&mut rng); - - c.bench_function("Fp4 multiply", |bench| { - bench.iter(|| { - let mut acc = a; - // Chain 1000 multiplications to smooth noise - for _ in 0..1000 { - acc *= b; - } - acc - }); - }); -} - -pub fn bench_f128_mul(c: &mut Criterion) { - let mut rng = test_rng(); - let a = F128::rand(&mut rng); - let b = F128::rand(&mut rng); - - c.bench_function("F128 multiply", |bench| { - bench.iter(|| { - let mut acc = a; - for _ in 0..1000 { - acc *= b; - } - acc - }); - }); -} - -pub fn bench_fp2_goldilocks_mul(c: &mut Criterion) { - let mut rng = test_rng(); - let a = Fp2Goldilocks::rand(&mut rng); - let b = Fp2Goldilocks::rand(&mut rng); - - c.bench_function("Fp2 multiply", |bench| { - bench.iter(|| { - let mut acc = a; - for _ in 0..1000 { - acc *= b; - } - acc - }); - }); -} - -// smalls - -// pub fn bench_small_fp4_mul(c: &mut Criterion) { -// let mut rng = test_rng(); -// let a = Fp4SmallM31::rand(&mut rng); -// let b = Fp4SmallM31::rand(&mut rng); - -// c.bench_function("Fp4 multiply", |bench| { -// bench.iter(|| { -// let mut acc = a; -// // Chain 1000 multiplications to smooth noise -// for _ in 0..1000 { -// acc *= b; -// } -// acc -// }); -// }); -// } - -// pub fn bench_small_f128_mul(c: &mut Criterion) { -// let mut rng = test_rng(); -// let a = SmallF128Mont::rand(&mut rng); -// let b = SmallF128Mont::rand(&mut rng); - -// c.bench_function("Small F128 multiply", |bench| { -// bench.iter(|| { -// let mut acc = a; -// for _ in 0..1000 { -// acc *= b; -// } -// acc -// }); -// }); -// } - -// pub fn bench_small_fp2_goldilocks_mul(c: &mut Criterion) { -// let mut rng = test_rng(); -// let a = Fp2SmallGoldilocks::rand(&mut rng); -// let b = Fp2SmallGoldilocks::rand(&mut rng); - -// c.bench_function("Fp2 multiply", |bench| { -// bench.iter(|| { -// let mut acc = a; -// for _ in 0..1000 { -// acc *= b; -// } -// acc -// }); -// }); -// } - -criterion_group!( - benches, - // bench_small_f128_mul, - // bench_small_fp4_mul, - // bench_small_fp2_goldilocks_mul, - bench_f128_mul, - bench_fp4_mul, - bench_fp2_goldilocks_mul, -); -criterion_main!(benches); diff --git a/src/tests/fields.rs b/src/tests/fields.rs index f85675fb..cc709943 100644 --- a/src/tests/fields.rs +++ b/src/tests/fields.rs @@ -1,6 +1,4 @@ -use ark_ff::ark_ff_macros::SmallFpConfig; use ark_ff::fields::{Fp128, Fp64, MontBackend, MontConfig}; -use ark_ff::{BigInt, SmallFp, SmallFpConfig, SqrtPrecomputation}; #[derive(MontConfig)] #[modulus = "19"] @@ -14,35 +12,14 @@ pub type F19 = Fp64>; pub struct M31Config; pub type M31 = Fp64>; -#[derive(SmallFpConfig)] -#[modulus = "2147483647"] // m31 -#[generator = "7"] -#[backend = "montgomery"] -pub struct SmallFieldMont; -pub type SmallF32Mont = SmallFp; - #[derive(MontConfig)] #[modulus = "18446744069414584321"] // q = 2^64 - 2^32 + 1 #[generator = "2"] pub struct F64Config; pub type F64 = Fp64>; -#[derive(SmallFpConfig)] -#[modulus = "18446744069414584321"] // Goldilock's prime 2^64 - 2^32 + 1 -#[generator = "7"] -#[backend = "montgomery"] -pub struct SmallF64ConfigMont; -pub type SmallF64Mont = SmallFp; - #[derive(MontConfig)] #[modulus = "143244528689204659050391023439224324689"] // q = 143244528689204659050391023439224324689 #[generator = "2"] pub struct F128Config; pub type F128 = Fp128>; - -#[derive(SmallFpConfig)] -#[modulus = "143244528689204659050391023439224324689"] -#[generator = "3"] -#[backend = "montgomery"] -pub struct SmallF128ConfigMont; -pub type SmallF128Mont = SmallFp; diff --git a/src/tests/mod.rs b/src/tests/mod.rs index edf1119a..fc8f786d 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -4,5 +4,5 @@ mod streams; pub mod multilinear; pub mod multilinear_product; pub mod polynomials; -pub use fields::{SmallF128Mont, SmallF32Mont, SmallF64Mont, F128, F19, F64, M31}; +pub use fields::{F128, F19, F64, M31}; pub use streams::BenchStream; From 9c4e286617c025bf2e289bb6f4cc9d4d23a7ac8d Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Wed, 5 Nov 2025 14:23:07 +0100 Subject: [PATCH 10/11] cleanup no default features --- src/multilinear/provers/time/prover.rs | 9 +- .../provers/time/reductions/pairwise.rs | 68 +++++-------- .../provers/time/reductions/variablewise.rs | 99 +++++++------------ 3 files changed, 62 insertions(+), 114 deletions(-) diff --git a/src/multilinear/provers/time/prover.rs b/src/multilinear/provers/time/prover.rs index b1981c1e..191c0f6d 100644 --- a/src/multilinear/provers/time/prover.rs +++ b/src/multilinear/provers/time/prover.rs @@ -53,12 +53,10 @@ impl> TimeProver { return None; } - let num_free_variables = self.num_variables - self.current_round; if self.current_round != 0 { if self.current_round > 1 { variablewise::reduce_evaluations( self.evaluations.as_mut().unwrap(), - num_free_variables, verifier_message.unwrap(), F::ONE - verifier_message.unwrap(), ); @@ -67,7 +65,6 @@ impl> TimeProver { variablewise::reduce_evaluations_from_stream( &self.evaluation_streams[0], self.evaluations.as_mut().unwrap(), - num_free_variables, verifier_message.unwrap(), F::ONE - verifier_message.unwrap(), ); @@ -76,10 +73,8 @@ impl> TimeProver { // evaluate using vsbw let sums = match &self.evaluations { - None => { - variablewise::evaluate_from_stream(&self.evaluation_streams[0], num_free_variables) - } - Some(evaluations) => variablewise::evaluate(evaluations, num_free_variables), + None => variablewise::evaluate_from_stream(&self.evaluation_streams[0]), + Some(evaluations) => variablewise::evaluate(evaluations), }; // Increment the round counter diff --git a/src/multilinear/provers/time/reductions/pairwise.rs b/src/multilinear/provers/time/reductions/pairwise.rs index 94b1fed4..68763caf 100644 --- a/src/multilinear/provers/time/reductions/pairwise.rs +++ b/src/multilinear/provers/time/reductions/pairwise.rs @@ -1,53 +1,37 @@ use ark_ff::Field; use ark_std::vec::Vec; -use ark_std::{cfg_chunks, cfg_into_iter, cfg_iter}; +use ark_std::{cfg_chunks, cfg_into_iter}; +#[cfg(feature = "parallel")] use rayon::{ - iter::{ - IndexedParallelIterator, IntoParallelIterator, IntoParallelRefIterator, ParallelIterator, - }, + iter::{IndexedParallelIterator, IntoParallelIterator, ParallelIterator}, prelude::ParallelSlice, }; use crate::streams::Stream; -pub fn evaluate(src: &Vec) -> (F, F) { - // Sum even indices - let even_sum = cfg_iter!(src) - .enumerate() - .filter(|(i, _)| i % 2 == 0) - .map(|(_, val)| *val) +pub fn evaluate(src: &[F]) -> (F, F) { + let even_sum = cfg_into_iter!(0..src.len()) + .step_by(2) + .map(|i| src[i]) .sum(); - - // Sum odd indices - let odd_sum = cfg_iter!(src) - .enumerate() - .filter(|(i, _)| i % 2 == 1) - .map(|(_, val)| *val) + let odd_sum = cfg_into_iter!(1..src.len()) + .step_by(2) + .map(|i| src[i]) .sum(); - (even_sum, odd_sum) } pub fn evaluate_from_stream>(src: &S) -> (F, F) { let len = 1usize << src.num_variables(); - - cfg_into_iter!(0..len) - .map(|i| (i, src.evaluation(i))) - .fold( - || (F::zero(), F::zero()), - |(mut even, mut odd), (i, val)| { - if i % 2 == 0 { - even += val; - } else { - odd += val; - } - (even, odd) - }, - ) - .reduce( - || (F::zero(), F::zero()), - |(e1, o1), (e2, o2)| (e1 + e2, o1 + o2), - ) + let even_sum = cfg_into_iter!(0..len) + .step_by(2) + .map(|i| src.evaluation(i)) + .sum(); + let odd_sum = cfg_into_iter!(1..len) + .step_by(2) + .map(|i| src.evaluation(i)) + .sum(); + (even_sum, odd_sum) } pub fn reduce_evaluations( @@ -65,23 +49,19 @@ pub fn reduce_evaluations( } pub fn reduce_evaluations_from_stream>( - stream: &S, + src: &S, dst: &mut Vec, verifier_message: F, verifier_message_hat: F, ) { // compute from stream - let len = 2_i32.pow(stream.num_variables() as u32) as usize; + let len = 1usize << src.num_variables(); let out: Vec = cfg_into_iter!(0..len / 2) .map(|i| { - let a = stream.evaluation(2 * i); - let b = stream.evaluation((2 * i) + 1); + let a = src.evaluation(2 * i); + let b = src.evaluation((2 * i) + 1); a * verifier_message_hat + b * verifier_message }) .collect(); - // write back into dst - if dst.len() < out.len() { - dst.resize(out.len(), F::zero()); - } - dst[..out.len()].copy_from_slice(&out); + *dst = out; } diff --git a/src/multilinear/provers/time/reductions/variablewise.rs b/src/multilinear/provers/time/reductions/variablewise.rs index 9ac69ceb..e56bedb7 100644 --- a/src/multilinear/provers/time/reductions/variablewise.rs +++ b/src/multilinear/provers/time/reductions/variablewise.rs @@ -1,89 +1,62 @@ use ark_ff::Field; use ark_std::vec::Vec; -use ark_std::{cfg_into_iter, cfg_iter_mut}; -use rayon::iter::IntoParallelRefMutIterator; -use rayon::iter::{IndexedParallelIterator, IntoParallelIterator, ParallelIterator}; +use ark_std::cfg_into_iter; +#[cfg(feature = "parallel")] +use rayon::iter::{ + IntoParallelIterator, ParallelIterator, +}; use crate::streams::Stream; -pub fn evaluate(src: &[F], num_free_variables: usize) -> (F, F) { - let bitmask: usize = 1 << (num_free_variables - 1); - let (sum_0, sum_1) = cfg_into_iter!(0..src.len()) - .map(|i| { - let val = src[i]; - // Route value into the proper bucket - if (i & bitmask) == 0 { - (val, F::zero()) // contributes to sum_0 - } else { - (F::zero(), val) // contributes to sum_1 - } - }) - // Combine partial (sum0, sum1) pairs from each worker/thread. - .reduce( - || (F::zero(), F::zero()), - |(a0, a1), (b0, b1)| (a0 + b0, a1 + b1), - ); +pub fn evaluate(src: &[F]) -> (F, F) { + let sum_0 = cfg_into_iter!(0..src.len() / 2).map(|i| src[i]).sum(); + let sum_1 = cfg_into_iter!(src.len() / 2..src.len()) + .map(|i| src[i]) + .sum(); (sum_0, sum_1) } -pub fn evaluate_from_stream>(src: &S, num_free_variables: usize) -> (F, F) { +pub fn evaluate_from_stream>(src: &S) -> (F, F) { let len = 1usize << src.num_variables(); - let bitmask: usize = 1 << (num_free_variables - 1); - let (sum_0, sum_1) = cfg_into_iter!(0..len) - .map(|i| { - let val = src.evaluation(i); - // Route value into the proper bucket - if (i & bitmask) == 0 { - (val, F::zero()) // contributes to sum_0 - } else { - (F::zero(), val) // contributes to sum_1 - } - }) - // Combine partial (sum0, sum1) pairs from each worker/thread. - .reduce( - || (F::zero(), F::zero()), - |(a0, a1), (b0, b1)| (a0 + b0, a1 + b1), - ); + let sum_0 = cfg_into_iter!(0..len / 2).map(|i| src.evaluation(i)).sum(); + let sum_1 = cfg_into_iter!(len / 2..len) + .map(|i| src.evaluation(i)) + .sum(); (sum_0, sum_1) } pub fn reduce_evaluations( src: &mut Vec, - num_free_variables: usize, verifier_message: F, verifier_message_hat: F, ) { - let setbit: usize = 1 << num_free_variables; - let mut dest = vec![F::zero(); src.len() / 2]; - cfg_iter_mut!(dest).enumerate().for_each(|(i0, slot)| { - let i1 = i0 | setbit; - let v0 = src[i0]; - let v1 = src[i1]; - *slot = v0 * verifier_message_hat + v1 * verifier_message; - }); - *src = dest; + let second_half_bit: usize = src.len() / 2; + let out: Vec = cfg_into_iter!(0..src.len() / 2) + .map(|i0| { + let v0 = src[i0]; + let i1 = i0 | second_half_bit; + let v1 = src[i1]; + v0 * verifier_message_hat + v1 * verifier_message + }) + .collect(); + *src = out; } pub fn reduce_evaluations_from_stream>( src: &S, dst: &mut Vec, - num_free_variables: usize, verifier_message: F, verifier_message_hat: F, ) { - // compute from stream - let len = 2_i32.pow(src.num_variables() as u32) as usize; - let setbit: usize = 1 << num_free_variables; - let mut out = vec![F::zero(); len / 2]; - cfg_iter_mut!(out).enumerate().for_each(|(i0, slot)| { - let i1 = i0 | setbit; - let v0 = src.evaluation(i0); - let v1 = src.evaluation(i1); - *slot = v0 * verifier_message_hat + v1 * verifier_message; - }); - // write back into dst - if dst.len() < out.len() { - dst.resize(out.len(), F::zero()); - } - dst[..out.len()].copy_from_slice(&out); + let len = 1usize << src.num_variables(); + let second_half_bit: usize = len / 2; + let out: Vec = cfg_into_iter!(0..len / 2) + .map(|i0| { + let v0 = src.evaluation(i0); + let i1 = i0 | second_half_bit; + let v1 = src.evaluation(i1); + v0 * verifier_message_hat + v1 * verifier_message + }) + .collect(); + *dst = out; } From bd107ceb54beb2fad6a5cb1c22bd39a844754acc Mon Sep 17 00:00:00 2001 From: Andrew Z <1497456+z-tech@users.noreply.github.com> Date: Wed, 5 Nov 2025 14:25:01 +0100 Subject: [PATCH 11/11] lint --- src/multilinear/provers/time/reductions/variablewise.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/multilinear/provers/time/reductions/variablewise.rs b/src/multilinear/provers/time/reductions/variablewise.rs index e56bedb7..f2533128 100644 --- a/src/multilinear/provers/time/reductions/variablewise.rs +++ b/src/multilinear/provers/time/reductions/variablewise.rs @@ -1,10 +1,8 @@ use ark_ff::Field; -use ark_std::vec::Vec; use ark_std::cfg_into_iter; +use ark_std::vec::Vec; #[cfg(feature = "parallel")] -use rayon::iter::{ - IntoParallelIterator, ParallelIterator, -}; +use rayon::iter::{IntoParallelIterator, ParallelIterator}; use crate::streams::Stream;