diff --git a/src/multilinear/mod.rs b/src/multilinear/mod.rs index 48e4ed0..5304992 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::{ReduceMode, TimeProver, TimeProverConfig}, }; pub use sumcheck::Sumcheck; diff --git a/src/multilinear/provers/space/config.rs b/src/multilinear/provers/space/config.rs index 4aaba1e..049106f 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 f9d5f90..825664d 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 8a2d850..e1da9a1 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/multilinear/provers/time/config.rs b/src/multilinear/provers/time/config.rs index b83ea4f..e27066e 100644 --- a/src/multilinear/provers/time/config.rs +++ b/src/multilinear/provers/time/config.rs @@ -1,6 +1,10 @@ use ark_ff::Field; -use crate::{prover::ProverConfig, streams::Stream}; +use crate::{ + multilinear::provers::time::reductions::ReduceMode, + prover::{BatchProverConfig, ProverConfig}, + streams::Stream, +}; pub struct TimeProverConfig where @@ -9,7 +13,8 @@ where { pub num_variables: usize, pub claim: F, - pub stream: S, + pub streams: Vec, + pub reduce_mode: ReduceMode, } impl TimeProverConfig @@ -17,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, - stream, + streams: vec![stream], + reduce_mode, } } } @@ -31,7 +37,19 @@ impl> ProverConfig for TimeProverConfig { Self { claim, num_variables, - stream, + streams: vec![stream], + reduce_mode: ReduceMode::Pairwise, + } + } +} + +impl> BatchProverConfig for TimeProverConfig { + fn default(claim: F, num_variables: usize, streams: Vec) -> Self { + Self { + 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 a6591f7..2c9a361 100644 --- a/src/multilinear/provers/time/core.rs +++ b/src/multilinear/provers/time/core.rs @@ -1,167 +1,19 @@ +use crate::multilinear::provers::time::reductions::ReduceMode; 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, + pub reduce_mode: ReduceMode, } impl> TimeProver { - fn num_free_variables(&self) -> usize { - 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(), - None => 2usize.pow(self.evaluation_stream.num_variables() as u32), - }; - - #[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] - } else { - self.evaluation_stream.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), - ); - - // 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(), - }; - - // 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. - 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 | setbit; - let v0 = src[i0]; - 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 | setbit; - let v0 = stream.evaluation(i0); - 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 3af1ceb..f586f9d 100644 --- a/src/multilinear/provers/time/mod.rs +++ b/src/multilinear/provers/time/mod.rs @@ -1,6 +1,8 @@ mod config; mod core; mod prover; +mod reductions; 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 9c34258..191c0f6 100644 --- a/src/multilinear/provers/time/prover.rs +++ b/src/multilinear/provers/time/prover.rs @@ -1,11 +1,90 @@ use ark_ff::Field; +use crate::multilinear::provers::time::reductions::ReduceMode; use crate::{ - multilinear::{TimeProver, TimeProverConfig}, + multilinear::{ + provers::time::reductions::{pairwise, variablewise}, + TimeProver, TimeProverConfig, + }, prover::Prover, streams::Stream, }; +impl> TimeProver { + 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; + } + + if self.current_round != 0 { + if self.current_round > 1 { + variablewise::reduce_evaluations( + self.evaluations.as_mut().unwrap(), + 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(), + 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]), + Some(evaluations) => variablewise::evaluate(evaluations), + }; + + // Increment the round counter + self.current_round += 1; + + // Return the computed polynomial + Some(sums) + } +} + impl> Prover for TimeProver { type ProverConfig = TimeProverConfig; type ProverMessage = Option<(F, F)>; @@ -20,34 +99,17 @@ 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, + 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; - } - - // 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(), - ) + match self.reduce_mode { + ReduceMode::Pairwise => self.next_message_pairwise(verifier_message), + ReduceMode::Variablewise => self.next_message_variablewise(verifier_message), } - - // evaluate using vsbw - let sums = self.vsbw_evaluate(); - - // Increment the round counter - self.current_round += 1; - - // Return the computed polynomial - Some(sums) } } @@ -56,11 +118,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/provers/time/reductions/mod.rs b/src/multilinear/provers/time/reductions/mod.rs new file mode 100644 index 0000000..e59f35f --- /dev/null +++ b/src/multilinear/provers/time/reductions/mod.rs @@ -0,0 +1,8 @@ +pub mod pairwise; +pub mod variablewise; + +#[derive(Copy, Clone, Debug)] +pub enum ReduceMode { + Pairwise, + Variablewise, +} diff --git a/src/multilinear/provers/time/reductions/pairwise.rs b/src/multilinear/provers/time/reductions/pairwise.rs new file mode 100644 index 0000000..68763ca --- /dev/null +++ b/src/multilinear/provers/time/reductions/pairwise.rs @@ -0,0 +1,67 @@ +use ark_ff::Field; +use ark_std::vec::Vec; +use ark_std::{cfg_chunks, cfg_into_iter}; +#[cfg(feature = "parallel")] +use rayon::{ + iter::{IndexedParallelIterator, IntoParallelIterator, ParallelIterator}, + prelude::ParallelSlice, +}; + +use crate::streams::Stream; + +pub fn evaluate(src: &[F]) -> (F, F) { + let even_sum = cfg_into_iter!(0..src.len()) + .step_by(2) + .map(|i| src[i]) + .sum(); + 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(); + 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( + 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>( + src: &S, + dst: &mut Vec, + verifier_message: F, + verifier_message_hat: F, +) { + // compute from stream + let len = 1usize << src.num_variables(); + let out: Vec = cfg_into_iter!(0..len / 2) + .map(|i| { + let a = src.evaluation(2 * i); + let b = src.evaluation((2 * i) + 1); + a * verifier_message_hat + b * verifier_message + }) + .collect(); + *dst = out; +} diff --git a/src/multilinear/provers/time/reductions/variablewise.rs b/src/multilinear/provers/time/reductions/variablewise.rs new file mode 100644 index 0000000..f253312 --- /dev/null +++ b/src/multilinear/provers/time/reductions/variablewise.rs @@ -0,0 +1,60 @@ +use ark_ff::Field; +use ark_std::cfg_into_iter; +use ark_std::vec::Vec; +#[cfg(feature = "parallel")] +use rayon::iter::{IntoParallelIterator, ParallelIterator}; + +use crate::streams::Stream; + +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) -> (F, F) { + let len = 1usize << src.num_variables(); + 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, + verifier_message: F, + verifier_message_hat: F, +) { + 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, + verifier_message: F, + verifier_message_hat: F, +) { + 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; +} diff --git a/src/multilinear/sumcheck.rs b/src/multilinear/sumcheck.rs index affb44c..6aaa896 100644 --- a/src/multilinear/sumcheck.rs +++ b/src/multilinear/sumcheck.rs @@ -62,41 +62,64 @@ 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() { + const NUM_VARIABLES: usize = 20; + // take an evaluation stream - let evaluation_stream: BenchStream = BenchStream::new(20); + let evaluation_stream: BenchStream = BenchStream::new(NUM_VARIABLES); 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()), + BlendyProverConfig::new(claim, 3, NUM_VARIABLES, evaluation_stream.clone()), ); - let mut time_prover = TimeProver::>::new(, - > as Prover>::ProverConfig::default( - claim, - 20, - 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::< + + // time_prover_variablewise + let mut time_prover_variablewise = TimeProver::>::new(, - TimeProver>, - >(&mut time_prover, &mut ark_std::test_rng()); - // ensure the transcript is identical + > as Prover>::ProverConfig::new( + claim, + NUM_VARIABLES, + evaluation_stream.clone(), + ReduceMode::Variablewise, + )); + let time_prover_variablewise_transcript = + Sumcheck::::prove::, TimeProver>>( + &mut time_prover_variablewise, + &mut ark_std::test_rng(), + ); + + // ensure transcripts identical assert_eq!( - time_prover_transcript.prover_messages, + 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, + NUM_VARIABLES, + evaluation_stream, + )); + let time_prover_pairwise_transcript = + Sumcheck::::prove::, TimeProver>>( + &mut time_prover_pairwise, + &mut ark_std::test_rng(), + ); + assert!(time_prover_pairwise_transcript.is_accepted); } } diff --git a/src/prover/core.rs b/src/prover/core.rs index 6023937..48532dc 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 53f9143..4cf9982 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}; diff --git a/src/tests/fields.rs b/src/tests/fields.rs index 345b54e..cc70994 100644 --- a/src/tests/fields.rs +++ b/src/tests/fields.rs @@ -6,6 +6,12 @@ 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(MontConfig)] #[modulus = "18446744069414584321"] // q = 2^64 - 2^32 + 1 #[generator = "2"] diff --git a/src/tests/mod.rs b/src/tests/mod.rs index a238aae..fc8f786 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::{F128, F19, F64, M31}; pub use streams::BenchStream; diff --git a/src/tests/multilinear/mod.rs b/src/tests/multilinear/mod.rs index 98beda5..7ee03ec 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), + ); +}