From 0fe9dc4c583f85386650bacbd5d29817209196b8 Mon Sep 17 00:00:00 2001 From: tonyfloatersu <tonyfloater@gmail.com> Date: Thu, 21 Nov 2024 01:45:49 -0500 Subject: [PATCH 01/65] Orion PCS implementation prorotyped --- Cargo.lock | 8 + Cargo.toml | 1 - arith/src/serde.rs | 13 +- gkr/src/prover/linear_gkr.rs | 3 + gkr/src/verifier.rs | 2 + poly_commit/Cargo.toml | 15 ++ poly_commit/benches/orion.rs | 126 +++++++++++ poly_commit/src/lib.rs | 5 + poly_commit/src/orion.rs | 14 ++ poly_commit/src/orion/linear_code.rs | 295 ++++++++++++++++++++++++++ poly_commit/src/orion/pcs_impl.rs | 300 +++++++++++++++++++++++++++ poly_commit/src/orion/serde.rs | 124 +++++++++++ poly_commit/src/orion/tests.rs | 199 ++++++++++++++++++ poly_commit/src/orion/utils.rs | 167 +++++++++++++++ poly_commit/src/traits.rs | 33 +++ poly_commit/tests/common.rs | 5 + 16 files changed, 1303 insertions(+), 7 deletions(-) create mode 100644 poly_commit/benches/orion.rs create mode 100644 poly_commit/src/orion.rs create mode 100644 poly_commit/src/orion/linear_code.rs create mode 100644 poly_commit/src/orion/pcs_impl.rs create mode 100644 poly_commit/src/orion/serde.rs create mode 100644 poly_commit/src/orion/tests.rs create mode 100644 poly_commit/src/orion/utils.rs diff --git a/Cargo.lock b/Cargo.lock index a271ae76..e203cd90 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1794,11 +1794,19 @@ name = "poly_commit" version = "0.1.0" dependencies = [ "arith", + "ark-std", + "criterion", + "gf2", + "gf2_128", "gkr_field_config", + "mersenne31", "mpi_config", "polynomials", "rand", + "thiserror", "transcript", + "tree", + "tynm", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index ac8a020e..cfa7b6dd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,6 @@ members = [ ] resolver = "2" - [workspace.dependencies] ark-std = "0.4" ark-bn254 = "0.4.0" diff --git a/arith/src/serde.rs b/arith/src/serde.rs index 30e47a43..e059f610 100644 --- a/arith/src/serde.rs +++ b/arith/src/serde.rs @@ -23,19 +23,19 @@ pub trait FieldSerde: Sized { fn deserialize_from<R: Read>(reader: R) -> FieldSerdeResult<Self>; } -macro_rules! field_serde_for_integer { +macro_rules! field_serde_for_number { ($int_type: ident, $size_in_bytes: expr) => { impl FieldSerde for $int_type { /// size of the serialized bytes const SERIALIZED_SIZE: usize = $size_in_bytes; - /// serialize u64 into bytes + /// serialize number into bytes fn serialize_into<W: Write>(&self, mut writer: W) -> FieldSerdeResult<()> { writer.write_all(&self.to_le_bytes())?; Ok(()) } - /// deserialize bytes into u64 + /// deserialize bytes into number fn deserialize_from<R: Read>(mut reader: R) -> FieldSerdeResult<Self> { let mut buffer = [0u8; Self::SERIALIZED_SIZE]; reader.read_exact(&mut buffer)?; @@ -45,9 +45,10 @@ macro_rules! field_serde_for_integer { }; } -field_serde_for_integer!(u64, 8); -field_serde_for_integer!(usize, 8); -field_serde_for_integer!(u8, 1); +field_serde_for_number!(u64, 8); +field_serde_for_number!(usize, 8); +field_serde_for_number!(u8, 1); +field_serde_for_number!(f64, 8); impl<V: FieldSerde> FieldSerde for Vec<V> { const SERIALIZED_SIZE: usize = unimplemented!(); diff --git a/gkr/src/prover/linear_gkr.rs b/gkr/src/prover/linear_gkr.rs index 5aca67f4..b0c6f758 100644 --- a/gkr/src/prover/linear_gkr.rs +++ b/gkr/src/prover/linear_gkr.rs @@ -168,6 +168,7 @@ impl<Cfg: GKRConfig> Prover<Cfg> { pcs_scratch: &mut <Cfg::PCS as PCSForExpanderGKR<Cfg::FieldConfig, Cfg::Transcript>>::ScratchPad, transcript: &mut Cfg::Transcript, ) { + transcript.lock_proof(); let opening = Cfg::PCS::open( pcs_params, &self.config.mpi_config, @@ -177,6 +178,8 @@ impl<Cfg: GKRConfig> Prover<Cfg> { transcript, pcs_scratch, ); + transcript.unlock_proof(); + let mut buffer = vec![]; opening.serialize_into(&mut buffer).unwrap(); // TODO: error propagation transcript.append_u8_slice(&buffer); diff --git a/gkr/src/verifier.rs b/gkr/src/verifier.rs index 712ba76e..89349359 100644 --- a/gkr/src/verifier.rs +++ b/gkr/src/verifier.rs @@ -360,6 +360,7 @@ impl<Cfg: GKRConfig> Verifier<Cfg> { ) .unwrap(); + transcript.lock_proof(); let verified = Cfg::PCS::verify( pcs_params, &self.config.mpi_config, @@ -370,6 +371,7 @@ impl<Cfg: GKRConfig> Verifier<Cfg> { transcript, &opening, ); + transcript.unlock_proof(); let mut buffer = vec![]; opening.serialize_into(&mut buffer).unwrap(); // TODO: error propagation diff --git a/poly_commit/Cargo.toml b/poly_commit/Cargo.toml index fe1ed056..e69708f2 100644 --- a/poly_commit/Cargo.toml +++ b/poly_commit/Cargo.toml @@ -9,5 +9,20 @@ gkr_field_config = { path = "../config/gkr_field_config" } mpi_config = { path = "../config/mpi_config" } polynomials = { path = "../arith/polynomials"} transcript = { path = "../transcript" } +tree = { path = "../tree" } rand.workspace = true +ark-std.workspace = true +thiserror.workspace = true + +[dev-dependencies] +gf2 = { path = "../arith/gf2" } +gf2_128 = { path = "../arith/gf2_128" } +mersenne31 = { path = "../arith/mersenne31" } + +tynm.workspace = true +criterion.workspace = true + +[[bench]] +name = "orion" +harness = false diff --git a/poly_commit/benches/orion.rs b/poly_commit/benches/orion.rs new file mode 100644 index 00000000..d538a746 --- /dev/null +++ b/poly_commit/benches/orion.rs @@ -0,0 +1,126 @@ +use std::{hint::black_box, ops::Mul}; + +use arith::{Field, SimdField}; +use ark_std::test_rng; +use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion}; +use gf2::{GF2x128, GF2x8, GF2}; +use gf2_128::GF2_128; +use poly_commit::{OrionSRS, OrionScratchPad, ORION_CODE_PARAMETER_INSTANCE}; +use polynomials::MultiLinearPoly; +use transcript::{BytesHashTranscript, Keccak256hasher, Transcript}; +use tynm::type_name; + +fn committing_benchmark_helper<F, EvalF, ComPackF, OpenPackF, T>( + c: &mut Criterion, + lowest_num_vars: usize, + highest_num_vars: usize, +) where + F: Field, + EvalF: Field + From<F> + Mul<F, Output = EvalF>, + ComPackF: SimdField<Scalar = F>, + OpenPackF: SimdField<Scalar = F>, + T: Transcript<EvalF>, +{ + let mut group = c.benchmark_group(format!( + "Orion PCS committing benchmarking: F = {}, ComPackF = {}", + type_name::<F>(), + type_name::<ComPackF>(), + )); + + let mut rng = test_rng(); + let mut orion_scratch = OrionScratchPad::default(); + + for num_vars in lowest_num_vars..=highest_num_vars { + let poly = MultiLinearPoly::<F>::random(num_vars, &mut rng); + + let orion_srs = + OrionSRS::from_random::<F>(num_vars, ORION_CODE_PARAMETER_INSTANCE, &mut rng); + + group + .bench_function( + BenchmarkId::new(format!("{num_vars} variables"), num_vars), + |b| { + b.iter(|| { + _ = black_box( + orion_srs + .commit::<F, ComPackF>(&poly, &mut orion_scratch) + .unwrap(), + ) + }) + }, + ) + .sample_size(10); + } +} + +fn orion_committing_benchmark(c: &mut Criterion) { + committing_benchmark_helper::< + GF2, + GF2_128, + GF2x128, + GF2x8, + BytesHashTranscript<_, Keccak256hasher>, + >(c, 19, 30); +} + +fn opening_benchmark_helper<F, EvalF, ComPackF, OpenPackF, T>( + c: &mut Criterion, + lowest_num_vars: usize, + highest_num_vars: usize, +) where + F: Field, + EvalF: Field + From<F> + Mul<F, Output = EvalF>, + ComPackF: SimdField<Scalar = F>, + OpenPackF: SimdField<Scalar = F>, + T: Transcript<EvalF>, +{ + let mut group = c.benchmark_group(format!( + "Orion PCS opening benchmarking: F = {}, EvalF = {}, ComPackF = {}", + type_name::<F>(), + type_name::<EvalF>(), + type_name::<ComPackF>(), + )); + + let mut rng = test_rng(); + let mut transcript = T::new(); + let mut orion_scratch = OrionScratchPad::default(); + + for num_vars in lowest_num_vars..=highest_num_vars { + let poly = MultiLinearPoly::<F>::random(num_vars, &mut rng); + let eval_point: Vec<_> = (0..num_vars) + .map(|_| EvalF::random_unsafe(&mut rng)) + .collect(); + + let orion_srs = + OrionSRS::from_random::<F>(num_vars, ORION_CODE_PARAMETER_INSTANCE, &mut rng); + + let _orion_commitment = orion_srs + .commit::<F, ComPackF>(&poly, &mut orion_scratch) + .unwrap(); + + group + .bench_function( + BenchmarkId::new(format!("{num_vars} variables"), num_vars), + |b| { + b.iter(|| { + _ = black_box(orion_srs.open::<F, EvalF, ComPackF, OpenPackF, T>( + &poly, + &eval_point, + &mut transcript, + &orion_scratch, + )) + }) + }, + ) + .sample_size(10); + } +} + +fn orion_opening_benchmark(c: &mut Criterion) { + opening_benchmark_helper::<GF2, GF2_128, GF2x128, GF2x8, BytesHashTranscript<_, Keccak256hasher>>( + c, 19, 30, + ); +} + +criterion_group!(bench, orion_committing_benchmark, orion_opening_benchmark); +criterion_main!(bench); diff --git a/poly_commit/src/lib.rs b/poly_commit/src/lib.rs index a307b89c..21efebb9 100644 --- a/poly_commit/src/lib.rs +++ b/poly_commit/src/lib.rs @@ -39,8 +39,13 @@ pub fn expander_pcs_init_testing_only< ) } +pub const PCS_SOUNDNESS_BITS: usize = 128; + mod utils; use transcript::Transcript; use utils::PCSEmptyType; pub mod raw; + +pub mod orion; +pub use self::orion::*; diff --git a/poly_commit/src/orion.rs b/poly_commit/src/orion.rs new file mode 100644 index 00000000..64a5b73a --- /dev/null +++ b/poly_commit/src/orion.rs @@ -0,0 +1,14 @@ +mod utils; +pub use utils::{ + OrionCommitment, OrionPCSError, OrionProof, OrionResult, OrionSRS, OrionScratchPad, + SubsetSumLUTs, +}; + +mod linear_code; +pub use linear_code::{OrionCodeParameter, ORION_CODE_PARAMETER_INSTANCE}; + +mod pcs_impl; +mod serde; + +#[cfg(test)] +mod tests; diff --git a/poly_commit/src/orion/linear_code.rs b/poly_commit/src/orion/linear_code.rs new file mode 100644 index 00000000..d33e4963 --- /dev/null +++ b/poly_commit/src/orion/linear_code.rs @@ -0,0 +1,295 @@ +use std::cmp; + +use arith::Field; +use rand::seq::index; + +use super::{OrionPCSError, OrionResult}; + +/* + * IMPLEMENTATIONS FOR ORION EXPANDER GRAPH + */ + +pub type DirectedEdge = usize; + +pub type DirectedNeighboring = Vec<DirectedEdge>; + +#[derive(Clone, Debug, Default)] +pub struct OrionExpanderGraph { + // L R vertices size book keeping: + // keep track of message length (l), and "compressed" code length (r) + pub l_vertices_size: usize, + pub r_vertices_size: usize, + + // neighboring stands for all (weighted) connected vertices of a vertex. + // In this context, the neighborings stands for the neighborings + // of vertices in R set of the bipariate graph, which explains why it has + // size of l_vertices_size, while each neighboring reserved r_vertices_size + // capacity. + pub neighborings: Vec<DirectedNeighboring>, +} + +impl OrionExpanderGraph { + pub fn new( + l_vertices_size: usize, + r_vertices_size: usize, + expanding_degree: usize, + mut rng: impl rand::RngCore, + ) -> Self { + let mut neighborings: Vec<DirectedNeighboring> = + vec![Vec::with_capacity(l_vertices_size); r_vertices_size]; + + (0..l_vertices_size).for_each(|l_index| { + let random_r_vertices = index::sample(&mut rng, r_vertices_size, expanding_degree); + + random_r_vertices + .iter() + .for_each(|r_index| neighborings[r_index].push(l_index)) + }); + + Self { + neighborings, + l_vertices_size, + r_vertices_size, + } + } + + #[inline(always)] + pub fn expander_mul<F: Field>( + &self, + l_vertices: &[F], + r_vertices: &mut [F], + ) -> OrionResult<()> { + if l_vertices.len() != self.l_vertices_size || r_vertices.len() != self.r_vertices_size { + return Err(OrionPCSError::ParameterUnmatchError); + } + + r_vertices + .iter_mut() + .zip(self.neighborings.iter()) + .for_each(|(ri, ni)| { + *ri = ni.iter().map(|&edge_i| l_vertices[edge_i]).sum(); + }); + + Ok(()) + } +} + +/* + * IMPLEMENTATIONS FOR ORION CODE FROM EXPANDER GRAPH + */ + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct OrionCodeParameter { + // parameter for graph g0, that maps n -> (\alpha_g0 n) + // \alpha_g0 should be ranging in (0, 1) + pub alpha_g0: f64, + pub degree_g0: usize, + + // parameter regarding graph generation for the code: + // stopping condition when message is too short for the recursive code + // in the next round. + pub length_threshold_g0s: usize, + + // parameter for graph g1, let the message in the middle has length L, + // then the graph g1 maps L -> (\alpha_g1 L) + pub alpha_g1: f64, + pub degree_g1: usize, + + // code's relateive distance + pub hamming_weight: f64, +} + +// NOTE: This instance of code derives from Orion paper Section 5. +pub const ORION_CODE_PARAMETER_INSTANCE: OrionCodeParameter = OrionCodeParameter { + alpha_g0: 0.33, + degree_g0: 6, + + length_threshold_g0s: 12, + + alpha_g1: 0.337, + degree_g1: 6, + + hamming_weight: 0.055, +}; + +#[allow(clippy::doc_lazy_continuation)] +/// ACKNOWLEDGEMENT: on alphabet being F2 binary case, we appreciate the help from +/// - Section 18 in essential coding theory +/// https://cse.buffalo.edu/faculty/atri/courses/coding-theory/book/web-coding-book.pdf +/// +/// - Notes from coding theory +/// https://www.cs.cmu.edu/~venkatg/teaching/codingtheory/notes/notes8.pdf +/// +/// - Druk-Ishai 2014 +/// https://dl.acm.org/doi/10.1145/2554797.2554815 + +#[derive(Clone, Debug, Default)] +pub struct OrionExpanderGraphPositioned { + pub graph: OrionExpanderGraph, + + pub input_starts: usize, + pub output_starts: usize, + pub output_ends: usize, +} + +impl OrionExpanderGraphPositioned { + #[inline(always)] + pub fn new( + input_starts: usize, + output_starts: usize, + output_ends: usize, + expanding_degree: usize, + mut rng: impl rand::RngCore, + ) -> Self { + Self { + graph: OrionExpanderGraph::new( + output_starts - input_starts, + output_ends - output_starts + 1, + expanding_degree, + &mut rng, + ), + input_starts, + output_starts, + output_ends, + } + } + + #[inline(always)] + pub fn expander_mul<F: Field>(&self, buffer: &mut [F], scratch: &mut [F]) -> OrionResult<()> { + let input_ref = &buffer[self.input_starts..self.output_starts]; + let output_ref = &mut scratch[self.output_starts..self.output_ends + 1]; + + self.graph.expander_mul(input_ref, output_ref)?; + buffer[self.output_starts..self.output_ends + 1].copy_from_slice(output_ref); + + Ok(()) + } +} + +// NOTE: The OrionCode here is representing an instance of Spielman code +// (Spielman96), that relies on 2 lists of expander graphs serving as +// error reduction code, and thus the linear error correction code derive +// from the parity matrices corresponding to these expander graphs. +#[derive(Clone, Debug, Default)] +pub struct OrionCode { + pub hamming_weight: f64, + + // empirical parameters for this instance of expander code on input/codeword + pub msg_len: usize, + pub codeword_len: usize, + + // g0s (affecting left side alphabets of the codeword) + // generated from the largest to the smallest + pub g0s: Vec<OrionExpanderGraphPositioned>, + + // g1s (affecting right side alphabets of the codeword) + // generated from the smallest to the largest + pub g1s: Vec<OrionExpanderGraphPositioned>, +} + +pub type OrionCodeword<F> = Vec<F>; + +impl OrionCode { + pub fn new(params: OrionCodeParameter, msg_len: usize, mut rng: impl rand::RngCore) -> Self { + // NOTE: sanity check - 1 / threshold_len > hamming_weight + // as was part of Druk-Ishai-14 distance proof by induction + assert!(1f64 / (params.length_threshold_g0s as f64) > params.hamming_weight); + + // NOTE: sanity check for both alpha_g0 and alpha_g1 + assert!(0f64 < params.alpha_g0 && params.alpha_g0 < 1f64); + assert!(0f64 < params.alpha_g1 && params.alpha_g1 < 1f64); + + // NOTE: the real deal of code instance generation starts here + let mut recursive_g0_output_starts: Vec<usize> = Vec::new(); + + let mut g0s: Vec<OrionExpanderGraphPositioned> = Vec::new(); + let mut g1s: Vec<OrionExpanderGraphPositioned> = Vec::new(); + + let mut g0_input_starts = 0; + let mut g0_output_starts = msg_len; + + while g0_output_starts - g0_input_starts > params.length_threshold_g0s { + let n = g0_output_starts - g0_input_starts; + let g0_output_len = (n as f64 * params.alpha_g0).round() as usize; + let degree_g0 = cmp::min(params.degree_g0, g0_output_len); + + g0s.push(OrionExpanderGraphPositioned::new( + g0_input_starts, + g0_output_starts, + g0_output_starts + g0_output_len - 1, + degree_g0, + &mut rng, + )); + + recursive_g0_output_starts.push(g0_output_starts); + + (g0_input_starts, g0_output_starts) = + (g0_output_starts, g0_output_starts + g0_output_len); + } + + // After g0s are generated, we generate g1s + let mut g1_output_starts = g0_output_starts; + + while let Some(g1_input_starts) = recursive_g0_output_starts.pop() { + let n = g1_output_starts - g1_input_starts; + let g1_output_len = (n as f64 * params.alpha_g1).round() as usize; + let degree_g1 = cmp::min(params.degree_g1, g1_output_len); + + g1s.push(OrionExpanderGraphPositioned::new( + g1_input_starts, + g1_output_starts, + g1_output_starts + g1_output_len - 1, + degree_g1, + &mut rng, + )); + + g1_output_starts += g1_output_len; + } + + let codeword_len = g1_output_starts; + Self { + hamming_weight: params.hamming_weight, + msg_len, + codeword_len, + g0s, + g1s, + } + } + + #[inline(always)] + pub fn code_len(&self) -> usize { + self.codeword_len + } + + #[inline(always)] + pub fn msg_len(&self) -> usize { + self.msg_len + } + + #[inline(always)] + pub fn hamming_weight(&self) -> f64 { + self.hamming_weight + } + + #[inline(always)] + pub fn encode<F: Field>(&self, msg: &[F]) -> OrionResult<OrionCodeword<F>> { + let mut codeword = vec![F::ZERO; self.code_len()]; + self.encode_in_place(msg, &mut codeword)?; + Ok(codeword) + } + + #[inline(always)] + pub fn encode_in_place<F: Field>(&self, msg: &[F], buffer: &mut [F]) -> OrionResult<()> { + if msg.len() != self.msg_len() || buffer.len() != self.code_len() { + return Err(OrionPCSError::ParameterUnmatchError); + } + + buffer[..self.msg_len()].copy_from_slice(msg); + let mut scratch = vec![F::ZERO; self.code_len()]; + + self.g0s + .iter() + .chain(self.g1s.iter()) + .try_for_each(|g| g.expander_mul(buffer, &mut scratch)) + } +} diff --git a/poly_commit/src/orion/pcs_impl.rs b/poly_commit/src/orion/pcs_impl.rs new file mode 100644 index 00000000..030b3b4e --- /dev/null +++ b/poly_commit/src/orion/pcs_impl.rs @@ -0,0 +1,300 @@ +use std::iter; +use std::ops::Mul; + +use arith::{Field, SimdField}; +use polynomials::{EqPolynomial, MultiLinearPoly}; +use transcript::Transcript; + +use crate::{traits::TensorCodeIOPPCS, PCS_SOUNDNESS_BITS}; + +use super::{ + linear_code::{OrionCode, OrionCodeParameter}, + utils::*, +}; + +impl OrionSRS { + pub fn new<F: Field>(num_variables: usize, code_instance: OrionCode) -> OrionResult<Self> { + let (_, msg_size) = Self::evals_shape::<F>(num_variables); + if msg_size != code_instance.msg_len() { + return Err(OrionPCSError::ParameterUnmatchError); + } + + // NOTE: we just move the instance of code, + // don't think the instance of expander code will be used elsewhere + Ok(Self { + num_variables, + code_instance, + }) + } + + pub fn from_random<F: Field>( + num_variables: usize, + code_param_instance: OrionCodeParameter, + mut rng: impl rand::RngCore, + ) -> Self { + let (_, msg_size) = Self::evals_shape::<F>(num_variables); + + Self { + num_variables, + code_instance: OrionCode::new(code_param_instance, msg_size, &mut rng), + } + } + + pub fn commit<F, ComPackF>( + &self, + poly: &MultiLinearPoly<F>, + scratch_pad: &mut OrionScratchPad<F, ComPackF>, + ) -> OrionResult<OrionCommitment> + where + F: Field, + ComPackF: SimdField<Scalar = F>, + { + let (row_num, msg_size) = Self::evals_shape::<F>(poly.get_num_vars()); + + // NOTE: pre transpose evaluations + let mut transposed_evaluations = poly.coeffs.clone(); + let mut scratch = vec![F::ZERO; 1 << poly.get_num_vars()]; + transpose_in_place(&mut transposed_evaluations, &mut scratch, row_num); + drop(scratch); + + // NOTE: SIMD pack each row of transposed matrix + assert_eq!(transposed_evaluations.len() % ComPackF::PACK_SIZE, 0); + let mut packed_evals: Vec<ComPackF> = transposed_evaluations + .chunks(ComPackF::PACK_SIZE) + .map(SimdField::pack) + .collect(); + drop(transposed_evaluations); + + // NOTE: transpose back to rows of evaluations, but packed + let packed_rows = row_num / ComPackF::PACK_SIZE; + assert_eq!(row_num % ComPackF::PACK_SIZE, 0); + + let mut scratch = vec![ComPackF::ZERO; packed_rows * msg_size]; + transpose_in_place(&mut packed_evals, &mut scratch, msg_size); + drop(scratch); + + // NOTE: packed codeword buffer and encode over packed field + let mut packed_interleaved_codewords = + vec![ComPackF::ZERO; packed_rows * self.codeword_len()]; + packed_evals + .chunks(msg_size) + .zip(packed_interleaved_codewords.chunks_mut(self.codeword_len())) + .try_for_each(|(evals, codeword)| { + self.code_instance.encode_in_place(evals, codeword) + })?; + drop(packed_evals); + + // NOTE: transpose codeword s.t., the matrix has codewords being columns + let mut scratch = vec![ComPackF::ZERO; packed_rows * self.codeword_len()]; + transpose_in_place(&mut packed_interleaved_codewords, &mut scratch, packed_rows); + drop(scratch); + + // NOTE: commit the interleaved codeword + // we just directly commit to the packed field elements to leaves + // Also note, when codeword is not power of 2 length, pad to nearest po2 + // to commit by merkle tree + if !packed_interleaved_codewords.len().is_power_of_two() { + let aligned_po2_len = packed_interleaved_codewords.len().next_power_of_two(); + packed_interleaved_codewords.resize(aligned_po2_len, ComPackF::ZERO); + } + scratch_pad.interleaved_alphabet_commitment = + tree::Tree::compact_new_with_packed_field_elems::<F, ComPackF>( + packed_interleaved_codewords, + ); + + Ok(scratch_pad.interleaved_alphabet_commitment.root()) + } + + pub fn open<F, EvalF, ComPackF, OpenPackF, T>( + &self, + poly: &MultiLinearPoly<F>, + point: &[EvalF], + transcript: &mut T, + scratch_pad: &OrionScratchPad<F, ComPackF>, + ) -> (EvalF, OrionProof<EvalF>) + where + F: Field, + EvalF: Field + From<F> + Mul<F, Output = EvalF>, + ComPackF: SimdField<Scalar = F>, + OpenPackF: SimdField<Scalar = F>, + T: Transcript<EvalF>, + { + let (row_num, msg_size) = Self::evals_shape::<F>(poly.get_num_vars()); + let num_of_vars_in_msg = msg_size.ilog2() as usize; + + // NOTE: transpose evaluations for linear combinations in evaulation/proximity tests + let mut transposed_evaluations = poly.coeffs.clone(); + let mut scratch = vec![F::ZERO; 1 << poly.get_num_vars()]; + transpose_in_place(&mut transposed_evaluations, &mut scratch, row_num); + drop(scratch); + + // NOTE: SIMD pack each row of transposed matrix + assert_eq!(transposed_evaluations.len() % OpenPackF::PACK_SIZE, 0); + let packed_evals: Vec<OpenPackF> = transposed_evaluations + .chunks(OpenPackF::PACK_SIZE) + .map(OpenPackF::pack) + .collect(); + drop(transposed_evaluations); + + // NOTE: declare the look up tables for column sums + let packed_rows = row_num / OpenPackF::PACK_SIZE; + let mut luts = SubsetSumLUTs::new(OpenPackF::PACK_SIZE, packed_rows); + + // NOTE: working on evaluation response of tensor code IOP based PCS + let mut eval_row = vec![EvalF::ZERO; msg_size]; + + let eq_col_coeffs = EqPolynomial::build_eq_x_r(&point[num_of_vars_in_msg..]); + luts.build(&eq_col_coeffs); + + packed_evals + .chunks(packed_rows) + .zip(eval_row.iter_mut()) + .for_each(|(p_col, res)| *res = luts.lookup_and_sum(p_col)); + + // NOTE: draw random linear combination out + // and compose proximity response(s) of tensor code IOP based PCS + let proximity_test_num = self.proximity_repetitions::<EvalF>(PCS_SOUNDNESS_BITS); + let mut proximity_rows = vec![vec![EvalF::ZERO; msg_size]; proximity_test_num]; + + proximity_rows.iter_mut().for_each(|row_buffer| { + let random_coeffs = transcript.generate_challenge_field_elements(row_num); + luts.build(&random_coeffs); + + packed_evals + .chunks(packed_rows) + .zip(row_buffer.iter_mut()) + .for_each(|(p_col, res)| *res = luts.lookup_and_sum(p_col)); + }); + drop(luts); + + // NOTE: working on evaluation on top of evaluation response + let mut scratch = vec![EvalF::ZERO; msg_size]; + let eval = MultiLinearPoly::evaluate_with_buffer( + &eval_row, + &point[..num_of_vars_in_msg], + &mut scratch, + ); + drop(scratch); + + // NOTE: MT opening for point queries + let leaf_range = row_num / tree::leaf_adic::<F>(); + let query_num = self.query_complexity(PCS_SOUNDNESS_BITS); + let query_indices = transcript.generate_challenge_index_vector(query_num); + let query_openings = query_indices + .iter() + .map(|qi| { + let index = *qi % self.codeword_len(); + let left = index * leaf_range; + let right = left + leaf_range - 1; + + scratch_pad + .interleaved_alphabet_commitment + .range_query(left, right) + }) + .collect(); + + ( + eval, + OrionProof { + eval_row, + proximity_rows, + query_openings, + }, + ) + } + + pub fn verify<F, EvalF, ComPackF, OpenPackF, T>( + &self, + commitment: &OrionCommitment, + point: &[EvalF], + evaluation: EvalF, + proof: &OrionProof<EvalF>, + transcript: &mut T, + ) -> bool + where + F: Field, + EvalF: Field + From<F> + Mul<F, Output = EvalF>, + ComPackF: SimdField<Scalar = F>, + OpenPackF: SimdField<Scalar = F>, + T: Transcript<EvalF>, + { + let (row_num, msg_size) = Self::evals_shape::<F>(point.len()); + let num_of_vars_in_msg = msg_size.ilog2() as usize; + + // NOTE: working on evaluation response, evaluate the rest of the response + let mut scratch = vec![EvalF::ZERO; msg_size]; + let final_eval = MultiLinearPoly::evaluate_with_buffer( + &proof.eval_row, + &point[..num_of_vars_in_msg], + &mut scratch, + ); + if final_eval != evaluation { + return false; + } + + // NOTE: working on proximity responses, draw random linear combinations + // then draw query points from fiat shamir transcripts + let proximity_test_num = self.proximity_repetitions::<EvalF>(PCS_SOUNDNESS_BITS); + let random_linear_combinations: Vec<Vec<EvalF>> = (0..proximity_test_num) + .map(|_| transcript.generate_challenge_field_elements(row_num)) + .collect(); + let query_num = self.query_complexity(PCS_SOUNDNESS_BITS); + let query_indices = transcript.generate_challenge_index_vector(query_num); + + // NOTE: check consistency in MT in the opening trees and against the commitment tree + let leaf_range = row_num / tree::leaf_adic::<F>(); + let mt_consistency = + query_indices + .iter() + .zip(proof.query_openings.iter()) + .all(|(&qi, range_path)| { + let index = qi % self.codeword_len(); + range_path.verify(commitment) && index == range_path.left / leaf_range + }); + if !mt_consistency { + return false; + } + + // NOTE: prepare the interleaved alphabets from the MT paths, + // but pack them back into look up table acceptable formats + let packed_interleaved_alphabets: Vec<_> = proof + .query_openings + .iter() + .map(|p| -> Vec<_> { + p.unpack_field_elems::<F, ComPackF>() + .chunks(OpenPackF::PACK_SIZE) + .map(OpenPackF::pack) + .collect() + }) + .collect(); + + // NOTE: encode the proximity/evaluation responses, + // check againts all challenged indices by check alphabets against + // linear combined interleaved alphabet + let mut luts = SubsetSumLUTs::new(OpenPackF::PACK_SIZE, row_num / OpenPackF::PACK_SIZE); + assert_eq!(row_num % OpenPackF::PACK_SIZE, 0); + + let eq_linear_combination = EqPolynomial::build_eq_x_r(&point[num_of_vars_in_msg..]); + random_linear_combinations + .iter() + .zip(proof.proximity_rows.iter()) + .chain(iter::once((&eq_linear_combination, &proof.eval_row))) + .all(|(rl, msg)| { + let codeword = match self.code_instance.encode(msg) { + Ok(c) => c, + _ => return false, + }; + + luts.build(rl); + + query_indices + .iter() + .zip(packed_interleaved_alphabets.iter()) + .all(|(&qi, interleaved_alphabet)| { + let index = qi % self.codeword_len(); + let alphabet = luts.lookup_and_sum(interleaved_alphabet); + alphabet == codeword[index] + }) + }) + } +} diff --git a/poly_commit/src/orion/serde.rs b/poly_commit/src/orion/serde.rs new file mode 100644 index 00000000..46f498a2 --- /dev/null +++ b/poly_commit/src/orion/serde.rs @@ -0,0 +1,124 @@ +use std::io::{Read, Write}; + +use arith::{Field, FieldSerde, FieldSerdeResult}; + +use crate::orion::{ + linear_code::*, + utils::{OrionProof, OrionSRS}, +}; + +impl FieldSerde for OrionExpanderGraph { + const SERIALIZED_SIZE: usize = unimplemented!(); + + fn serialize_into<W: Write>(&self, mut writer: W) -> FieldSerdeResult<()> { + self.l_vertices_size.serialize_into(&mut writer)?; + self.r_vertices_size.serialize_into(&mut writer)?; + self.neighborings.serialize_into(&mut writer)?; + Ok(()) + } + + fn deserialize_from<R: Read>(mut reader: R) -> FieldSerdeResult<Self> { + let l_vertices_size = usize::deserialize_from(&mut reader)?; + let r_vertices_size = usize::deserialize_from(&mut reader)?; + let neighborings: Vec<DirectedNeighboring> = Vec::deserialize_from(&mut reader)?; + Ok(Self { + l_vertices_size, + r_vertices_size, + neighborings, + }) + } +} + +impl FieldSerde for OrionExpanderGraphPositioned { + const SERIALIZED_SIZE: usize = unimplemented!(); + + fn serialize_into<W: Write>(&self, mut writer: W) -> FieldSerdeResult<()> { + self.input_starts.serialize_into(&mut writer)?; + self.output_starts.serialize_into(&mut writer)?; + self.output_ends.serialize_into(&mut writer)?; + self.graph.serialize_into(&mut writer)?; + Ok(()) + } + + fn deserialize_from<R: Read>(mut reader: R) -> FieldSerdeResult<Self> { + let input_starts = usize::deserialize_from(&mut reader)?; + let output_starts = usize::deserialize_from(&mut reader)?; + let output_ends = usize::deserialize_from(&mut reader)?; + let graph = OrionExpanderGraph::deserialize_from(&mut reader)?; + Ok(Self { + input_starts, + output_starts, + output_ends, + graph, + }) + } +} + +impl FieldSerde for OrionCode { + const SERIALIZED_SIZE: usize = unimplemented!(); + + fn serialize_into<W: Write>(&self, mut writer: W) -> FieldSerdeResult<()> { + self.hamming_weight.serialize_into(&mut writer)?; + self.msg_len.serialize_into(&mut writer)?; + self.codeword_len.serialize_into(&mut writer)?; + self.g0s.serialize_into(&mut writer)?; + self.g1s.serialize_into(&mut writer)?; + Ok(()) + } + + fn deserialize_from<R: Read>(mut reader: R) -> FieldSerdeResult<Self> { + let hamming_weight = f64::deserialize_from(&mut reader)?; + let msg_len = usize::deserialize_from(&mut reader)?; + let codeword_len = usize::deserialize_from(&mut reader)?; + let g0s: Vec<OrionExpanderGraphPositioned> = Vec::deserialize_from(&mut reader)?; + let g1s: Vec<OrionExpanderGraphPositioned> = Vec::deserialize_from(&mut reader)?; + Ok(Self { + hamming_weight, + msg_len, + codeword_len, + g0s, + g1s, + }) + } +} + +impl FieldSerde for OrionSRS { + const SERIALIZED_SIZE: usize = unimplemented!(); + + fn serialize_into<W: Write>(&self, mut writer: W) -> FieldSerdeResult<()> { + self.num_variables.serialize_into(&mut writer)?; + self.code_instance.serialize_into(&mut writer)?; + Ok(()) + } + + fn deserialize_from<R: Read>(mut reader: R) -> FieldSerdeResult<Self> { + let num_variables = usize::deserialize_from(&mut reader)?; + let code_instance = OrionCode::deserialize_from(&mut reader)?; + Ok(Self { + num_variables, + code_instance, + }) + } +} + +impl<F: Field> FieldSerde for OrionProof<F> { + const SERIALIZED_SIZE: usize = unimplemented!(); + + fn serialize_into<W: Write>(&self, mut writer: W) -> FieldSerdeResult<()> { + self.eval_row.serialize_into(&mut writer)?; + self.proximity_rows.serialize_into(&mut writer)?; + self.query_openings.serialize_into(&mut writer)?; + Ok(()) + } + + fn deserialize_from<R: Read>(mut reader: R) -> FieldSerdeResult<Self> { + let eval_row: Vec<F> = Vec::deserialize_from(&mut reader)?; + let proximity_rows: Vec<Vec<F>> = Vec::deserialize_from(&mut reader)?; + let query_openings: Vec<tree::RangePath> = Vec::deserialize_from(&mut reader)?; + Ok(OrionProof { + eval_row, + proximity_rows, + query_openings, + }) + } +} diff --git a/poly_commit/src/orion/tests.rs b/poly_commit/src/orion/tests.rs new file mode 100644 index 00000000..41309c7c --- /dev/null +++ b/poly_commit/src/orion/tests.rs @@ -0,0 +1,199 @@ +use std::ops::Mul; + +use arith::{Field, SimdField}; +use ark_std::test_rng; +use gf2::{GF2x128, GF2x64, GF2x8, GF2}; +use gf2_128::GF2_128; +use polynomials::MultiLinearPoly; +use transcript::{BytesHashTranscript, Keccak256hasher, Transcript}; + +use crate::{ + orion::{ + linear_code::{OrionCode, ORION_CODE_PARAMETER_INSTANCE}, + utils::*, + }, + traits::TensorCodeIOPPCS, +}; + +fn column_combination<F, PackF>(mat: &[F], combination: &[F]) -> Vec<F> +where + F: Field, + PackF: SimdField<Scalar = F>, +{ + assert_eq!(combination.len() % PackF::PACK_SIZE, 0); + + let mut luts = SubsetSumLUTs::new(PackF::PACK_SIZE, combination.len() / PackF::PACK_SIZE); + luts.build(combination); + + mat.chunks(combination.len()) + .map(|p_col| { + let packed: Vec<_> = p_col.chunks(PackF::PACK_SIZE).map(PackF::pack).collect(); + luts.lookup_and_sum(&packed) + }) + .collect() +} + +fn test_orion_code_generic<F, PackF>(msg_len: usize) +where + F: Field, + PackF: SimdField<Scalar = F>, +{ + let mut rng = test_rng(); + + let orion_code = OrionCode::new(ORION_CODE_PARAMETER_INSTANCE, msg_len, &mut rng); + + let row_num = 1024 / F::FIELD_SIZE; + let weights: Vec<_> = (0..row_num).map(|_| F::random_unsafe(&mut rng)).collect(); + + // NOTE: generate message and codeword in the slice buffer + let mut message_mat = vec![F::ZERO; row_num * orion_code.msg_len()]; + let mut codeword_mat = vec![F::ZERO; row_num * orion_code.code_len()]; + + message_mat + .chunks_mut(orion_code.msg_len()) + .zip(codeword_mat.chunks_mut(orion_code.code_len())) + .for_each(|(msg, codeword)| { + msg.fill_with(|| F::random_unsafe(&mut rng)); + orion_code.encode_in_place(msg, codeword).unwrap() + }); + + // NOTE: transpose message and codeword matrix + let mut message_scratch = vec![F::ZERO; row_num * orion_code.msg_len()]; + transpose_in_place(&mut message_mat, &mut message_scratch, row_num); + drop(message_scratch); + + let mut codeword_scratch = vec![F::ZERO; row_num * orion_code.code_len()]; + transpose_in_place(&mut codeword_mat, &mut codeword_scratch, row_num); + drop(codeword_scratch); + + // NOTE: message and codeword matrix linear combination with weights + let msg_linear_combined = column_combination::<F, PackF>(&message_mat, &weights); + let codeword_linear_combined = column_combination::<F, PackF>(&codeword_mat, &weights); + + let codeword_computed = orion_code.encode(&msg_linear_combined).unwrap(); + + assert_eq!(codeword_linear_combined, codeword_computed); +} + +#[test] +fn test_orion_code() { + (5..=15).for_each(|num_vars| { + let msg_len = 1usize << num_vars; + test_orion_code_generic::<GF2, GF2x8>(msg_len); + }); +} + +fn dumb_commit<F, ComPackF>(orion_srs: &OrionSRS, poly: &MultiLinearPoly<F>) -> OrionCommitment +where + F: Field, + ComPackF: SimdField<Scalar = F>, +{ + let (row_num, msg_size) = OrionSRS::evals_shape::<F>(poly.get_num_vars()); + + let mut interleaved_codewords: Vec<_> = poly + .coeffs + .chunks(msg_size) + .flat_map(|msg| orion_srs.code_instance.encode(&msg).unwrap()) + .collect(); + + let mut scratch = vec![F::ZERO; row_num * orion_srs.codeword_len()]; + transpose_in_place(&mut interleaved_codewords, &mut scratch, row_num); + drop(scratch); + + if !interleaved_codewords.len().is_power_of_two() { + let aligned_po2_len = interleaved_codewords.len().next_power_of_two(); + interleaved_codewords.resize(aligned_po2_len, F::default()); + } + + let interleaved_alphabet_tree = + tree::Tree::compact_new_with_field_elems::<F, ComPackF>(interleaved_codewords); + + interleaved_alphabet_tree.root() +} + +fn test_orion_commit_consistency_generic<F, ComPackF>(num_vars: usize) +where + F: Field, + ComPackF: SimdField<Scalar = F>, +{ + let mut rng = test_rng(); + + let random_poly = MultiLinearPoly::<F>::random(num_vars, &mut rng); + let orion_pcs = OrionSRS::from_random::<F>(num_vars, ORION_CODE_PARAMETER_INSTANCE, &mut rng); + + let mut orion_scratch = OrionScratchPad::default(); + + let real_commitment = orion_pcs + .commit::<F, ComPackF>(&random_poly, &mut orion_scratch) + .unwrap(); + + let dumb_commitment = dumb_commit::<F, ComPackF>(&orion_pcs, &random_poly); + + assert_eq!(real_commitment, dumb_commitment); +} + +#[test] +fn test_orion_commit_consistency() { + (19..=25).for_each(|num_vars| { + test_orion_commit_consistency_generic::<GF2, GF2x64>(num_vars); + test_orion_commit_consistency_generic::<GF2, GF2x128>(num_vars); + }); +} + +fn test_orion_pcs_full_e2e_generics<F, EvalF, ComPackF, OpenPackF>(num_vars: usize) +where + F: Field, + EvalF: Field + Mul<F, Output = EvalF> + From<F>, + ComPackF: SimdField<Scalar = F>, + OpenPackF: SimdField<Scalar = F>, +{ + let mut rng = test_rng(); + + let random_poly = MultiLinearPoly::<F>::random(num_vars, &mut rng); + let random_poly_ext = MultiLinearPoly::new( + random_poly + .coeffs + .iter() + .cloned() + .map(EvalF::from) + .collect(), + ); + let random_point: Vec<_> = (0..num_vars) + .map(|_| EvalF::random_unsafe(&mut rng)) + .collect(); + let expected_eval = random_poly_ext.evaluate_jolt(&random_point); + + let mut transcript: BytesHashTranscript<EvalF, Keccak256hasher> = BytesHashTranscript::new(); + let mut transcript_cloned = transcript.clone(); + + let orion_srs = OrionSRS::from_random::<F>(num_vars, ORION_CODE_PARAMETER_INSTANCE, &mut rng); + + let mut orion_scratch = OrionScratchPad::default(); + + let orion_commitment = orion_srs + .commit::<F, ComPackF>(&random_poly, &mut orion_scratch) + .unwrap(); + + let (_, opening) = orion_srs.open::<F, EvalF, ComPackF, OpenPackF, _>( + &random_poly, + &random_point, + &mut transcript, + &orion_scratch, + ); + + assert!(orion_srs.verify::<F, EvalF, ComPackF, OpenPackF, _>( + &orion_commitment, + &random_point, + expected_eval, + &opening, + &mut transcript_cloned + )); +} + +#[test] +fn test_orion_pcs_full_e2e() { + (19..=25).for_each(|num_vars| { + test_orion_pcs_full_e2e_generics::<GF2, GF2_128, GF2x64, GF2x8>(num_vars); + test_orion_pcs_full_e2e_generics::<GF2, GF2_128, GF2x128, GF2x8>(num_vars); + }); +} diff --git a/poly_commit/src/orion/utils.rs b/poly_commit/src/orion/utils.rs new file mode 100644 index 00000000..4e104c48 --- /dev/null +++ b/poly_commit/src/orion/utils.rs @@ -0,0 +1,167 @@ +use std::marker::PhantomData; + +use arith::{Field, FieldSerdeError, SimdField}; +use thiserror::Error; + +use crate::{traits::TensorCodeIOPPCS, StructuredReferenceString}; + +use super::linear_code::OrionCode; + +/* + * PCS ERROR AND RESULT SETUP + */ + +#[derive(Debug, Error)] +pub enum OrionPCSError { + #[error("Orion PCS linear code parameter unmatch error")] + ParameterUnmatchError, + + #[error("field serde error")] + SerializationError(#[from] FieldSerdeError), +} + +pub type OrionResult<T> = std::result::Result<T, OrionPCSError>; + +/* + * RELEVANT TYPES SETUP + */ + +#[derive(Clone, Debug)] +pub struct OrionSRS { + pub num_variables: usize, + pub code_instance: OrionCode, +} + +impl TensorCodeIOPPCS for OrionSRS { + fn codeword_len(&self) -> usize { + self.code_instance.code_len() + } + + fn hamming_weight(&self) -> f64 { + self.code_instance.hamming_weight() + } +} + +impl StructuredReferenceString for OrionSRS { + type PKey = OrionSRS; + + type VKey = OrionSRS; + + fn into_keys(self) -> (Self::PKey, Self::VKey) { + (self.clone(), self.clone()) + } +} + +pub type OrionCommitment = tree::Node; + +// TODO: maybe prepare memory API for transpose in commit and open? +#[derive(Clone, Debug, Default)] +pub struct OrionScratchPad<F, ComPackF> +where + F: Field, + ComPackF: SimdField<Scalar = F>, +{ + pub interleaved_alphabet_commitment: tree::Tree, + pub _phantom: PhantomData<ComPackF>, +} + +#[derive(Clone, Debug, Default)] +pub struct OrionProof<EvalF: Field> { + pub eval_row: Vec<EvalF>, + pub proximity_rows: Vec<Vec<EvalF>>, + pub query_openings: Vec<tree::RangePath>, +} + +/* + * IMPLEMENTATIONS FOR MATRIX TRANSPOSE + */ + +pub(crate) const fn cache_batch_size<F: Sized>() -> usize { + const CACHE_SIZE: usize = 1 << 16; + CACHE_SIZE / size_of::<F>() +} + +#[inline(always)] +pub(crate) fn transpose_in_place<F: Field>(mat: &mut [F], scratch: &mut [F], row_num: usize) { + let col_num = mat.len() / row_num; + let batch_size = cache_batch_size::<F>(); + + mat.chunks(batch_size) + .enumerate() + .for_each(|(i, ith_batch)| { + let batch_srt = batch_size * i; + + ith_batch.iter().enumerate().for_each(|(j, &elem_j)| { + let src = batch_srt + j; + let dst = (src / col_num) + (src % col_num) * row_num; + + scratch[dst] = elem_j; + }) + }); + + mat.copy_from_slice(scratch); +} + +/* + * LINEAR OPERATIONS + */ + +pub struct SubsetSumLUTs<F: Field> { + pub entry_bits: usize, + pub tables: Vec<Vec<F>>, +} + +impl<F: Field> SubsetSumLUTs<F> { + #[inline] + pub fn new(entry_bits: usize, table_num: usize) -> Self { + assert!(entry_bits > 0 && table_num > 0); + + Self { + entry_bits, + tables: vec![vec![F::ZERO; 1 << entry_bits]; table_num], + } + } + + #[inline] + pub fn build(&mut self, weights: &[F]) { + assert_eq!(weights.len() % self.entry_bits, 0); + assert_eq!(weights.len() / self.entry_bits, self.tables.len()); + + self.tables.iter_mut().for_each(|lut| lut.fill(F::ZERO)); + + // NOTE: we are assuming that the table is for {0, 1}-linear combination + self.tables + .iter_mut() + .zip(weights.chunks(self.entry_bits)) + .for_each(|(lut_i, sub_weights)| { + sub_weights.iter().enumerate().for_each(|(i, weight_i)| { + let bit_mask = 1 << (self.entry_bits - i - 1); + lut_i.iter_mut().enumerate().for_each(|(bit_map, li)| { + if bit_map & bit_mask == bit_mask { + *li += weight_i; + } + }); + }); + }); + } + + #[inline] + pub fn lookup_and_sum<BitF, EntryF>(&self, indices: &[EntryF]) -> F + where + BitF: Field, + EntryF: SimdField<Scalar = BitF>, + { + // NOTE: at least the entry field elem should have a matching field size + // and the the entry field being a SIMD field with same packing size as + // the bits for the table entry + assert_eq!(EntryF::FIELD_SIZE, 1); + assert_eq!(EntryF::PACK_SIZE, self.entry_bits); + assert_eq!(indices.len(), self.tables.len()); + + self.tables + .iter() + .zip(indices.iter()) + .map(|(t_i, index)| t_i[index.as_u32_unchecked() as usize]) + .sum() + } +} diff --git a/poly_commit/src/traits.rs b/poly_commit/src/traits.rs index 155983d8..daa25895 100644 --- a/poly_commit/src/traits.rs +++ b/poly_commit/src/traits.rs @@ -143,3 +143,36 @@ pub trait PCSForExpanderGKR<C: GKRFieldConfig, T: Transcript<C::ChallengeField>> opening: &Self::Opening, ) -> bool; } + +pub(crate) trait TensorCodeIOPPCS { + fn codeword_len(&self) -> usize; + + fn hamming_weight(&self) -> f64; + + fn evals_shape<F: Field>(num_vars: usize) -> (usize, usize) { + let elems_for_smallest_tree = tree::leaf_adic::<F>() * 2; + + let row_num: usize = elems_for_smallest_tree; + let msg_size: usize = (1 << num_vars) / row_num; + + (row_num, msg_size) + } + + fn query_complexity(&self, soundness_bits: usize) -> usize { + // NOTE: use Ligero (AHIV22) or Avg-case dist to a code (BKS18) + // version of avg case dist in unique decoding technique. + let avg_case_dist = self.hamming_weight() / 3f64; + let sec_bits = -(1f64 - avg_case_dist).log2(); + + (soundness_bits as f64 / sec_bits).ceil() as usize + } + + fn proximity_repetitions<F: Field>(&self, soundness_bits: usize) -> usize { + // NOTE: use Ligero (AHIV22) or Avg-case dist to a code (BKS18) + // version of avg case dist in unique decoding technique. + // Here is the probability union bound + let single_run_soundness_bits = F::FIELD_SIZE - self.codeword_len().ilog2() as usize; + + (soundness_bits as f64 / single_run_soundness_bits as f64).ceil() as usize + } +} diff --git a/poly_commit/tests/common.rs b/poly_commit/tests/common.rs index bf104b34..541c44aa 100644 --- a/poly_commit/tests/common.rs +++ b/poly_commit/tests/common.rs @@ -64,6 +64,8 @@ pub fn test_gkr_pcs< for xx in xs { let ExpanderGKRChallenge { x, x_simd, x_mpi } = xx; + + transcript.lock_proof(); let opening = P::open( params, mpi_config, @@ -73,11 +75,13 @@ pub fn test_gkr_pcs< transcript, &mut scratch_pad, ); + transcript.unlock_proof(); if mpi_config.is_root() { // this will always pass for RawExpanderGKR, so make sure it is correct let v = RawExpanderGKR::<C, T>::eval(&coeffs_gathered, x, x_simd, x_mpi); + transcript.lock_proof(); assert!(P::verify( params, mpi_config, @@ -88,6 +92,7 @@ pub fn test_gkr_pcs< transcript, &opening )); + transcript.unlock_proof(); } } } From 069444691ca413de5a01dabc1f5d7b1fb8c3c4b8 Mon Sep 17 00:00:00 2001 From: Hang Su <tonyfloater@gmail.com> Date: Fri, 22 Nov 2024 00:03:32 -0500 Subject: [PATCH 02/65] rust compilation cache again? --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7ec67aab..3f5ecea0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -85,7 +85,7 @@ jobs: - uses: Swatinem/rust-cache@v2 with: # The prefix cache key, this can be changed to start a new cache manually. - prefix-key: "mpi-v5.0.5" # update me if brew formula changes to a new version + prefix-key: "mpi-v5.0.6_" # update me if brew formula changes to a new version - name: Set RUSTFLAGS for AVX if: matrix.feature != '' run: echo "RUSTFLAGS=$RUSTFLAGS -C target-feature=+${{ matrix.feature }}" >> $GITHUB_ENV From 6ed985a23b0e6e44d7fbb4d214a778fea049ce03 Mon Sep 17 00:00:00 2001 From: Hang Su <tonyfloater@gmail.com> Date: Fri, 22 Nov 2024 20:18:06 -0500 Subject: [PATCH 03/65] orion interface changes, working on aligning with pcs trait --- arith/gf2/src/gf2x128.rs | 2 + arith/gf2/src/tests.rs | 25 +- arith/gf2_128/src/tests.rs | 16 +- arith/src/simd_field.rs | 12 + poly_commit/benches/orion.rs | 31 +- poly_commit/src/orion.rs | 2 + poly_commit/src/orion/pcs_impl.rs | 519 ++++++++++++++---------------- poly_commit/src/orion/tests.rs | 35 +- poly_commit/src/orion/utils.rs | 31 +- 9 files changed, 336 insertions(+), 337 deletions(-) diff --git a/arith/gf2/src/gf2x128.rs b/arith/gf2/src/gf2x128.rs index 97da6594..b5ee2368 100644 --- a/arith/gf2/src/gf2x128.rs +++ b/arith/gf2/src/gf2x128.rs @@ -34,6 +34,7 @@ impl SimdField for GF2x128 { let mut packed_to_gf2x64 = [GF2x64::ZERO; Self::PACK_SIZE / GF2x64::PACK_SIZE]; packed_to_gf2x64 .iter_mut() + .rev() .zip(base_vec.chunks(GF2x64::PACK_SIZE)) .for_each(|(gf2x64, pack)| *gf2x64 = GF2x64::pack(pack)); @@ -47,6 +48,7 @@ impl SimdField for GF2x128 { packed_to_gf2x64 .iter() + .rev() .flat_map(|packed| packed.unpack()) .collect() } diff --git a/arith/gf2/src/tests.rs b/arith/gf2/src/tests.rs index e2c5f2b2..1fdac690 100644 --- a/arith/gf2/src/tests.rs +++ b/arith/gf2/src/tests.rs @@ -1,7 +1,9 @@ use ark_std::test_rng; use std::io::Cursor; -use arith::{random_field_tests, random_inversion_tests, random_simd_field_tests, Field}; +use arith::{ + random_field_tests, random_inversion_tests, random_simd_field_tests, Field, SimdField, +}; use crate::{GF2x128, GF2x64, GF2x8, GF2}; @@ -25,8 +27,10 @@ fn test_simd_field() { random_simd_field_tests::<GF2x128>("Vectorized GF2 len 128".to_string()); } -fn custom_serde_vectorize_gf2<F: Field>() { - let a = F::from(0); +fn custom_serde_vectorize_gf2<F: SimdField<Scalar = GF2>>() { + let mut rng = test_rng(); + + let a = F::random_unsafe(&mut rng); let mut buffer = vec![]; assert!(a.serialize_into(&mut buffer).is_ok()); let mut cursor = Cursor::new(buffer); @@ -34,6 +38,21 @@ fn custom_serde_vectorize_gf2<F: Field>() { assert!(b.is_ok()); let b = b.unwrap(); assert_eq!(a, b); + + let mut random_packed = vec![GF2x8::ZERO; F::PACK_SIZE / GF2x8::PACK_SIZE]; + random_packed + .iter_mut() + .for_each(|v| *v = GF2x8::random_unsafe(&mut rng)); + + let actual_packed = F::pack_from_simd(&random_packed); + let expected_packed = F::pack( + &random_packed + .iter() + .flat_map(|v| v.unpack()) + .collect::<Vec<_>>(), + ); + + assert_eq!(actual_packed, expected_packed); } #[test] diff --git a/arith/gf2_128/src/tests.rs b/arith/gf2_128/src/tests.rs index 43be77ff..232d8dba 100644 --- a/arith/gf2_128/src/tests.rs +++ b/arith/gf2_128/src/tests.rs @@ -1,8 +1,6 @@ -use std::io::Cursor; - use arith::{ random_extension_field_tests, random_field_tests, random_inversion_tests, - random_simd_field_tests, FieldSerde, + random_simd_field_tests, }; use ark_std::test_rng; @@ -28,18 +26,6 @@ fn test_ext_field() { random_inversion_tests::<GF2_128, _>(&mut rng, "GF2_128".to_string()); } -#[test] -fn test_custom_serde_vectorize_gf2_128() { - let a = GF2_128::from(0); - let mut buffer = vec![]; - assert!(a.serialize_into(&mut buffer).is_ok()); - let mut cursor = Cursor::new(buffer); - let b = GF2_128::deserialize_from(&mut cursor); - assert!(b.is_ok()); - let b = b.unwrap(); - assert_eq!(a, b); -} - #[cfg(target_arch = "aarch64")] #[test] // known answer test, results cross-checked with avx_gf2_128 diff --git a/arith/src/simd_field.rs b/arith/src/simd_field.rs index f5e2a75f..d535f41c 100644 --- a/arith/src/simd_field.rs +++ b/arith/src/simd_field.rs @@ -14,6 +14,18 @@ pub trait SimdField: From<Self::Scalar> + Field { /// pack a vec of scalar field into self fn pack(base_vec: &[Self::Scalar]) -> Self; + /// pack a vec of simd field with same scalar field into self + fn pack_from_simd<PackF>(simd_vec: &[PackF]) -> Self + where + PackF: SimdField<Scalar = Self::Scalar>, + { + assert_eq!(simd_vec.len() * PackF::PACK_SIZE, Self::PACK_SIZE); + let mut temp: Vec<_> = simd_vec.to_vec(); + temp.reverse(); + + unsafe { *(temp.as_ptr() as *const Self) } + } + /// unpack into a vector. fn unpack(&self) -> Vec<Self::Scalar>; } diff --git a/poly_commit/benches/orion.rs b/poly_commit/benches/orion.rs index d538a746..ccd2dd2e 100644 --- a/poly_commit/benches/orion.rs +++ b/poly_commit/benches/orion.rs @@ -5,7 +5,7 @@ use ark_std::test_rng; use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion}; use gf2::{GF2x128, GF2x8, GF2}; use gf2_128::GF2_128; -use poly_commit::{OrionSRS, OrionScratchPad, ORION_CODE_PARAMETER_INSTANCE}; +use poly_commit::*; use polynomials::MultiLinearPoly; use transcript::{BytesHashTranscript, Keccak256hasher, Transcript}; use tynm::type_name; @@ -28,26 +28,17 @@ fn committing_benchmark_helper<F, EvalF, ComPackF, OpenPackF, T>( )); let mut rng = test_rng(); - let mut orion_scratch = OrionScratchPad::default(); + let mut scratch_pad = OrionScratchPad::<F, ComPackF>::default(); for num_vars in lowest_num_vars..=highest_num_vars { let poly = MultiLinearPoly::<F>::random(num_vars, &mut rng); - let orion_srs = - OrionSRS::from_random::<F>(num_vars, ORION_CODE_PARAMETER_INSTANCE, &mut rng); + let srs = OrionSRS::from_random::<F>(num_vars, ORION_CODE_PARAMETER_INSTANCE, &mut rng); group .bench_function( BenchmarkId::new(format!("{num_vars} variables"), num_vars), - |b| { - b.iter(|| { - _ = black_box( - orion_srs - .commit::<F, ComPackF>(&poly, &mut orion_scratch) - .unwrap(), - ) - }) - }, + |b| b.iter(|| _ = black_box(orion_commit(&srs, &poly, &mut scratch_pad).unwrap())), ) .sample_size(10); } @@ -83,7 +74,7 @@ fn opening_benchmark_helper<F, EvalF, ComPackF, OpenPackF, T>( let mut rng = test_rng(); let mut transcript = T::new(); - let mut orion_scratch = OrionScratchPad::default(); + let mut scratch_pad = OrionScratchPad::<F, ComPackF>::default(); for num_vars in lowest_num_vars..=highest_num_vars { let poly = MultiLinearPoly::<F>::random(num_vars, &mut rng); @@ -91,23 +82,21 @@ fn opening_benchmark_helper<F, EvalF, ComPackF, OpenPackF, T>( .map(|_| EvalF::random_unsafe(&mut rng)) .collect(); - let orion_srs = - OrionSRS::from_random::<F>(num_vars, ORION_CODE_PARAMETER_INSTANCE, &mut rng); + let srs = OrionSRS::from_random::<F>(num_vars, ORION_CODE_PARAMETER_INSTANCE, &mut rng); - let _orion_commitment = orion_srs - .commit::<F, ComPackF>(&poly, &mut orion_scratch) - .unwrap(); + let _commitment = orion_commit(&srs, &poly, &mut scratch_pad).unwrap(); group .bench_function( BenchmarkId::new(format!("{num_vars} variables"), num_vars), |b| { b.iter(|| { - _ = black_box(orion_srs.open::<F, EvalF, ComPackF, OpenPackF, T>( + _ = black_box(orion_open::<F, EvalF, ComPackF, OpenPackF, T>( + &srs, &poly, &eval_point, &mut transcript, - &orion_scratch, + &scratch_pad, )) }) }, diff --git a/poly_commit/src/orion.rs b/poly_commit/src/orion.rs index 64a5b73a..dd5ceb71 100644 --- a/poly_commit/src/orion.rs +++ b/poly_commit/src/orion.rs @@ -8,6 +8,8 @@ mod linear_code; pub use linear_code::{OrionCodeParameter, ORION_CODE_PARAMETER_INSTANCE}; mod pcs_impl; +pub use pcs_impl::{orion_commit, orion_open, orion_verify}; + mod serde; #[cfg(test)] diff --git a/poly_commit/src/orion/pcs_impl.rs b/poly_commit/src/orion/pcs_impl.rs index 030b3b4e..28558679 100644 --- a/poly_commit/src/orion/pcs_impl.rs +++ b/poly_commit/src/orion/pcs_impl.rs @@ -7,294 +7,259 @@ use transcript::Transcript; use crate::{traits::TensorCodeIOPPCS, PCS_SOUNDNESS_BITS}; -use super::{ - linear_code::{OrionCode, OrionCodeParameter}, - utils::*, -}; - -impl OrionSRS { - pub fn new<F: Field>(num_variables: usize, code_instance: OrionCode) -> OrionResult<Self> { - let (_, msg_size) = Self::evals_shape::<F>(num_variables); - if msg_size != code_instance.msg_len() { - return Err(OrionPCSError::ParameterUnmatchError); - } - - // NOTE: we just move the instance of code, - // don't think the instance of expander code will be used elsewhere - Ok(Self { - num_variables, - code_instance, - }) - } - - pub fn from_random<F: Field>( - num_variables: usize, - code_param_instance: OrionCodeParameter, - mut rng: impl rand::RngCore, - ) -> Self { - let (_, msg_size) = Self::evals_shape::<F>(num_variables); - - Self { - num_variables, - code_instance: OrionCode::new(code_param_instance, msg_size, &mut rng), - } +use super::utils::*; + +pub fn orion_commit<F, ComPackF>( + pk: &OrionSRS, + poly: &MultiLinearPoly<F>, + scratch_pad: &mut OrionScratchPad<F, ComPackF>, +) -> OrionResult<OrionCommitment> +where + F: Field, + ComPackF: SimdField<Scalar = F>, +{ + let (row_num, msg_size) = OrionSRS::evals_shape::<F>(poly.get_num_vars()); + + // NOTE: pre transpose evaluations + let mut transposed_evaluations = poly.coeffs.clone(); + let mut scratch = vec![F::ZERO; 1 << poly.get_num_vars()]; + transpose_in_place(&mut transposed_evaluations, &mut scratch, row_num); + drop(scratch); + + // NOTE: SIMD pack each row of transposed matrix + assert_eq!(transposed_evaluations.len() % ComPackF::PACK_SIZE, 0); + let mut packed_evals: Vec<ComPackF> = transposed_evaluations + .chunks(ComPackF::PACK_SIZE) + .map(SimdField::pack) + .collect(); + drop(transposed_evaluations); + + // NOTE: transpose back to rows of evaluations, but packed + let packed_rows = row_num / ComPackF::PACK_SIZE; + assert_eq!(row_num % ComPackF::PACK_SIZE, 0); + + let mut scratch = vec![ComPackF::ZERO; packed_rows * msg_size]; + transpose_in_place(&mut packed_evals, &mut scratch, msg_size); + drop(scratch); + + // NOTE: packed codeword buffer and encode over packed field + let mut packed_interleaved_codewords = vec![ComPackF::ZERO; packed_rows * pk.codeword_len()]; + packed_evals + .chunks(msg_size) + .zip(packed_interleaved_codewords.chunks_mut(pk.codeword_len())) + .try_for_each(|(evals, codeword)| pk.code_instance.encode_in_place(evals, codeword))?; + drop(packed_evals); + + // NOTE: transpose codeword s.t., the matrix has codewords being columns + let mut scratch = vec![ComPackF::ZERO; packed_rows * pk.codeword_len()]; + transpose_in_place(&mut packed_interleaved_codewords, &mut scratch, packed_rows); + drop(scratch); + + // NOTE: commit the interleaved codeword + // we just directly commit to the packed field elements to leaves + // Also note, when codeword is not power of 2 length, pad to nearest po2 + // to commit by merkle tree + if !packed_interleaved_codewords.len().is_power_of_two() { + let aligned_po2_len = packed_interleaved_codewords.len().next_power_of_two(); + packed_interleaved_codewords.resize(aligned_po2_len, ComPackF::ZERO); } + scratch_pad.interleaved_alphabet_commitment = tree::Tree::compact_new_with_packed_field_elems::< + F, + ComPackF, + >(packed_interleaved_codewords); - pub fn commit<F, ComPackF>( - &self, - poly: &MultiLinearPoly<F>, - scratch_pad: &mut OrionScratchPad<F, ComPackF>, - ) -> OrionResult<OrionCommitment> - where - F: Field, - ComPackF: SimdField<Scalar = F>, - { - let (row_num, msg_size) = Self::evals_shape::<F>(poly.get_num_vars()); - - // NOTE: pre transpose evaluations - let mut transposed_evaluations = poly.coeffs.clone(); - let mut scratch = vec![F::ZERO; 1 << poly.get_num_vars()]; - transpose_in_place(&mut transposed_evaluations, &mut scratch, row_num); - drop(scratch); - - // NOTE: SIMD pack each row of transposed matrix - assert_eq!(transposed_evaluations.len() % ComPackF::PACK_SIZE, 0); - let mut packed_evals: Vec<ComPackF> = transposed_evaluations - .chunks(ComPackF::PACK_SIZE) - .map(SimdField::pack) - .collect(); - drop(transposed_evaluations); - - // NOTE: transpose back to rows of evaluations, but packed - let packed_rows = row_num / ComPackF::PACK_SIZE; - assert_eq!(row_num % ComPackF::PACK_SIZE, 0); - - let mut scratch = vec![ComPackF::ZERO; packed_rows * msg_size]; - transpose_in_place(&mut packed_evals, &mut scratch, msg_size); - drop(scratch); - - // NOTE: packed codeword buffer and encode over packed field - let mut packed_interleaved_codewords = - vec![ComPackF::ZERO; packed_rows * self.codeword_len()]; - packed_evals - .chunks(msg_size) - .zip(packed_interleaved_codewords.chunks_mut(self.codeword_len())) - .try_for_each(|(evals, codeword)| { - self.code_instance.encode_in_place(evals, codeword) - })?; - drop(packed_evals); - - // NOTE: transpose codeword s.t., the matrix has codewords being columns - let mut scratch = vec![ComPackF::ZERO; packed_rows * self.codeword_len()]; - transpose_in_place(&mut packed_interleaved_codewords, &mut scratch, packed_rows); - drop(scratch); - - // NOTE: commit the interleaved codeword - // we just directly commit to the packed field elements to leaves - // Also note, when codeword is not power of 2 length, pad to nearest po2 - // to commit by merkle tree - if !packed_interleaved_codewords.len().is_power_of_two() { - let aligned_po2_len = packed_interleaved_codewords.len().next_power_of_two(); - packed_interleaved_codewords.resize(aligned_po2_len, ComPackF::ZERO); - } - scratch_pad.interleaved_alphabet_commitment = - tree::Tree::compact_new_with_packed_field_elems::<F, ComPackF>( - packed_interleaved_codewords, - ); - - Ok(scratch_pad.interleaved_alphabet_commitment.root()) - } + Ok(scratch_pad.interleaved_alphabet_commitment.root()) +} - pub fn open<F, EvalF, ComPackF, OpenPackF, T>( - &self, - poly: &MultiLinearPoly<F>, - point: &[EvalF], - transcript: &mut T, - scratch_pad: &OrionScratchPad<F, ComPackF>, - ) -> (EvalF, OrionProof<EvalF>) - where - F: Field, - EvalF: Field + From<F> + Mul<F, Output = EvalF>, - ComPackF: SimdField<Scalar = F>, - OpenPackF: SimdField<Scalar = F>, - T: Transcript<EvalF>, - { - let (row_num, msg_size) = Self::evals_shape::<F>(poly.get_num_vars()); - let num_of_vars_in_msg = msg_size.ilog2() as usize; - - // NOTE: transpose evaluations for linear combinations in evaulation/proximity tests - let mut transposed_evaluations = poly.coeffs.clone(); - let mut scratch = vec![F::ZERO; 1 << poly.get_num_vars()]; - transpose_in_place(&mut transposed_evaluations, &mut scratch, row_num); - drop(scratch); - - // NOTE: SIMD pack each row of transposed matrix - assert_eq!(transposed_evaluations.len() % OpenPackF::PACK_SIZE, 0); - let packed_evals: Vec<OpenPackF> = transposed_evaluations - .chunks(OpenPackF::PACK_SIZE) - .map(OpenPackF::pack) - .collect(); - drop(transposed_evaluations); - - // NOTE: declare the look up tables for column sums - let packed_rows = row_num / OpenPackF::PACK_SIZE; - let mut luts = SubsetSumLUTs::new(OpenPackF::PACK_SIZE, packed_rows); - - // NOTE: working on evaluation response of tensor code IOP based PCS - let mut eval_row = vec![EvalF::ZERO; msg_size]; - - let eq_col_coeffs = EqPolynomial::build_eq_x_r(&point[num_of_vars_in_msg..]); - luts.build(&eq_col_coeffs); +pub fn orion_open<F, EvalF, ComPackF, OpenPackF, T>( + pk: &OrionSRS, + poly: &MultiLinearPoly<F>, + point: &[EvalF], + transcript: &mut T, + scratch_pad: &OrionScratchPad<F, ComPackF>, +) -> (EvalF, OrionProof<EvalF>) +where + F: Field, + EvalF: Field + From<F> + Mul<F, Output = EvalF>, + ComPackF: SimdField<Scalar = F>, + OpenPackF: SimdField<Scalar = F>, + T: Transcript<EvalF>, +{ + let (row_num, msg_size) = OrionSRS::evals_shape::<F>(poly.get_num_vars()); + let num_of_vars_in_msg = msg_size.ilog2() as usize; + + // NOTE: transpose evaluations for linear combinations in evaulation/proximity tests + let mut transposed_evaluations = poly.coeffs.clone(); + let mut scratch = vec![F::ZERO; 1 << poly.get_num_vars()]; + transpose_in_place(&mut transposed_evaluations, &mut scratch, row_num); + drop(scratch); + + // NOTE: SIMD pack each row of transposed matrix + assert_eq!(transposed_evaluations.len() % OpenPackF::PACK_SIZE, 0); + let packed_evals: Vec<OpenPackF> = transposed_evaluations + .chunks(OpenPackF::PACK_SIZE) + .map(OpenPackF::pack) + .collect(); + drop(transposed_evaluations); + + // NOTE: declare the look up tables for column sums + let packed_rows = row_num / OpenPackF::PACK_SIZE; + let mut luts = SubsetSumLUTs::new(OpenPackF::PACK_SIZE, packed_rows); + + // NOTE: working on evaluation response of tensor code IOP based PCS + let mut eval_row = vec![EvalF::ZERO; msg_size]; + + let eq_col_coeffs = EqPolynomial::build_eq_x_r(&point[num_of_vars_in_msg..]); + luts.build(&eq_col_coeffs); + + packed_evals + .chunks(packed_rows) + .zip(eval_row.iter_mut()) + .for_each(|(p_col, res)| *res = luts.lookup_and_sum(p_col)); + + // NOTE: draw random linear combination out + // and compose proximity response(s) of tensor code IOP based PCS + let proximity_test_num = pk.proximity_repetitions::<EvalF>(PCS_SOUNDNESS_BITS); + let mut proximity_rows = vec![vec![EvalF::ZERO; msg_size]; proximity_test_num]; + + proximity_rows.iter_mut().for_each(|row_buffer| { + let random_coeffs = transcript.generate_challenge_field_elements(row_num); + luts.build(&random_coeffs); packed_evals .chunks(packed_rows) - .zip(eval_row.iter_mut()) + .zip(row_buffer.iter_mut()) .for_each(|(p_col, res)| *res = luts.lookup_and_sum(p_col)); + }); + drop(luts); + + // NOTE: working on evaluation on top of evaluation response + let mut scratch = vec![EvalF::ZERO; msg_size]; + let eval = MultiLinearPoly::evaluate_with_buffer( + &eval_row, + &point[..num_of_vars_in_msg], + &mut scratch, + ); + drop(scratch); + + // NOTE: MT opening for point queries + let leaf_range = row_num / tree::leaf_adic::<F>(); + let query_num = pk.query_complexity(PCS_SOUNDNESS_BITS); + let query_indices = transcript.generate_challenge_index_vector(query_num); + let query_openings = query_indices + .iter() + .map(|qi| { + let index = *qi % pk.codeword_len(); + let left = index * leaf_range; + let right = left + leaf_range - 1; + + scratch_pad + .interleaved_alphabet_commitment + .range_query(left, right) + }) + .collect(); + + ( + eval, + OrionProof { + eval_row, + proximity_rows, + query_openings, + }, + ) +} - // NOTE: draw random linear combination out - // and compose proximity response(s) of tensor code IOP based PCS - let proximity_test_num = self.proximity_repetitions::<EvalF>(PCS_SOUNDNESS_BITS); - let mut proximity_rows = vec![vec![EvalF::ZERO; msg_size]; proximity_test_num]; - - proximity_rows.iter_mut().for_each(|row_buffer| { - let random_coeffs = transcript.generate_challenge_field_elements(row_num); - luts.build(&random_coeffs); - - packed_evals - .chunks(packed_rows) - .zip(row_buffer.iter_mut()) - .for_each(|(p_col, res)| *res = luts.lookup_and_sum(p_col)); - }); - drop(luts); - - // NOTE: working on evaluation on top of evaluation response - let mut scratch = vec![EvalF::ZERO; msg_size]; - let eval = MultiLinearPoly::evaluate_with_buffer( - &eval_row, - &point[..num_of_vars_in_msg], - &mut scratch, - ); - drop(scratch); - - // NOTE: MT opening for point queries - let leaf_range = row_num / tree::leaf_adic::<F>(); - let query_num = self.query_complexity(PCS_SOUNDNESS_BITS); - let query_indices = transcript.generate_challenge_index_vector(query_num); - let query_openings = query_indices +pub fn orion_verify<F, EvalF, ComPackF, OpenPackF, T>( + vk: &OrionSRS, + commitment: &OrionCommitment, + point: &[EvalF], + evaluation: EvalF, + transcript: &mut T, + proof: &OrionProof<EvalF>, +) -> bool +where + F: Field, + EvalF: Field + From<F> + Mul<F, Output = EvalF>, + ComPackF: SimdField<Scalar = F>, + OpenPackF: SimdField<Scalar = F>, + T: Transcript<EvalF>, +{ + let (row_num, msg_size) = OrionSRS::evals_shape::<F>(point.len()); + let num_of_vars_in_msg = msg_size.ilog2() as usize; + + // NOTE: working on evaluation response, evaluate the rest of the response + let mut scratch = vec![EvalF::ZERO; msg_size]; + let final_eval = MultiLinearPoly::evaluate_with_buffer( + &proof.eval_row, + &point[..num_of_vars_in_msg], + &mut scratch, + ); + if final_eval != evaluation { + return false; + } + + // NOTE: working on proximity responses, draw random linear combinations + // then draw query points from fiat shamir transcripts + let proximity_test_num = vk.proximity_repetitions::<EvalF>(PCS_SOUNDNESS_BITS); + let random_linear_combinations: Vec<Vec<EvalF>> = (0..proximity_test_num) + .map(|_| transcript.generate_challenge_field_elements(row_num)) + .collect(); + let query_num = vk.query_complexity(PCS_SOUNDNESS_BITS); + let query_indices = transcript.generate_challenge_index_vector(query_num); + + // NOTE: check consistency in MT in the opening trees and against the commitment tree + let leaf_range = row_num / tree::leaf_adic::<F>(); + let mt_consistency = + query_indices .iter() - .map(|qi| { - let index = *qi % self.codeword_len(); - let left = index * leaf_range; - let right = left + leaf_range - 1; - - scratch_pad - .interleaved_alphabet_commitment - .range_query(left, right) - }) - .collect(); - - ( - eval, - OrionProof { - eval_row, - proximity_rows, - query_openings, - }, - ) + .zip(proof.query_openings.iter()) + .all(|(&qi, range_path)| { + let index = qi % vk.codeword_len(); + range_path.verify(commitment) && index == range_path.left / leaf_range + }); + if !mt_consistency { + return false; } - pub fn verify<F, EvalF, ComPackF, OpenPackF, T>( - &self, - commitment: &OrionCommitment, - point: &[EvalF], - evaluation: EvalF, - proof: &OrionProof<EvalF>, - transcript: &mut T, - ) -> bool - where - F: Field, - EvalF: Field + From<F> + Mul<F, Output = EvalF>, - ComPackF: SimdField<Scalar = F>, - OpenPackF: SimdField<Scalar = F>, - T: Transcript<EvalF>, - { - let (row_num, msg_size) = Self::evals_shape::<F>(point.len()); - let num_of_vars_in_msg = msg_size.ilog2() as usize; - - // NOTE: working on evaluation response, evaluate the rest of the response - let mut scratch = vec![EvalF::ZERO; msg_size]; - let final_eval = MultiLinearPoly::evaluate_with_buffer( - &proof.eval_row, - &point[..num_of_vars_in_msg], - &mut scratch, - ); - if final_eval != evaluation { - return false; - } - - // NOTE: working on proximity responses, draw random linear combinations - // then draw query points from fiat shamir transcripts - let proximity_test_num = self.proximity_repetitions::<EvalF>(PCS_SOUNDNESS_BITS); - let random_linear_combinations: Vec<Vec<EvalF>> = (0..proximity_test_num) - .map(|_| transcript.generate_challenge_field_elements(row_num)) - .collect(); - let query_num = self.query_complexity(PCS_SOUNDNESS_BITS); - let query_indices = transcript.generate_challenge_index_vector(query_num); - - // NOTE: check consistency in MT in the opening trees and against the commitment tree - let leaf_range = row_num / tree::leaf_adic::<F>(); - let mt_consistency = + // NOTE: prepare the interleaved alphabets from the MT paths, + // but pack them back into look up table acceptable formats + let packed_interleaved_alphabets: Vec<_> = proof + .query_openings + .iter() + .map(|p| -> Vec<_> { + p.unpack_field_elems::<F, ComPackF>() + .chunks(OpenPackF::PACK_SIZE) + .map(OpenPackF::pack) + .collect() + }) + .collect(); + + // NOTE: encode the proximity/evaluation responses, + // check againts all challenged indices by check alphabets against + // linear combined interleaved alphabet + let mut luts = SubsetSumLUTs::new(OpenPackF::PACK_SIZE, row_num / OpenPackF::PACK_SIZE); + assert_eq!(row_num % OpenPackF::PACK_SIZE, 0); + + let eq_linear_combination = EqPolynomial::build_eq_x_r(&point[num_of_vars_in_msg..]); + random_linear_combinations + .iter() + .zip(proof.proximity_rows.iter()) + .chain(iter::once((&eq_linear_combination, &proof.eval_row))) + .all(|(rl, msg)| { + let codeword = match vk.code_instance.encode(msg) { + Ok(c) => c, + _ => return false, + }; + + luts.build(rl); + query_indices .iter() - .zip(proof.query_openings.iter()) - .all(|(&qi, range_path)| { - let index = qi % self.codeword_len(); - range_path.verify(commitment) && index == range_path.left / leaf_range - }); - if !mt_consistency { - return false; - } - - // NOTE: prepare the interleaved alphabets from the MT paths, - // but pack them back into look up table acceptable formats - let packed_interleaved_alphabets: Vec<_> = proof - .query_openings - .iter() - .map(|p| -> Vec<_> { - p.unpack_field_elems::<F, ComPackF>() - .chunks(OpenPackF::PACK_SIZE) - .map(OpenPackF::pack) - .collect() - }) - .collect(); - - // NOTE: encode the proximity/evaluation responses, - // check againts all challenged indices by check alphabets against - // linear combined interleaved alphabet - let mut luts = SubsetSumLUTs::new(OpenPackF::PACK_SIZE, row_num / OpenPackF::PACK_SIZE); - assert_eq!(row_num % OpenPackF::PACK_SIZE, 0); - - let eq_linear_combination = EqPolynomial::build_eq_x_r(&point[num_of_vars_in_msg..]); - random_linear_combinations - .iter() - .zip(proof.proximity_rows.iter()) - .chain(iter::once((&eq_linear_combination, &proof.eval_row))) - .all(|(rl, msg)| { - let codeword = match self.code_instance.encode(msg) { - Ok(c) => c, - _ => return false, - }; - - luts.build(rl); - - query_indices - .iter() - .zip(packed_interleaved_alphabets.iter()) - .all(|(&qi, interleaved_alphabet)| { - let index = qi % self.codeword_len(); - let alphabet = luts.lookup_and_sum(interleaved_alphabet); - alphabet == codeword[index] - }) - }) - } + .zip(packed_interleaved_alphabets.iter()) + .all(|(&qi, interleaved_alphabet)| { + let index = qi % vk.codeword_len(); + let alphabet = luts.lookup_and_sum(interleaved_alphabet); + alphabet == codeword[index] + }) + }) } diff --git a/poly_commit/src/orion/tests.rs b/poly_commit/src/orion/tests.rs index 41309c7c..54d7da0d 100644 --- a/poly_commit/src/orion/tests.rs +++ b/poly_commit/src/orion/tests.rs @@ -10,6 +10,7 @@ use transcript::{BytesHashTranscript, Keccak256hasher, Transcript}; use crate::{ orion::{ linear_code::{OrionCode, ORION_CODE_PARAMETER_INSTANCE}, + pcs_impl::*, utils::*, }, traits::TensorCodeIOPPCS, @@ -119,15 +120,11 @@ where let mut rng = test_rng(); let random_poly = MultiLinearPoly::<F>::random(num_vars, &mut rng); - let orion_pcs = OrionSRS::from_random::<F>(num_vars, ORION_CODE_PARAMETER_INSTANCE, &mut rng); + let srs = OrionSRS::from_random::<F>(num_vars, ORION_CODE_PARAMETER_INSTANCE, &mut rng); + let mut scratch_pad = OrionScratchPad::<F, ComPackF>::default(); - let mut orion_scratch = OrionScratchPad::default(); - - let real_commitment = orion_pcs - .commit::<F, ComPackF>(&random_poly, &mut orion_scratch) - .unwrap(); - - let dumb_commitment = dumb_commit::<F, ComPackF>(&orion_pcs, &random_poly); + let real_commitment = orion_commit(&srs, &random_poly, &mut scratch_pad).unwrap(); + let dumb_commitment = dumb_commit::<F, ComPackF>(&srs, &random_poly); assert_eq!(real_commitment, dumb_commitment); } @@ -166,27 +163,25 @@ where let mut transcript: BytesHashTranscript<EvalF, Keccak256hasher> = BytesHashTranscript::new(); let mut transcript_cloned = transcript.clone(); - let orion_srs = OrionSRS::from_random::<F>(num_vars, ORION_CODE_PARAMETER_INSTANCE, &mut rng); - - let mut orion_scratch = OrionScratchPad::default(); - - let orion_commitment = orion_srs - .commit::<F, ComPackF>(&random_poly, &mut orion_scratch) - .unwrap(); + let srs = OrionSRS::from_random::<F>(num_vars, ORION_CODE_PARAMETER_INSTANCE, &mut rng); + let mut scratch_pad = OrionScratchPad::<F, ComPackF>::default(); + let commitment = orion_commit(&srs, &random_poly, &mut scratch_pad).unwrap(); - let (_, opening) = orion_srs.open::<F, EvalF, ComPackF, OpenPackF, _>( + let (_, opening) = orion_open::<F, EvalF, ComPackF, OpenPackF, _>( + &srs, &random_poly, &random_point, &mut transcript, - &orion_scratch, + &scratch_pad, ); - assert!(orion_srs.verify::<F, EvalF, ComPackF, OpenPackF, _>( - &orion_commitment, + assert!(orion_verify::<F, EvalF, ComPackF, OpenPackF, _>( + &srs, + &commitment, &random_point, expected_eval, + &mut transcript_cloned, &opening, - &mut transcript_cloned )); } diff --git a/poly_commit/src/orion/utils.rs b/poly_commit/src/orion/utils.rs index 4e104c48..79b33f13 100644 --- a/poly_commit/src/orion/utils.rs +++ b/poly_commit/src/orion/utils.rs @@ -5,7 +5,7 @@ use thiserror::Error; use crate::{traits::TensorCodeIOPPCS, StructuredReferenceString}; -use super::linear_code::OrionCode; +use super::linear_code::{OrionCode, OrionCodeParameter}; /* * PCS ERROR AND RESULT SETUP @@ -52,6 +52,35 @@ impl StructuredReferenceString for OrionSRS { } } +impl OrionSRS { + pub fn new<F: Field>(num_variables: usize, code_instance: OrionCode) -> OrionResult<Self> { + let (_, msg_size) = Self::evals_shape::<F>(num_variables); + if msg_size != code_instance.msg_len() { + return Err(OrionPCSError::ParameterUnmatchError); + } + + // NOTE: we just move the instance of code, + // don't think the instance of expander code will be used elsewhere + Ok(Self { + num_variables, + code_instance, + }) + } + + pub fn from_random<F: Field>( + num_variables: usize, + code_param_instance: OrionCodeParameter, + mut rng: impl rand::RngCore, + ) -> Self { + let (_, msg_size) = Self::evals_shape::<F>(num_variables); + + Self { + num_variables, + code_instance: OrionCode::new(code_param_instance, msg_size, &mut rng), + } + } +} + pub type OrionCommitment = tree::Node; // TODO: maybe prepare memory API for transpose in commit and open? From 4e81cd9d60efb7111428d9a4fc2d2d5ef84577e6 Mon Sep 17 00:00:00 2001 From: Hang Su <tonyfloater@gmail.com> Date: Sat, 23 Nov 2024 07:16:05 -0500 Subject: [PATCH 04/65] orion commit from multilinear poly over simd fields --- .github/workflows/ci.yml | 2 +- poly_commit/benches/orion.rs | 8 +- poly_commit/src/orion.rs | 2 +- poly_commit/src/orion/pcs_impl.rs | 136 ++++++++++++++++++++++++------ poly_commit/src/orion/tests.rs | 4 +- 5 files changed, 120 insertions(+), 32 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3f5ecea0..f557ea11 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -85,7 +85,7 @@ jobs: - uses: Swatinem/rust-cache@v2 with: # The prefix cache key, this can be changed to start a new cache manually. - prefix-key: "mpi-v5.0.6_" # update me if brew formula changes to a new version + prefix-key: "giggidy" # update me if brew formula changes to a new version - name: Set RUSTFLAGS for AVX if: matrix.feature != '' run: echo "RUSTFLAGS=$RUSTFLAGS -C target-feature=+${{ matrix.feature }}" >> $GITHUB_ENV diff --git a/poly_commit/benches/orion.rs b/poly_commit/benches/orion.rs index ccd2dd2e..bdcb7b96 100644 --- a/poly_commit/benches/orion.rs +++ b/poly_commit/benches/orion.rs @@ -38,7 +38,11 @@ fn committing_benchmark_helper<F, EvalF, ComPackF, OpenPackF, T>( group .bench_function( BenchmarkId::new(format!("{num_vars} variables"), num_vars), - |b| b.iter(|| _ = black_box(orion_commit(&srs, &poly, &mut scratch_pad).unwrap())), + |b| { + b.iter(|| { + _ = black_box(orion_commit_base(&srs, &poly, &mut scratch_pad).unwrap()) + }) + }, ) .sample_size(10); } @@ -84,7 +88,7 @@ fn opening_benchmark_helper<F, EvalF, ComPackF, OpenPackF, T>( let srs = OrionSRS::from_random::<F>(num_vars, ORION_CODE_PARAMETER_INSTANCE, &mut rng); - let _commitment = orion_commit(&srs, &poly, &mut scratch_pad).unwrap(); + let _commitment = orion_commit_base(&srs, &poly, &mut scratch_pad).unwrap(); group .bench_function( diff --git a/poly_commit/src/orion.rs b/poly_commit/src/orion.rs index dd5ceb71..0df87cce 100644 --- a/poly_commit/src/orion.rs +++ b/poly_commit/src/orion.rs @@ -8,7 +8,7 @@ mod linear_code; pub use linear_code::{OrionCodeParameter, ORION_CODE_PARAMETER_INSTANCE}; mod pcs_impl; -pub use pcs_impl::{orion_commit, orion_open, orion_verify}; +pub use pcs_impl::{orion_commit_base, orion_commit_simd_field, orion_open, orion_verify}; mod serde; diff --git a/poly_commit/src/orion/pcs_impl.rs b/poly_commit/src/orion/pcs_impl.rs index 28558679..69614ccb 100644 --- a/poly_commit/src/orion/pcs_impl.rs +++ b/poly_commit/src/orion/pcs_impl.rs @@ -9,49 +9,96 @@ use crate::{traits::TensorCodeIOPPCS, PCS_SOUNDNESS_BITS}; use super::utils::*; -pub fn orion_commit<F, ComPackF>( - pk: &OrionSRS, - poly: &MultiLinearPoly<F>, - scratch_pad: &mut OrionScratchPad<F, ComPackF>, -) -> OrionResult<OrionCommitment> +#[inline(always)] +fn transpose_and_pack<F, PackF>( + evaluations: &mut [F], + row_num: usize, + msg_size: usize, +) -> Vec<PackF> where F: Field, - ComPackF: SimdField<Scalar = F>, + PackF: SimdField<Scalar = F>, { - let (row_num, msg_size) = OrionSRS::evals_shape::<F>(poly.get_num_vars()); - // NOTE: pre transpose evaluations - let mut transposed_evaluations = poly.coeffs.clone(); - let mut scratch = vec![F::ZERO; 1 << poly.get_num_vars()]; - transpose_in_place(&mut transposed_evaluations, &mut scratch, row_num); + let mut scratch = vec![F::ZERO; evaluations.len()]; + transpose_in_place(evaluations, &mut scratch, row_num); drop(scratch); // NOTE: SIMD pack each row of transposed matrix - assert_eq!(transposed_evaluations.len() % ComPackF::PACK_SIZE, 0); - let mut packed_evals: Vec<ComPackF> = transposed_evaluations - .chunks(ComPackF::PACK_SIZE) + assert_eq!(evaluations.len() % PackF::PACK_SIZE, 0); + let mut packed_evals: Vec<PackF> = evaluations + .chunks(PackF::PACK_SIZE) .map(SimdField::pack) .collect(); - drop(transposed_evaluations); // NOTE: transpose back to rows of evaluations, but packed - let packed_rows = row_num / ComPackF::PACK_SIZE; - assert_eq!(row_num % ComPackF::PACK_SIZE, 0); + let packed_rows = row_num / PackF::PACK_SIZE; + assert_eq!(row_num % PackF::PACK_SIZE, 0); - let mut scratch = vec![ComPackF::ZERO; packed_rows * msg_size]; + let mut scratch = vec![PackF::ZERO; packed_rows * msg_size]; transpose_in_place(&mut packed_evals, &mut scratch, msg_size); drop(scratch); + packed_evals +} + +#[inline(always)] +fn transpose_and_pack_from_simd<F, CircuitF, PackF>( + evaluations: &mut [CircuitF], + row_num: usize, + msg_size: usize, +) -> Vec<PackF> +where + F: Field, + CircuitF: SimdField<Scalar = F>, + PackF: SimdField<Scalar = F>, +{ + // NOTE: pre transpose evaluations + let mut scratch = vec![CircuitF::ZERO; evaluations.len()]; + transpose_in_place(evaluations, &mut scratch, row_num); + drop(scratch); + + // NOTE: SIMD pack each row of transposed matrix + let relative_pack_size = PackF::PACK_SIZE / CircuitF::PACK_SIZE; + assert_eq!(PackF::PACK_SIZE % CircuitF::PACK_SIZE, 0); + assert_eq!(evaluations.len() % relative_pack_size, 0); + let mut packed_evals: Vec<PackF> = evaluations + .chunks(relative_pack_size) + .map(SimdField::pack_from_simd) + .collect(); + + // NOTE: transpose back to rows of evaluations, but packed + let packed_rows = row_num / relative_pack_size; + assert_eq!(row_num % relative_pack_size, 0); + + let mut scratch = vec![PackF::ZERO; packed_rows * msg_size]; + transpose_in_place(&mut packed_evals, &mut scratch, msg_size); + drop(scratch); + + packed_evals +} + +#[inline(always)] +fn commit_encoded<F, PackF>( + pk: &OrionSRS, + packed_evals: &[PackF], + scratch_pad: &mut OrionScratchPad<F, PackF>, + packed_rows: usize, + msg_size: usize, +) -> OrionResult<OrionCommitment> +where + F: Field, + PackF: SimdField<Scalar = F>, +{ // NOTE: packed codeword buffer and encode over packed field - let mut packed_interleaved_codewords = vec![ComPackF::ZERO; packed_rows * pk.codeword_len()]; + let mut packed_interleaved_codewords = vec![PackF::ZERO; packed_rows * pk.codeword_len()]; packed_evals .chunks(msg_size) .zip(packed_interleaved_codewords.chunks_mut(pk.codeword_len())) .try_for_each(|(evals, codeword)| pk.code_instance.encode_in_place(evals, codeword))?; - drop(packed_evals); // NOTE: transpose codeword s.t., the matrix has codewords being columns - let mut scratch = vec![ComPackF::ZERO; packed_rows * pk.codeword_len()]; + let mut scratch = vec![PackF::ZERO; packed_rows * pk.codeword_len()]; transpose_in_place(&mut packed_interleaved_codewords, &mut scratch, packed_rows); drop(scratch); @@ -61,16 +108,53 @@ where // to commit by merkle tree if !packed_interleaved_codewords.len().is_power_of_two() { let aligned_po2_len = packed_interleaved_codewords.len().next_power_of_two(); - packed_interleaved_codewords.resize(aligned_po2_len, ComPackF::ZERO); + packed_interleaved_codewords.resize(aligned_po2_len, PackF::ZERO); } - scratch_pad.interleaved_alphabet_commitment = tree::Tree::compact_new_with_packed_field_elems::< - F, - ComPackF, - >(packed_interleaved_codewords); + scratch_pad.interleaved_alphabet_commitment = + tree::Tree::compact_new_with_packed_field_elems::<F, PackF>(packed_interleaved_codewords); Ok(scratch_pad.interleaved_alphabet_commitment.root()) } +pub fn orion_commit_base<F, ComPackF>( + pk: &OrionSRS, + poly: &MultiLinearPoly<F>, + scratch_pad: &mut OrionScratchPad<F, ComPackF>, +) -> OrionResult<OrionCommitment> +where + F: Field, + ComPackF: SimdField<Scalar = F>, +{ + let (row_num, msg_size) = OrionSRS::evals_shape::<F>(poly.get_num_vars()); + let packed_rows = row_num / ComPackF::PACK_SIZE; + let mut evals = poly.coeffs.clone(); + + let packed_evals = transpose_and_pack(&mut evals, row_num, msg_size); + + commit_encoded(pk, &packed_evals, scratch_pad, packed_rows, msg_size) +} + +pub fn orion_commit_simd_field<F, CircuitF, ComPackF>( + pk: &OrionSRS, + poly: &MultiLinearPoly<CircuitF>, + scratch_pad: &mut OrionScratchPad<F, ComPackF>, +) -> OrionResult<OrionCommitment> +where + F: Field, + CircuitF: SimdField<Scalar = F>, + ComPackF: SimdField<Scalar = F>, +{ + let (row_num, msg_size) = OrionSRS::evals_shape::<CircuitF>(poly.get_num_vars()); + let relative_pack_size = ComPackF::PACK_SIZE / CircuitF::PACK_SIZE; + let packed_rows = row_num / relative_pack_size; + let mut evals = poly.coeffs.clone(); + + let packed_evals = + transpose_and_pack_from_simd::<F, CircuitF, ComPackF>(&mut evals, row_num, msg_size); + + commit_encoded(pk, &packed_evals, scratch_pad, packed_rows, msg_size) +} + pub fn orion_open<F, EvalF, ComPackF, OpenPackF, T>( pk: &OrionSRS, poly: &MultiLinearPoly<F>, diff --git a/poly_commit/src/orion/tests.rs b/poly_commit/src/orion/tests.rs index 54d7da0d..59c1fa2e 100644 --- a/poly_commit/src/orion/tests.rs +++ b/poly_commit/src/orion/tests.rs @@ -123,7 +123,7 @@ where let srs = OrionSRS::from_random::<F>(num_vars, ORION_CODE_PARAMETER_INSTANCE, &mut rng); let mut scratch_pad = OrionScratchPad::<F, ComPackF>::default(); - let real_commitment = orion_commit(&srs, &random_poly, &mut scratch_pad).unwrap(); + let real_commitment = orion_commit_base(&srs, &random_poly, &mut scratch_pad).unwrap(); let dumb_commitment = dumb_commit::<F, ComPackF>(&srs, &random_poly); assert_eq!(real_commitment, dumb_commitment); @@ -165,7 +165,7 @@ where let srs = OrionSRS::from_random::<F>(num_vars, ORION_CODE_PARAMETER_INSTANCE, &mut rng); let mut scratch_pad = OrionScratchPad::<F, ComPackF>::default(); - let commitment = orion_commit(&srs, &random_poly, &mut scratch_pad).unwrap(); + let commitment = orion_commit_base(&srs, &random_poly, &mut scratch_pad).unwrap(); let (_, opening) = orion_open::<F, EvalF, ComPackF, OpenPackF, _>( &srs, From 533be262a809ad052f3453ee08f93fbc0c081467 Mon Sep 17 00:00:00 2001 From: Hang Su <tonyfloater@gmail.com> Date: Sat, 23 Nov 2024 20:01:49 -0500 Subject: [PATCH 05/65] minor, refactoring orion opening base field, need to work on orion opening simd field --- poly_commit/benches/orion.rs | 8 +- poly_commit/src/orion.rs | 8 +- poly_commit/src/orion/base_field_impl.rs | 139 +++++++++++++++++ poly_commit/src/orion/pcs_impl.rs | 190 +---------------------- poly_commit/src/orion/simd_field_impl.rs | 60 +++++++ poly_commit/src/orion/tests.rs | 7 +- 6 files changed, 222 insertions(+), 190 deletions(-) create mode 100644 poly_commit/src/orion/base_field_impl.rs create mode 100644 poly_commit/src/orion/simd_field_impl.rs diff --git a/poly_commit/benches/orion.rs b/poly_commit/benches/orion.rs index bdcb7b96..0d28f545 100644 --- a/poly_commit/benches/orion.rs +++ b/poly_commit/benches/orion.rs @@ -40,7 +40,9 @@ fn committing_benchmark_helper<F, EvalF, ComPackF, OpenPackF, T>( BenchmarkId::new(format!("{num_vars} variables"), num_vars), |b| { b.iter(|| { - _ = black_box(orion_commit_base(&srs, &poly, &mut scratch_pad).unwrap()) + _ = black_box( + orion_commit_base_field(&srs, &poly, &mut scratch_pad).unwrap(), + ) }) }, ) @@ -88,14 +90,14 @@ fn opening_benchmark_helper<F, EvalF, ComPackF, OpenPackF, T>( let srs = OrionSRS::from_random::<F>(num_vars, ORION_CODE_PARAMETER_INSTANCE, &mut rng); - let _commitment = orion_commit_base(&srs, &poly, &mut scratch_pad).unwrap(); + let _commitment = orion_commit_base_field(&srs, &poly, &mut scratch_pad).unwrap(); group .bench_function( BenchmarkId::new(format!("{num_vars} variables"), num_vars), |b| { b.iter(|| { - _ = black_box(orion_open::<F, EvalF, ComPackF, OpenPackF, T>( + _ = black_box(orion_open_base_field::<F, EvalF, ComPackF, OpenPackF, T>( &srs, &poly, &eval_point, diff --git a/poly_commit/src/orion.rs b/poly_commit/src/orion.rs index 0df87cce..a1eb5dfa 100644 --- a/poly_commit/src/orion.rs +++ b/poly_commit/src/orion.rs @@ -8,7 +8,13 @@ mod linear_code; pub use linear_code::{OrionCodeParameter, ORION_CODE_PARAMETER_INSTANCE}; mod pcs_impl; -pub use pcs_impl::{orion_commit_base, orion_commit_simd_field, orion_open, orion_verify}; +pub use pcs_impl::orion_verify; + +mod base_field_impl; +pub use base_field_impl::{orion_commit_base_field, orion_open_base_field}; + +mod simd_field_impl; +pub use simd_field_impl::orion_commit_simd_field; mod serde; diff --git a/poly_commit/src/orion/base_field_impl.rs b/poly_commit/src/orion/base_field_impl.rs new file mode 100644 index 00000000..4a736bc3 --- /dev/null +++ b/poly_commit/src/orion/base_field_impl.rs @@ -0,0 +1,139 @@ +use std::ops::Mul; + +use arith::{Field, SimdField}; +use polynomials::{EqPolynomial, MultiLinearPoly}; +use transcript::Transcript; + +use crate::{ + orion::{ + pcs_impl::{commit_encoded, orion_mt_openings}, + utils::transpose_in_place, + }, + traits::TensorCodeIOPPCS, + SubsetSumLUTs, PCS_SOUNDNESS_BITS, +}; + +use super::{OrionCommitment, OrionProof, OrionResult, OrionSRS, OrionScratchPad}; + +#[inline(always)] +fn transpose_and_pack<F, PackF>(evaluations: &mut [F], row_num: usize) -> Vec<PackF> +where + F: Field, + PackF: SimdField<Scalar = F>, +{ + // NOTE: pre transpose evaluations + let mut scratch = vec![F::ZERO; evaluations.len()]; + transpose_in_place(evaluations, &mut scratch, row_num); + drop(scratch); + + // NOTE: SIMD pack each row of transposed matrix + evaluations + .chunks(PackF::PACK_SIZE) + .map(SimdField::pack) + .collect() +} + +pub fn orion_commit_base_field<F, ComPackF>( + pk: &OrionSRS, + poly: &MultiLinearPoly<F>, + scratch_pad: &mut OrionScratchPad<F, ComPackF>, +) -> OrionResult<OrionCommitment> +where + F: Field, + ComPackF: SimdField<Scalar = F>, +{ + let (row_num, msg_size) = OrionSRS::evals_shape::<F>(poly.get_num_vars()); + let packed_rows = row_num / ComPackF::PACK_SIZE; + assert_eq!(row_num % ComPackF::PACK_SIZE, 0); + + let mut evals = poly.coeffs.clone(); + assert_eq!(evals.len() % ComPackF::PACK_SIZE, 0); + + let mut packed_evals: Vec<ComPackF> = transpose_and_pack(&mut evals, row_num); + drop(evals); + + // NOTE: transpose back to rows of evaluations, but packed + let mut scratch = vec![ComPackF::ZERO; packed_rows * msg_size]; + transpose_in_place(&mut packed_evals, &mut scratch, msg_size); + drop(scratch); + + commit_encoded(pk, &packed_evals, scratch_pad, packed_rows, msg_size) +} + +pub fn orion_open_base_field<F, EvalF, ComPackF, OpenPackF, T>( + pk: &OrionSRS, + poly: &MultiLinearPoly<F>, + point: &[EvalF], + transcript: &mut T, + scratch_pad: &OrionScratchPad<F, ComPackF>, +) -> (EvalF, OrionProof<EvalF>) +where + F: Field, + EvalF: Field + From<F> + Mul<F, Output = EvalF>, + ComPackF: SimdField<Scalar = F>, + OpenPackF: SimdField<Scalar = F>, + T: Transcript<EvalF>, +{ + let (row_num, msg_size) = OrionSRS::evals_shape::<F>(poly.get_num_vars()); + let num_of_vars_in_msg = msg_size.ilog2() as usize; + + // NOTE: transpose evaluations for linear combinations in evaulation/proximity tests + let mut evals = poly.coeffs.clone(); + assert_eq!(evals.len() % OpenPackF::PACK_SIZE, 0); + + let packed_evals: Vec<OpenPackF> = transpose_and_pack(&mut evals, row_num); + drop(evals); + + // NOTE: declare the look up tables for column sums + let packed_rows = row_num / OpenPackF::PACK_SIZE; + let mut luts = SubsetSumLUTs::new(OpenPackF::PACK_SIZE, packed_rows); + + // NOTE: working on evaluation response of tensor code IOP based PCS + let mut eval_row = vec![EvalF::ZERO; msg_size]; + + let eq_col_coeffs = EqPolynomial::build_eq_x_r(&point[num_of_vars_in_msg..]); + luts.build(&eq_col_coeffs); + + packed_evals + .chunks(packed_rows) + .zip(eval_row.iter_mut()) + .for_each(|(p_col, res)| *res = luts.lookup_and_sum(p_col)); + + // NOTE: draw random linear combination out + // and compose proximity response(s) of tensor code IOP based PCS + let proximity_test_num = pk.proximity_repetitions::<EvalF>(PCS_SOUNDNESS_BITS); + let mut proximity_rows = vec![vec![EvalF::ZERO; msg_size]; proximity_test_num]; + + proximity_rows.iter_mut().for_each(|row_buffer| { + let random_coeffs = transcript.generate_challenge_field_elements(row_num); + luts.build(&random_coeffs); + + packed_evals + .chunks(packed_rows) + .zip(row_buffer.iter_mut()) + .for_each(|(p_col, res)| *res = luts.lookup_and_sum(p_col)); + }); + drop(luts); + + // NOTE: working on evaluation on top of evaluation response + let mut scratch = vec![EvalF::ZERO; msg_size]; + let eval = MultiLinearPoly::evaluate_with_buffer( + &eval_row, + &point[..num_of_vars_in_msg], + &mut scratch, + ); + drop(scratch); + + // NOTE: MT opening for point queries + let leaf_range = row_num / tree::leaf_adic::<F>(); + let query_openings = orion_mt_openings(pk, leaf_range, transcript, scratch_pad); + + ( + eval, + OrionProof { + eval_row, + proximity_rows, + query_openings, + }, + ) +} diff --git a/poly_commit/src/orion/pcs_impl.rs b/poly_commit/src/orion/pcs_impl.rs index 69614ccb..528bb0aa 100644 --- a/poly_commit/src/orion/pcs_impl.rs +++ b/poly_commit/src/orion/pcs_impl.rs @@ -10,76 +10,7 @@ use crate::{traits::TensorCodeIOPPCS, PCS_SOUNDNESS_BITS}; use super::utils::*; #[inline(always)] -fn transpose_and_pack<F, PackF>( - evaluations: &mut [F], - row_num: usize, - msg_size: usize, -) -> Vec<PackF> -where - F: Field, - PackF: SimdField<Scalar = F>, -{ - // NOTE: pre transpose evaluations - let mut scratch = vec![F::ZERO; evaluations.len()]; - transpose_in_place(evaluations, &mut scratch, row_num); - drop(scratch); - - // NOTE: SIMD pack each row of transposed matrix - assert_eq!(evaluations.len() % PackF::PACK_SIZE, 0); - let mut packed_evals: Vec<PackF> = evaluations - .chunks(PackF::PACK_SIZE) - .map(SimdField::pack) - .collect(); - - // NOTE: transpose back to rows of evaluations, but packed - let packed_rows = row_num / PackF::PACK_SIZE; - assert_eq!(row_num % PackF::PACK_SIZE, 0); - - let mut scratch = vec![PackF::ZERO; packed_rows * msg_size]; - transpose_in_place(&mut packed_evals, &mut scratch, msg_size); - drop(scratch); - - packed_evals -} - -#[inline(always)] -fn transpose_and_pack_from_simd<F, CircuitF, PackF>( - evaluations: &mut [CircuitF], - row_num: usize, - msg_size: usize, -) -> Vec<PackF> -where - F: Field, - CircuitF: SimdField<Scalar = F>, - PackF: SimdField<Scalar = F>, -{ - // NOTE: pre transpose evaluations - let mut scratch = vec![CircuitF::ZERO; evaluations.len()]; - transpose_in_place(evaluations, &mut scratch, row_num); - drop(scratch); - - // NOTE: SIMD pack each row of transposed matrix - let relative_pack_size = PackF::PACK_SIZE / CircuitF::PACK_SIZE; - assert_eq!(PackF::PACK_SIZE % CircuitF::PACK_SIZE, 0); - assert_eq!(evaluations.len() % relative_pack_size, 0); - let mut packed_evals: Vec<PackF> = evaluations - .chunks(relative_pack_size) - .map(SimdField::pack_from_simd) - .collect(); - - // NOTE: transpose back to rows of evaluations, but packed - let packed_rows = row_num / relative_pack_size; - assert_eq!(row_num % relative_pack_size, 0); - - let mut scratch = vec![PackF::ZERO; packed_rows * msg_size]; - transpose_in_place(&mut packed_evals, &mut scratch, msg_size); - drop(scratch); - - packed_evals -} - -#[inline(always)] -fn commit_encoded<F, PackF>( +pub(crate) fn commit_encoded<F, PackF>( pk: &OrionSRS, packed_evals: &[PackF], scratch_pad: &mut OrionScratchPad<F, PackF>, @@ -116,121 +47,23 @@ where Ok(scratch_pad.interleaved_alphabet_commitment.root()) } -pub fn orion_commit_base<F, ComPackF>( - pk: &OrionSRS, - poly: &MultiLinearPoly<F>, - scratch_pad: &mut OrionScratchPad<F, ComPackF>, -) -> OrionResult<OrionCommitment> -where - F: Field, - ComPackF: SimdField<Scalar = F>, -{ - let (row_num, msg_size) = OrionSRS::evals_shape::<F>(poly.get_num_vars()); - let packed_rows = row_num / ComPackF::PACK_SIZE; - let mut evals = poly.coeffs.clone(); - - let packed_evals = transpose_and_pack(&mut evals, row_num, msg_size); - - commit_encoded(pk, &packed_evals, scratch_pad, packed_rows, msg_size) -} - -pub fn orion_commit_simd_field<F, CircuitF, ComPackF>( - pk: &OrionSRS, - poly: &MultiLinearPoly<CircuitF>, - scratch_pad: &mut OrionScratchPad<F, ComPackF>, -) -> OrionResult<OrionCommitment> -where - F: Field, - CircuitF: SimdField<Scalar = F>, - ComPackF: SimdField<Scalar = F>, -{ - let (row_num, msg_size) = OrionSRS::evals_shape::<CircuitF>(poly.get_num_vars()); - let relative_pack_size = ComPackF::PACK_SIZE / CircuitF::PACK_SIZE; - let packed_rows = row_num / relative_pack_size; - let mut evals = poly.coeffs.clone(); - - let packed_evals = - transpose_and_pack_from_simd::<F, CircuitF, ComPackF>(&mut evals, row_num, msg_size); - - commit_encoded(pk, &packed_evals, scratch_pad, packed_rows, msg_size) -} - -pub fn orion_open<F, EvalF, ComPackF, OpenPackF, T>( +#[inline(always)] +pub(crate) fn orion_mt_openings<F, EvalF, ComPackF, T>( pk: &OrionSRS, - poly: &MultiLinearPoly<F>, - point: &[EvalF], + leaf_range: usize, transcript: &mut T, scratch_pad: &OrionScratchPad<F, ComPackF>, -) -> (EvalF, OrionProof<EvalF>) +) -> Vec<tree::RangePath> where F: Field, EvalF: Field + From<F> + Mul<F, Output = EvalF>, ComPackF: SimdField<Scalar = F>, - OpenPackF: SimdField<Scalar = F>, T: Transcript<EvalF>, { - let (row_num, msg_size) = OrionSRS::evals_shape::<F>(poly.get_num_vars()); - let num_of_vars_in_msg = msg_size.ilog2() as usize; - - // NOTE: transpose evaluations for linear combinations in evaulation/proximity tests - let mut transposed_evaluations = poly.coeffs.clone(); - let mut scratch = vec![F::ZERO; 1 << poly.get_num_vars()]; - transpose_in_place(&mut transposed_evaluations, &mut scratch, row_num); - drop(scratch); - - // NOTE: SIMD pack each row of transposed matrix - assert_eq!(transposed_evaluations.len() % OpenPackF::PACK_SIZE, 0); - let packed_evals: Vec<OpenPackF> = transposed_evaluations - .chunks(OpenPackF::PACK_SIZE) - .map(OpenPackF::pack) - .collect(); - drop(transposed_evaluations); - - // NOTE: declare the look up tables for column sums - let packed_rows = row_num / OpenPackF::PACK_SIZE; - let mut luts = SubsetSumLUTs::new(OpenPackF::PACK_SIZE, packed_rows); - - // NOTE: working on evaluation response of tensor code IOP based PCS - let mut eval_row = vec![EvalF::ZERO; msg_size]; - - let eq_col_coeffs = EqPolynomial::build_eq_x_r(&point[num_of_vars_in_msg..]); - luts.build(&eq_col_coeffs); - - packed_evals - .chunks(packed_rows) - .zip(eval_row.iter_mut()) - .for_each(|(p_col, res)| *res = luts.lookup_and_sum(p_col)); - - // NOTE: draw random linear combination out - // and compose proximity response(s) of tensor code IOP based PCS - let proximity_test_num = pk.proximity_repetitions::<EvalF>(PCS_SOUNDNESS_BITS); - let mut proximity_rows = vec![vec![EvalF::ZERO; msg_size]; proximity_test_num]; - - proximity_rows.iter_mut().for_each(|row_buffer| { - let random_coeffs = transcript.generate_challenge_field_elements(row_num); - luts.build(&random_coeffs); - - packed_evals - .chunks(packed_rows) - .zip(row_buffer.iter_mut()) - .for_each(|(p_col, res)| *res = luts.lookup_and_sum(p_col)); - }); - drop(luts); - - // NOTE: working on evaluation on top of evaluation response - let mut scratch = vec![EvalF::ZERO; msg_size]; - let eval = MultiLinearPoly::evaluate_with_buffer( - &eval_row, - &point[..num_of_vars_in_msg], - &mut scratch, - ); - drop(scratch); - // NOTE: MT opening for point queries - let leaf_range = row_num / tree::leaf_adic::<F>(); let query_num = pk.query_complexity(PCS_SOUNDNESS_BITS); let query_indices = transcript.generate_challenge_index_vector(query_num); - let query_openings = query_indices + query_indices .iter() .map(|qi| { let index = *qi % pk.codeword_len(); @@ -241,16 +74,7 @@ where .interleaved_alphabet_commitment .range_query(left, right) }) - .collect(); - - ( - eval, - OrionProof { - eval_row, - proximity_rows, - query_openings, - }, - ) + .collect() } pub fn orion_verify<F, EvalF, ComPackF, OpenPackF, T>( diff --git a/poly_commit/src/orion/simd_field_impl.rs b/poly_commit/src/orion/simd_field_impl.rs new file mode 100644 index 00000000..ed0d2362 --- /dev/null +++ b/poly_commit/src/orion/simd_field_impl.rs @@ -0,0 +1,60 @@ +use arith::{Field, SimdField}; +use polynomials::MultiLinearPoly; + +use crate::{orion::utils::transpose_in_place, traits::TensorCodeIOPPCS}; + +use super::{pcs_impl::commit_encoded, OrionCommitment, OrionResult, OrionSRS, OrionScratchPad}; + +#[inline(always)] +fn transpose_and_pack_simd<F, CircuitF, PackF>( + evaluations: &mut [CircuitF], + row_num: usize, +) -> Vec<PackF> +where + F: Field, + CircuitF: SimdField<Scalar = F>, + PackF: SimdField<Scalar = F>, +{ + // NOTE: pre transpose evaluations + let mut scratch = vec![CircuitF::ZERO; evaluations.len()]; + transpose_in_place(evaluations, &mut scratch, row_num); + drop(scratch); + + // NOTE: SIMD pack each row of transposed matrix + let relative_pack_size = PackF::PACK_SIZE / CircuitF::PACK_SIZE; + evaluations + .chunks(relative_pack_size) + .map(SimdField::pack_from_simd) + .collect() +} + +pub fn orion_commit_simd_field<F, CircuitF, ComPackF>( + pk: &OrionSRS, + poly: &MultiLinearPoly<CircuitF>, + scratch_pad: &mut OrionScratchPad<F, ComPackF>, +) -> OrionResult<OrionCommitment> +where + F: Field, + CircuitF: SimdField<Scalar = F>, + ComPackF: SimdField<Scalar = F>, +{ + let (row_num, msg_size) = OrionSRS::evals_shape::<CircuitF>(poly.get_num_vars()); + let relative_pack_size = ComPackF::PACK_SIZE / CircuitF::PACK_SIZE; + assert_eq!(ComPackF::PACK_SIZE % CircuitF::PACK_SIZE, 0); + + let packed_rows = row_num / relative_pack_size; + assert_eq!(row_num % relative_pack_size, 0); + + let mut evals = poly.coeffs.clone(); + assert_eq!(evals.len() % relative_pack_size, 0); + + let mut packed_evals = transpose_and_pack_simd::<F, CircuitF, ComPackF>(&mut evals, row_num); + drop(evals); + + // NOTE: transpose back to rows of evaluations, but packed + let mut scratch = vec![ComPackF::ZERO; packed_rows * msg_size]; + transpose_in_place(&mut packed_evals, &mut scratch, msg_size); + drop(scratch); + + commit_encoded(pk, &packed_evals, scratch_pad, packed_rows, msg_size) +} diff --git a/poly_commit/src/orion/tests.rs b/poly_commit/src/orion/tests.rs index 59c1fa2e..36536802 100644 --- a/poly_commit/src/orion/tests.rs +++ b/poly_commit/src/orion/tests.rs @@ -9,6 +9,7 @@ use transcript::{BytesHashTranscript, Keccak256hasher, Transcript}; use crate::{ orion::{ + base_field_impl::{orion_commit_base_field, orion_open_base_field}, linear_code::{OrionCode, ORION_CODE_PARAMETER_INSTANCE}, pcs_impl::*, utils::*, @@ -123,7 +124,7 @@ where let srs = OrionSRS::from_random::<F>(num_vars, ORION_CODE_PARAMETER_INSTANCE, &mut rng); let mut scratch_pad = OrionScratchPad::<F, ComPackF>::default(); - let real_commitment = orion_commit_base(&srs, &random_poly, &mut scratch_pad).unwrap(); + let real_commitment = orion_commit_base_field(&srs, &random_poly, &mut scratch_pad).unwrap(); let dumb_commitment = dumb_commit::<F, ComPackF>(&srs, &random_poly); assert_eq!(real_commitment, dumb_commitment); @@ -165,9 +166,9 @@ where let srs = OrionSRS::from_random::<F>(num_vars, ORION_CODE_PARAMETER_INSTANCE, &mut rng); let mut scratch_pad = OrionScratchPad::<F, ComPackF>::default(); - let commitment = orion_commit_base(&srs, &random_poly, &mut scratch_pad).unwrap(); + let commitment = orion_commit_base_field(&srs, &random_poly, &mut scratch_pad).unwrap(); - let (_, opening) = orion_open::<F, EvalF, ComPackF, OpenPackF, _>( + let (_, opening) = orion_open_base_field::<F, EvalF, ComPackF, OpenPackF, _>( &srs, &random_poly, &random_point, From a84e286b0d4560ce8d4496e6a019c599a9d864bc Mon Sep 17 00:00:00 2001 From: Hang Su <tonyfloater@gmail.com> Date: Sun, 24 Nov 2024 04:00:44 -0500 Subject: [PATCH 06/65] prototype simd version of orion opening, need to get some rest and check impl again --- poly_commit/src/orion.rs | 2 +- poly_commit/src/orion/base_field_impl.rs | 20 ++-- poly_commit/src/orion/pcs_impl.rs | 7 +- poly_commit/src/orion/simd_field_impl.rs | 119 ++++++++++++++++++++++- poly_commit/src/orion/tests.rs | 8 +- poly_commit/src/orion/utils.rs | 25 +++++ poly_commit/src/traits.rs | 4 +- 7 files changed, 161 insertions(+), 24 deletions(-) diff --git a/poly_commit/src/orion.rs b/poly_commit/src/orion.rs index a1eb5dfa..9cac8af9 100644 --- a/poly_commit/src/orion.rs +++ b/poly_commit/src/orion.rs @@ -14,7 +14,7 @@ mod base_field_impl; pub use base_field_impl::{orion_commit_base_field, orion_open_base_field}; mod simd_field_impl; -pub use simd_field_impl::orion_commit_simd_field; +pub use simd_field_impl::{orion_commit_simd_field, orion_open_simd_field}; mod serde; diff --git a/poly_commit/src/orion/base_field_impl.rs b/poly_commit/src/orion/base_field_impl.rs index 4a736bc3..cf1db8cb 100644 --- a/poly_commit/src/orion/base_field_impl.rs +++ b/poly_commit/src/orion/base_field_impl.rs @@ -8,13 +8,12 @@ use crate::{ orion::{ pcs_impl::{commit_encoded, orion_mt_openings}, utils::transpose_in_place, + OrionCommitment, OrionProof, OrionResult, OrionSRS, OrionScratchPad, }, traits::TensorCodeIOPPCS, SubsetSumLUTs, PCS_SOUNDNESS_BITS, }; -use super::{OrionCommitment, OrionProof, OrionResult, OrionSRS, OrionScratchPad}; - #[inline(always)] fn transpose_and_pack<F, PackF>(evaluations: &mut [F], row_num: usize) -> Vec<PackF> where @@ -75,7 +74,7 @@ where T: Transcript<EvalF>, { let (row_num, msg_size) = OrionSRS::evals_shape::<F>(poly.get_num_vars()); - let num_of_vars_in_msg = msg_size.ilog2() as usize; + let num_vars_in_row = row_num.ilog2() as usize; // NOTE: transpose evaluations for linear combinations in evaulation/proximity tests let mut evals = poly.coeffs.clone(); @@ -85,17 +84,17 @@ where drop(evals); // NOTE: declare the look up tables for column sums - let packed_rows = row_num / OpenPackF::PACK_SIZE; - let mut luts = SubsetSumLUTs::new(OpenPackF::PACK_SIZE, packed_rows); + let table_num = row_num / OpenPackF::PACK_SIZE; + let mut luts = SubsetSumLUTs::<EvalF>::new(OpenPackF::PACK_SIZE, table_num); // NOTE: working on evaluation response of tensor code IOP based PCS let mut eval_row = vec![EvalF::ZERO; msg_size]; - let eq_col_coeffs = EqPolynomial::build_eq_x_r(&point[num_of_vars_in_msg..]); + let eq_col_coeffs = EqPolynomial::build_eq_x_r(&point[point.len() - num_vars_in_row..]); luts.build(&eq_col_coeffs); packed_evals - .chunks(packed_rows) + .chunks(table_num) .zip(eval_row.iter_mut()) .for_each(|(p_col, res)| *res = luts.lookup_and_sum(p_col)); @@ -109,7 +108,7 @@ where luts.build(&random_coeffs); packed_evals - .chunks(packed_rows) + .chunks(table_num) .zip(row_buffer.iter_mut()) .for_each(|(p_col, res)| *res = luts.lookup_and_sum(p_col)); }); @@ -119,14 +118,13 @@ where let mut scratch = vec![EvalF::ZERO; msg_size]; let eval = MultiLinearPoly::evaluate_with_buffer( &eval_row, - &point[..num_of_vars_in_msg], + &point[..point.len() - num_vars_in_row], &mut scratch, ); drop(scratch); // NOTE: MT opening for point queries - let leaf_range = row_num / tree::leaf_adic::<F>(); - let query_openings = orion_mt_openings(pk, leaf_range, transcript, scratch_pad); + let query_openings = orion_mt_openings(pk, transcript, scratch_pad); ( eval, diff --git a/poly_commit/src/orion/pcs_impl.rs b/poly_commit/src/orion/pcs_impl.rs index 528bb0aa..b63ce6c7 100644 --- a/poly_commit/src/orion/pcs_impl.rs +++ b/poly_commit/src/orion/pcs_impl.rs @@ -50,7 +50,6 @@ where #[inline(always)] pub(crate) fn orion_mt_openings<F, EvalF, ComPackF, T>( pk: &OrionSRS, - leaf_range: usize, transcript: &mut T, scratch_pad: &OrionScratchPad<F, ComPackF>, ) -> Vec<tree::RangePath> @@ -60,6 +59,8 @@ where ComPackF: SimdField<Scalar = F>, T: Transcript<EvalF>, { + let leaves_in_range_opening = OrionSRS::LEAVES_IN_RANGE_OPENING; + // NOTE: MT opening for point queries let query_num = pk.query_complexity(PCS_SOUNDNESS_BITS); let query_indices = transcript.generate_challenge_index_vector(query_num); @@ -67,8 +68,8 @@ where .iter() .map(|qi| { let index = *qi % pk.codeword_len(); - let left = index * leaf_range; - let right = left + leaf_range - 1; + let left = index * leaves_in_range_opening; + let right = left + leaves_in_range_opening - 1; scratch_pad .interleaved_alphabet_commitment diff --git a/poly_commit/src/orion/simd_field_impl.rs b/poly_commit/src/orion/simd_field_impl.rs index ed0d2362..9d91e1e5 100644 --- a/poly_commit/src/orion/simd_field_impl.rs +++ b/poly_commit/src/orion/simd_field_impl.rs @@ -1,9 +1,18 @@ -use arith::{Field, SimdField}; -use polynomials::MultiLinearPoly; +use std::ops::Mul; -use crate::{orion::utils::transpose_in_place, traits::TensorCodeIOPPCS}; +use arith::{Field, SimdField}; +use polynomials::{EqPolynomial, MultiLinearPoly}; +use transcript::Transcript; -use super::{pcs_impl::commit_encoded, OrionCommitment, OrionResult, OrionSRS, OrionScratchPad}; +use crate::{ + orion::{ + pcs_impl::{commit_encoded, orion_mt_openings}, + utils::transpose_in_place, + OrionCommitment, OrionProof, OrionResult, OrionSRS, OrionScratchPad, + }, + traits::TensorCodeIOPPCS, + SubsetSumLUTs, PCS_SOUNDNESS_BITS, +}; #[inline(always)] fn transpose_and_pack_simd<F, CircuitF, PackF>( @@ -58,3 +67,105 @@ where commit_encoded(pk, &packed_evals, scratch_pad, packed_rows, msg_size) } + +#[inline(always)] +fn transpose_and_shuffle_simd<F, CircuitF, PackF>( + evaluations: &mut [CircuitF], + row_num: usize, +) -> Vec<PackF> +where + F: Field, + CircuitF: SimdField<Scalar = F>, + PackF: SimdField<Scalar = F>, +{ + // NOTE: pre transpose evaluations + let mut scratch = vec![CircuitF::ZERO; evaluations.len()]; + transpose_in_place(evaluations, &mut scratch, row_num); + drop(scratch); + + // NOTE: reshuffle the transposed matrix, from SIMD over row to SIMD over col + let mut scratch = vec![F::ZERO; CircuitF::PACK_SIZE * PackF::PACK_SIZE]; + evaluations + .chunks(PackF::PACK_SIZE) + .flat_map(|circuit_simd_chunk| -> Vec<PackF> { + let mut temp: Vec<F> = circuit_simd_chunk.iter().flat_map(|c| c.unpack()).collect(); + transpose_in_place(&mut temp, &mut scratch, PackF::PACK_SIZE); + temp.chunks(PackF::PACK_SIZE).map(PackF::pack).collect() + }) + .collect() +} + +// NOTE: this implementation doesn't quite align with opening for +// multilinear polynomials over base field, +// as this directly plug into GKR argument system. +// In that context, there is no need to evaluate, +// as evaluation statement can be reduced on the verifier side. +pub fn orion_open_simd_field<F, CircuitF, EvalF, CircuitEvalF, ComPackF, OpenPackF, T>( + pk: &OrionSRS, + poly: &MultiLinearPoly<CircuitF>, + point: &[EvalF], + transcript: &mut T, + scratch_pad: &mut OrionScratchPad<F, ComPackF>, +) -> OrionProof<CircuitEvalF> +where + F: Field, + CircuitF: SimdField<Scalar = F>, + EvalF: Field + From<F> + Mul<F, Output = EvalF>, + CircuitEvalF: SimdField<Scalar = EvalF>, + ComPackF: SimdField<Scalar = F>, + OpenPackF: SimdField<Scalar = F>, + T: Transcript<EvalF>, +{ + assert_eq!(CircuitF::PACK_SIZE, CircuitEvalF::PACK_SIZE); + + let (row_num, msg_size) = OrionSRS::evals_shape::<CircuitF>(poly.get_num_vars()); + let num_vars_in_row = row_num.ilog2() as usize; + + // NOTE: transpose and shuffle evaluations (repack evaluations in another direction) + // for linear combinations in evaulation/proximity tests + let mut evals = poly.coeffs.clone(); + assert_eq!(evals.len() * CircuitF::PACK_SIZE % OpenPackF::PACK_SIZE, 0); + + let packed_shuffled_evals: Vec<OpenPackF> = transpose_and_shuffle_simd(&mut evals, row_num); + drop(evals); + + // NOTE: declare the look up tables for column sums + let tables_num = row_num / OpenPackF::PACK_SIZE; + let mut luts = SubsetSumLUTs::<EvalF>::new(OpenPackF::PACK_SIZE, tables_num); + + // NOTE: working on evaluation response of tensor code IOP based PCS + let mut eval_row = vec![CircuitEvalF::ZERO; msg_size]; + + let eq_col_coeffs = EqPolynomial::build_eq_x_r(&point[point.len() - num_vars_in_row..]); + luts.build(&eq_col_coeffs); + + packed_shuffled_evals + .chunks(tables_num * CircuitEvalF::PACK_SIZE) + .zip(eval_row.iter_mut()) + .for_each(|(p_col, res)| *res = luts.lookup_and_sum_simd(p_col)); + + // NOTE: draw random linear combination out + // and compose proximity response(s) of tensor code IOP based PCS + let proximity_test_num = pk.proximity_repetitions::<EvalF>(PCS_SOUNDNESS_BITS); + let mut proximity_rows = vec![vec![CircuitEvalF::ZERO; msg_size]; proximity_test_num]; + + proximity_rows.iter_mut().for_each(|row_buffer| { + let random_coeffs = transcript.generate_challenge_field_elements(row_num); + luts.build(&random_coeffs); + + packed_shuffled_evals + .chunks(tables_num * CircuitEvalF::PACK_SIZE) + .zip(row_buffer.iter_mut()) + .for_each(|(p_col, res)| *res = luts.lookup_and_sum_simd(p_col)); + }); + drop(luts); + + // NOTE: MT opening for point queries + let query_openings = orion_mt_openings(pk, transcript, scratch_pad); + + OrionProof { + eval_row, + proximity_rows, + query_openings, + } +} diff --git a/poly_commit/src/orion/tests.rs b/poly_commit/src/orion/tests.rs index 36536802..f94548d2 100644 --- a/poly_commit/src/orion/tests.rs +++ b/poly_commit/src/orion/tests.rs @@ -138,7 +138,7 @@ fn test_orion_commit_consistency() { }); } -fn test_orion_pcs_full_e2e_generics<F, EvalF, ComPackF, OpenPackF>(num_vars: usize) +fn test_orion_pcs_base_full_e2e_generics<F, EvalF, ComPackF, OpenPackF>(num_vars: usize) where F: Field, EvalF: Field + Mul<F, Output = EvalF> + From<F>, @@ -187,9 +187,9 @@ where } #[test] -fn test_orion_pcs_full_e2e() { +fn test_orion_pcs_base_full_e2e() { (19..=25).for_each(|num_vars| { - test_orion_pcs_full_e2e_generics::<GF2, GF2_128, GF2x64, GF2x8>(num_vars); - test_orion_pcs_full_e2e_generics::<GF2, GF2_128, GF2x128, GF2x8>(num_vars); + test_orion_pcs_base_full_e2e_generics::<GF2, GF2_128, GF2x64, GF2x8>(num_vars); + test_orion_pcs_base_full_e2e_generics::<GF2, GF2_128, GF2x128, GF2x8>(num_vars); }); } diff --git a/poly_commit/src/orion/utils.rs b/poly_commit/src/orion/utils.rs index 79b33f13..4bf8fcc0 100644 --- a/poly_commit/src/orion/utils.rs +++ b/poly_commit/src/orion/utils.rs @@ -193,4 +193,29 @@ impl<F: Field> SubsetSumLUTs<F> { .map(|(t_i, index)| t_i[index.as_u32_unchecked() as usize]) .sum() } + + #[inline] + pub fn lookup_and_sum_simd<BitF, EntryF, SimdF>(&self, simd_indices: &[EntryF]) -> SimdF + where + BitF: Field, + EntryF: SimdField<Scalar = BitF>, + SimdF: SimdField<Scalar = F>, + { + assert_eq!(EntryF::FIELD_SIZE, 1); + assert_eq!(EntryF::PACK_SIZE, self.entry_bits); + assert_eq!(simd_indices.len(), self.tables.len() * SimdF::PACK_SIZE); + + let mut elts = vec![F::ZERO; SimdF::PACK_SIZE]; + + self.tables + .iter() + .zip(simd_indices.chunks(SimdF::PACK_SIZE)) + .for_each(|(t_i, indices)| { + elts.iter_mut() + .zip(indices.iter()) + .for_each(|(elem, index)| *elem += t_i[index.as_u32_unchecked() as usize]) + }); + + SimdF::pack(&elts) + } } diff --git a/poly_commit/src/traits.rs b/poly_commit/src/traits.rs index daa25895..4491f84f 100644 --- a/poly_commit/src/traits.rs +++ b/poly_commit/src/traits.rs @@ -145,12 +145,14 @@ pub trait PCSForExpanderGKR<C: GKRFieldConfig, T: Transcript<C::ChallengeField>> } pub(crate) trait TensorCodeIOPPCS { + const LEAVES_IN_RANGE_OPENING: usize = 2; + fn codeword_len(&self) -> usize; fn hamming_weight(&self) -> f64; fn evals_shape<F: Field>(num_vars: usize) -> (usize, usize) { - let elems_for_smallest_tree = tree::leaf_adic::<F>() * 2; + let elems_for_smallest_tree = tree::leaf_adic::<F>() * Self::LEAVES_IN_RANGE_OPENING; let row_num: usize = elems_for_smallest_tree; let msg_size: usize = (1 << num_vars) / row_num; From 14f853271438a922d948fadc17a3954533dddd25 Mon Sep 17 00:00:00 2001 From: Hang Su <tonyfloater@gmail.com> Date: Sun, 24 Nov 2024 04:24:38 -0500 Subject: [PATCH 07/65] minor, move original orion verify to base field impl --- poly_commit/src/orion.rs | 7 +- poly_commit/src/orion/base_field_impl.rs | 99 ++++++++++++- poly_commit/src/orion/pcs_impl.rs | 174 ----------------------- poly_commit/src/orion/simd_field_impl.rs | 3 +- poly_commit/src/orion/tests.rs | 20 +-- poly_commit/src/orion/utils.rs | 74 +++++++++- 6 files changed, 184 insertions(+), 193 deletions(-) delete mode 100644 poly_commit/src/orion/pcs_impl.rs diff --git a/poly_commit/src/orion.rs b/poly_commit/src/orion.rs index 9cac8af9..bab8bec5 100644 --- a/poly_commit/src/orion.rs +++ b/poly_commit/src/orion.rs @@ -7,11 +7,10 @@ pub use utils::{ mod linear_code; pub use linear_code::{OrionCodeParameter, ORION_CODE_PARAMETER_INSTANCE}; -mod pcs_impl; -pub use pcs_impl::orion_verify; - mod base_field_impl; -pub use base_field_impl::{orion_commit_base_field, orion_open_base_field}; +pub use base_field_impl::{ + orion_commit_base_field, orion_open_base_field, orion_verify_base_field, +}; mod simd_field_impl; pub use simd_field_impl::{orion_commit_simd_field, orion_open_simd_field}; diff --git a/poly_commit/src/orion/base_field_impl.rs b/poly_commit/src/orion/base_field_impl.rs index cf1db8cb..caa251c9 100644 --- a/poly_commit/src/orion/base_field_impl.rs +++ b/poly_commit/src/orion/base_field_impl.rs @@ -1,3 +1,4 @@ +use std::iter; use std::ops::Mul; use arith::{Field, SimdField}; @@ -6,8 +7,7 @@ use transcript::Transcript; use crate::{ orion::{ - pcs_impl::{commit_encoded, orion_mt_openings}, - utils::transpose_in_place, + utils::{commit_encoded, orion_mt_openings, transpose_in_place}, OrionCommitment, OrionProof, OrionResult, OrionSRS, OrionScratchPad, }, traits::TensorCodeIOPPCS, @@ -135,3 +135,98 @@ where }, ) } + +pub fn orion_verify_base_field<F, EvalF, ComPackF, OpenPackF, T>( + vk: &OrionSRS, + commitment: &OrionCommitment, + point: &[EvalF], + evaluation: EvalF, + transcript: &mut T, + proof: &OrionProof<EvalF>, +) -> bool +where + F: Field, + EvalF: Field + From<F> + Mul<F, Output = EvalF>, + ComPackF: SimdField<Scalar = F>, + OpenPackF: SimdField<Scalar = F>, + T: Transcript<EvalF>, +{ + let (row_num, msg_size) = OrionSRS::evals_shape::<F>(point.len()); + let num_vars_in_msg = msg_size.ilog2() as usize; + + // NOTE: working on evaluation response, evaluate the rest of the response + let mut scratch = vec![EvalF::ZERO; msg_size]; + let final_eval = MultiLinearPoly::evaluate_with_buffer( + &proof.eval_row, + &point[..num_vars_in_msg], + &mut scratch, + ); + if final_eval != evaluation { + return false; + } + + // NOTE: working on proximity responses, draw random linear combinations + // then draw query points from fiat shamir transcripts + let proximity_test_num = vk.proximity_repetitions::<EvalF>(PCS_SOUNDNESS_BITS); + let random_linear_combinations: Vec<Vec<EvalF>> = (0..proximity_test_num) + .map(|_| transcript.generate_challenge_field_elements(row_num)) + .collect(); + let query_num = vk.query_complexity(PCS_SOUNDNESS_BITS); + let query_indices = transcript.generate_challenge_index_vector(query_num); + + // NOTE: check consistency in MT in the opening trees and against the commitment tree + let leaf_range = row_num / tree::leaf_adic::<F>(); + let mt_consistency = + query_indices + .iter() + .zip(proof.query_openings.iter()) + .all(|(&qi, range_path)| { + let index = qi % vk.codeword_len(); + range_path.verify(commitment) && index == range_path.left / leaf_range + }); + if !mt_consistency { + return false; + } + + // NOTE: prepare the interleaved alphabets from the MT paths, + // but pack them back into look up table acceptable formats + let packed_interleaved_alphabets: Vec<_> = proof + .query_openings + .iter() + .map(|p| -> Vec<_> { + p.unpack_field_elems::<F, ComPackF>() + .chunks(OpenPackF::PACK_SIZE) + .map(OpenPackF::pack) + .collect() + }) + .collect(); + + // NOTE: encode the proximity/evaluation responses, + // check againts all challenged indices by check alphabets against + // linear combined interleaved alphabet + let mut luts = SubsetSumLUTs::new(OpenPackF::PACK_SIZE, row_num / OpenPackF::PACK_SIZE); + assert_eq!(row_num % OpenPackF::PACK_SIZE, 0); + + let eq_linear_combination = EqPolynomial::build_eq_x_r(&point[num_vars_in_msg..]); + random_linear_combinations + .iter() + .zip(proof.proximity_rows.iter()) + .chain(iter::once((&eq_linear_combination, &proof.eval_row))) + .all(|(rl, msg)| { + let codeword = match vk.code_instance.encode(msg) { + Ok(c) => c, + _ => return false, + }; + + luts.build(rl); + + query_indices + .iter() + .zip(packed_interleaved_alphabets.iter()) + .all(|(&qi, interleaved_alphabet)| { + let index = qi % vk.codeword_len(); + let alphabet = luts.lookup_and_sum(interleaved_alphabet); + alphabet == codeword[index] + }) + }) +} diff --git a/poly_commit/src/orion/pcs_impl.rs b/poly_commit/src/orion/pcs_impl.rs deleted file mode 100644 index b63ce6c7..00000000 --- a/poly_commit/src/orion/pcs_impl.rs +++ /dev/null @@ -1,174 +0,0 @@ -use std::iter; -use std::ops::Mul; - -use arith::{Field, SimdField}; -use polynomials::{EqPolynomial, MultiLinearPoly}; -use transcript::Transcript; - -use crate::{traits::TensorCodeIOPPCS, PCS_SOUNDNESS_BITS}; - -use super::utils::*; - -#[inline(always)] -pub(crate) fn commit_encoded<F, PackF>( - pk: &OrionSRS, - packed_evals: &[PackF], - scratch_pad: &mut OrionScratchPad<F, PackF>, - packed_rows: usize, - msg_size: usize, -) -> OrionResult<OrionCommitment> -where - F: Field, - PackF: SimdField<Scalar = F>, -{ - // NOTE: packed codeword buffer and encode over packed field - let mut packed_interleaved_codewords = vec![PackF::ZERO; packed_rows * pk.codeword_len()]; - packed_evals - .chunks(msg_size) - .zip(packed_interleaved_codewords.chunks_mut(pk.codeword_len())) - .try_for_each(|(evals, codeword)| pk.code_instance.encode_in_place(evals, codeword))?; - - // NOTE: transpose codeword s.t., the matrix has codewords being columns - let mut scratch = vec![PackF::ZERO; packed_rows * pk.codeword_len()]; - transpose_in_place(&mut packed_interleaved_codewords, &mut scratch, packed_rows); - drop(scratch); - - // NOTE: commit the interleaved codeword - // we just directly commit to the packed field elements to leaves - // Also note, when codeword is not power of 2 length, pad to nearest po2 - // to commit by merkle tree - if !packed_interleaved_codewords.len().is_power_of_two() { - let aligned_po2_len = packed_interleaved_codewords.len().next_power_of_two(); - packed_interleaved_codewords.resize(aligned_po2_len, PackF::ZERO); - } - scratch_pad.interleaved_alphabet_commitment = - tree::Tree::compact_new_with_packed_field_elems::<F, PackF>(packed_interleaved_codewords); - - Ok(scratch_pad.interleaved_alphabet_commitment.root()) -} - -#[inline(always)] -pub(crate) fn orion_mt_openings<F, EvalF, ComPackF, T>( - pk: &OrionSRS, - transcript: &mut T, - scratch_pad: &OrionScratchPad<F, ComPackF>, -) -> Vec<tree::RangePath> -where - F: Field, - EvalF: Field + From<F> + Mul<F, Output = EvalF>, - ComPackF: SimdField<Scalar = F>, - T: Transcript<EvalF>, -{ - let leaves_in_range_opening = OrionSRS::LEAVES_IN_RANGE_OPENING; - - // NOTE: MT opening for point queries - let query_num = pk.query_complexity(PCS_SOUNDNESS_BITS); - let query_indices = transcript.generate_challenge_index_vector(query_num); - query_indices - .iter() - .map(|qi| { - let index = *qi % pk.codeword_len(); - let left = index * leaves_in_range_opening; - let right = left + leaves_in_range_opening - 1; - - scratch_pad - .interleaved_alphabet_commitment - .range_query(left, right) - }) - .collect() -} - -pub fn orion_verify<F, EvalF, ComPackF, OpenPackF, T>( - vk: &OrionSRS, - commitment: &OrionCommitment, - point: &[EvalF], - evaluation: EvalF, - transcript: &mut T, - proof: &OrionProof<EvalF>, -) -> bool -where - F: Field, - EvalF: Field + From<F> + Mul<F, Output = EvalF>, - ComPackF: SimdField<Scalar = F>, - OpenPackF: SimdField<Scalar = F>, - T: Transcript<EvalF>, -{ - let (row_num, msg_size) = OrionSRS::evals_shape::<F>(point.len()); - let num_of_vars_in_msg = msg_size.ilog2() as usize; - - // NOTE: working on evaluation response, evaluate the rest of the response - let mut scratch = vec![EvalF::ZERO; msg_size]; - let final_eval = MultiLinearPoly::evaluate_with_buffer( - &proof.eval_row, - &point[..num_of_vars_in_msg], - &mut scratch, - ); - if final_eval != evaluation { - return false; - } - - // NOTE: working on proximity responses, draw random linear combinations - // then draw query points from fiat shamir transcripts - let proximity_test_num = vk.proximity_repetitions::<EvalF>(PCS_SOUNDNESS_BITS); - let random_linear_combinations: Vec<Vec<EvalF>> = (0..proximity_test_num) - .map(|_| transcript.generate_challenge_field_elements(row_num)) - .collect(); - let query_num = vk.query_complexity(PCS_SOUNDNESS_BITS); - let query_indices = transcript.generate_challenge_index_vector(query_num); - - // NOTE: check consistency in MT in the opening trees and against the commitment tree - let leaf_range = row_num / tree::leaf_adic::<F>(); - let mt_consistency = - query_indices - .iter() - .zip(proof.query_openings.iter()) - .all(|(&qi, range_path)| { - let index = qi % vk.codeword_len(); - range_path.verify(commitment) && index == range_path.left / leaf_range - }); - if !mt_consistency { - return false; - } - - // NOTE: prepare the interleaved alphabets from the MT paths, - // but pack them back into look up table acceptable formats - let packed_interleaved_alphabets: Vec<_> = proof - .query_openings - .iter() - .map(|p| -> Vec<_> { - p.unpack_field_elems::<F, ComPackF>() - .chunks(OpenPackF::PACK_SIZE) - .map(OpenPackF::pack) - .collect() - }) - .collect(); - - // NOTE: encode the proximity/evaluation responses, - // check againts all challenged indices by check alphabets against - // linear combined interleaved alphabet - let mut luts = SubsetSumLUTs::new(OpenPackF::PACK_SIZE, row_num / OpenPackF::PACK_SIZE); - assert_eq!(row_num % OpenPackF::PACK_SIZE, 0); - - let eq_linear_combination = EqPolynomial::build_eq_x_r(&point[num_of_vars_in_msg..]); - random_linear_combinations - .iter() - .zip(proof.proximity_rows.iter()) - .chain(iter::once((&eq_linear_combination, &proof.eval_row))) - .all(|(rl, msg)| { - let codeword = match vk.code_instance.encode(msg) { - Ok(c) => c, - _ => return false, - }; - - luts.build(rl); - - query_indices - .iter() - .zip(packed_interleaved_alphabets.iter()) - .all(|(&qi, interleaved_alphabet)| { - let index = qi % vk.codeword_len(); - let alphabet = luts.lookup_and_sum(interleaved_alphabet); - alphabet == codeword[index] - }) - }) -} diff --git a/poly_commit/src/orion/simd_field_impl.rs b/poly_commit/src/orion/simd_field_impl.rs index 9d91e1e5..446d2151 100644 --- a/poly_commit/src/orion/simd_field_impl.rs +++ b/poly_commit/src/orion/simd_field_impl.rs @@ -6,8 +6,7 @@ use transcript::Transcript; use crate::{ orion::{ - pcs_impl::{commit_encoded, orion_mt_openings}, - utils::transpose_in_place, + utils::{commit_encoded, orion_mt_openings, transpose_in_place}, OrionCommitment, OrionProof, OrionResult, OrionSRS, OrionScratchPad, }, traits::TensorCodeIOPPCS, diff --git a/poly_commit/src/orion/tests.rs b/poly_commit/src/orion/tests.rs index f94548d2..0fe139f9 100644 --- a/poly_commit/src/orion/tests.rs +++ b/poly_commit/src/orion/tests.rs @@ -9,9 +9,8 @@ use transcript::{BytesHashTranscript, Keccak256hasher, Transcript}; use crate::{ orion::{ - base_field_impl::{orion_commit_base_field, orion_open_base_field}, + base_field_impl::*, linear_code::{OrionCode, ORION_CODE_PARAMETER_INSTANCE}, - pcs_impl::*, utils::*, }, traits::TensorCodeIOPPCS, @@ -85,7 +84,10 @@ fn test_orion_code() { }); } -fn dumb_commit<F, ComPackF>(orion_srs: &OrionSRS, poly: &MultiLinearPoly<F>) -> OrionCommitment +fn dumb_commit_base_field<F, ComPackF>( + orion_srs: &OrionSRS, + poly: &MultiLinearPoly<F>, +) -> OrionCommitment where F: Field, ComPackF: SimdField<Scalar = F>, @@ -113,7 +115,7 @@ where interleaved_alphabet_tree.root() } -fn test_orion_commit_consistency_generic<F, ComPackF>(num_vars: usize) +fn test_orion_commit_base_field_consistency_generic<F, ComPackF>(num_vars: usize) where F: Field, ComPackF: SimdField<Scalar = F>, @@ -125,16 +127,16 @@ where let mut scratch_pad = OrionScratchPad::<F, ComPackF>::default(); let real_commitment = orion_commit_base_field(&srs, &random_poly, &mut scratch_pad).unwrap(); - let dumb_commitment = dumb_commit::<F, ComPackF>(&srs, &random_poly); + let dumb_commitment = dumb_commit_base_field::<F, ComPackF>(&srs, &random_poly); assert_eq!(real_commitment, dumb_commitment); } #[test] -fn test_orion_commit_consistency() { +fn test_orion_commit_base_field_consistency() { (19..=25).for_each(|num_vars| { - test_orion_commit_consistency_generic::<GF2, GF2x64>(num_vars); - test_orion_commit_consistency_generic::<GF2, GF2x128>(num_vars); + test_orion_commit_base_field_consistency_generic::<GF2, GF2x64>(num_vars); + test_orion_commit_base_field_consistency_generic::<GF2, GF2x128>(num_vars); }); } @@ -176,7 +178,7 @@ where &scratch_pad, ); - assert!(orion_verify::<F, EvalF, ComPackF, OpenPackF, _>( + assert!(orion_verify_base_field::<F, EvalF, ComPackF, OpenPackF, _>( &srs, &commitment, &random_point, diff --git a/poly_commit/src/orion/utils.rs b/poly_commit/src/orion/utils.rs index 4bf8fcc0..09a5819f 100644 --- a/poly_commit/src/orion/utils.rs +++ b/poly_commit/src/orion/utils.rs @@ -1,9 +1,10 @@ -use std::marker::PhantomData; +use std::{marker::PhantomData, ops::Mul}; use arith::{Field, FieldSerdeError, SimdField}; use thiserror::Error; +use transcript::Transcript; -use crate::{traits::TensorCodeIOPPCS, StructuredReferenceString}; +use crate::{traits::TensorCodeIOPPCS, StructuredReferenceString, PCS_SOUNDNESS_BITS}; use super::linear_code::{OrionCode, OrionCodeParameter}; @@ -101,6 +102,75 @@ pub struct OrionProof<EvalF: Field> { pub query_openings: Vec<tree::RangePath>, } +#[inline(always)] +pub(crate) fn commit_encoded<F, PackF>( + pk: &OrionSRS, + packed_evals: &[PackF], + scratch_pad: &mut OrionScratchPad<F, PackF>, + packed_rows: usize, + msg_size: usize, +) -> OrionResult<OrionCommitment> +where + F: Field, + PackF: SimdField<Scalar = F>, +{ + // NOTE: packed codeword buffer and encode over packed field + let mut packed_interleaved_codewords = vec![PackF::ZERO; packed_rows * pk.codeword_len()]; + packed_evals + .chunks(msg_size) + .zip(packed_interleaved_codewords.chunks_mut(pk.codeword_len())) + .try_for_each(|(evals, codeword)| pk.code_instance.encode_in_place(evals, codeword))?; + + // NOTE: transpose codeword s.t., the matrix has codewords being columns + let mut scratch = vec![PackF::ZERO; packed_rows * pk.codeword_len()]; + transpose_in_place(&mut packed_interleaved_codewords, &mut scratch, packed_rows); + drop(scratch); + + // NOTE: commit the interleaved codeword + // we just directly commit to the packed field elements to leaves + // Also note, when codeword is not power of 2 length, pad to nearest po2 + // to commit by merkle tree + if !packed_interleaved_codewords.len().is_power_of_two() { + let aligned_po2_len = packed_interleaved_codewords.len().next_power_of_two(); + packed_interleaved_codewords.resize(aligned_po2_len, PackF::ZERO); + } + scratch_pad.interleaved_alphabet_commitment = + tree::Tree::compact_new_with_packed_field_elems::<F, PackF>(packed_interleaved_codewords); + + Ok(scratch_pad.interleaved_alphabet_commitment.root()) +} + +#[inline(always)] +pub(crate) fn orion_mt_openings<F, EvalF, ComPackF, T>( + pk: &OrionSRS, + transcript: &mut T, + scratch_pad: &OrionScratchPad<F, ComPackF>, +) -> Vec<tree::RangePath> +where + F: Field, + EvalF: Field + From<F> + Mul<F, Output = EvalF>, + ComPackF: SimdField<Scalar = F>, + T: Transcript<EvalF>, +{ + let leaves_in_range_opening = OrionSRS::LEAVES_IN_RANGE_OPENING; + + // NOTE: MT opening for point queries + let query_num = pk.query_complexity(PCS_SOUNDNESS_BITS); + let query_indices = transcript.generate_challenge_index_vector(query_num); + query_indices + .iter() + .map(|qi| { + let index = *qi % pk.codeword_len(); + let left = index * leaves_in_range_opening; + let right = left + leaves_in_range_opening - 1; + + scratch_pad + .interleaved_alphabet_commitment + .range_query(left, right) + }) + .collect() +} + /* * IMPLEMENTATIONS FOR MATRIX TRANSPOSE */ From a81d45d2f41f448c7053a57771f20cb72d3c283a Mon Sep 17 00:00:00 2001 From: Hang Su <tonyfloater@gmail.com> Date: Sun, 24 Nov 2024 04:53:09 -0500 Subject: [PATCH 08/65] minor, renaming from Circuit*F to Simd*F --- poly_commit/src/orion/simd_field_impl.rs | 59 +++++++++++------------- 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/poly_commit/src/orion/simd_field_impl.rs b/poly_commit/src/orion/simd_field_impl.rs index 446d2151..0f3b4cbc 100644 --- a/poly_commit/src/orion/simd_field_impl.rs +++ b/poly_commit/src/orion/simd_field_impl.rs @@ -14,41 +14,38 @@ use crate::{ }; #[inline(always)] -fn transpose_and_pack_simd<F, CircuitF, PackF>( - evaluations: &mut [CircuitF], - row_num: usize, -) -> Vec<PackF> +fn transpose_and_pack_simd<F, SimdF, PackF>(evaluations: &mut [SimdF], row_num: usize) -> Vec<PackF> where F: Field, - CircuitF: SimdField<Scalar = F>, + SimdF: SimdField<Scalar = F>, PackF: SimdField<Scalar = F>, { // NOTE: pre transpose evaluations - let mut scratch = vec![CircuitF::ZERO; evaluations.len()]; + let mut scratch = vec![SimdF::ZERO; evaluations.len()]; transpose_in_place(evaluations, &mut scratch, row_num); drop(scratch); // NOTE: SIMD pack each row of transposed matrix - let relative_pack_size = PackF::PACK_SIZE / CircuitF::PACK_SIZE; + let relative_pack_size = PackF::PACK_SIZE / SimdF::PACK_SIZE; evaluations .chunks(relative_pack_size) .map(SimdField::pack_from_simd) .collect() } -pub fn orion_commit_simd_field<F, CircuitF, ComPackF>( +pub fn orion_commit_simd_field<F, SimdF, ComPackF>( pk: &OrionSRS, - poly: &MultiLinearPoly<CircuitF>, + poly: &MultiLinearPoly<SimdF>, scratch_pad: &mut OrionScratchPad<F, ComPackF>, ) -> OrionResult<OrionCommitment> where F: Field, - CircuitF: SimdField<Scalar = F>, + SimdF: SimdField<Scalar = F>, ComPackF: SimdField<Scalar = F>, { - let (row_num, msg_size) = OrionSRS::evals_shape::<CircuitF>(poly.get_num_vars()); - let relative_pack_size = ComPackF::PACK_SIZE / CircuitF::PACK_SIZE; - assert_eq!(ComPackF::PACK_SIZE % CircuitF::PACK_SIZE, 0); + let (row_num, msg_size) = OrionSRS::evals_shape::<SimdF>(poly.get_num_vars()); + let relative_pack_size = ComPackF::PACK_SIZE / SimdF::PACK_SIZE; + assert_eq!(ComPackF::PACK_SIZE % SimdF::PACK_SIZE, 0); let packed_rows = row_num / relative_pack_size; assert_eq!(row_num % relative_pack_size, 0); @@ -56,7 +53,7 @@ where let mut evals = poly.coeffs.clone(); assert_eq!(evals.len() % relative_pack_size, 0); - let mut packed_evals = transpose_and_pack_simd::<F, CircuitF, ComPackF>(&mut evals, row_num); + let mut packed_evals = transpose_and_pack_simd::<F, SimdF, ComPackF>(&mut evals, row_num); drop(evals); // NOTE: transpose back to rows of evaluations, but packed @@ -68,22 +65,22 @@ where } #[inline(always)] -fn transpose_and_shuffle_simd<F, CircuitF, PackF>( - evaluations: &mut [CircuitF], +fn transpose_and_shuffle_simd<F, SimdF, PackF>( + evaluations: &mut [SimdF], row_num: usize, ) -> Vec<PackF> where F: Field, - CircuitF: SimdField<Scalar = F>, + SimdF: SimdField<Scalar = F>, PackF: SimdField<Scalar = F>, { // NOTE: pre transpose evaluations - let mut scratch = vec![CircuitF::ZERO; evaluations.len()]; + let mut scratch = vec![SimdF::ZERO; evaluations.len()]; transpose_in_place(evaluations, &mut scratch, row_num); drop(scratch); // NOTE: reshuffle the transposed matrix, from SIMD over row to SIMD over col - let mut scratch = vec![F::ZERO; CircuitF::PACK_SIZE * PackF::PACK_SIZE]; + let mut scratch = vec![F::ZERO; SimdF::PACK_SIZE * PackF::PACK_SIZE]; evaluations .chunks(PackF::PACK_SIZE) .flat_map(|circuit_simd_chunk| -> Vec<PackF> { @@ -99,31 +96,31 @@ where // as this directly plug into GKR argument system. // In that context, there is no need to evaluate, // as evaluation statement can be reduced on the verifier side. -pub fn orion_open_simd_field<F, CircuitF, EvalF, CircuitEvalF, ComPackF, OpenPackF, T>( +pub fn orion_open_simd_field<F, SimdF, EvalF, SimdEvalF, ComPackF, OpenPackF, T>( pk: &OrionSRS, - poly: &MultiLinearPoly<CircuitF>, + poly: &MultiLinearPoly<SimdF>, point: &[EvalF], transcript: &mut T, scratch_pad: &mut OrionScratchPad<F, ComPackF>, -) -> OrionProof<CircuitEvalF> +) -> OrionProof<SimdEvalF> where F: Field, - CircuitF: SimdField<Scalar = F>, + SimdF: SimdField<Scalar = F>, EvalF: Field + From<F> + Mul<F, Output = EvalF>, - CircuitEvalF: SimdField<Scalar = EvalF>, + SimdEvalF: SimdField<Scalar = EvalF>, ComPackF: SimdField<Scalar = F>, OpenPackF: SimdField<Scalar = F>, T: Transcript<EvalF>, { - assert_eq!(CircuitF::PACK_SIZE, CircuitEvalF::PACK_SIZE); + assert_eq!(SimdF::PACK_SIZE, SimdEvalF::PACK_SIZE); - let (row_num, msg_size) = OrionSRS::evals_shape::<CircuitF>(poly.get_num_vars()); + let (row_num, msg_size) = OrionSRS::evals_shape::<SimdF>(poly.get_num_vars()); let num_vars_in_row = row_num.ilog2() as usize; // NOTE: transpose and shuffle evaluations (repack evaluations in another direction) // for linear combinations in evaulation/proximity tests let mut evals = poly.coeffs.clone(); - assert_eq!(evals.len() * CircuitF::PACK_SIZE % OpenPackF::PACK_SIZE, 0); + assert_eq!(evals.len() * SimdF::PACK_SIZE % OpenPackF::PACK_SIZE, 0); let packed_shuffled_evals: Vec<OpenPackF> = transpose_and_shuffle_simd(&mut evals, row_num); drop(evals); @@ -133,27 +130,27 @@ where let mut luts = SubsetSumLUTs::<EvalF>::new(OpenPackF::PACK_SIZE, tables_num); // NOTE: working on evaluation response of tensor code IOP based PCS - let mut eval_row = vec![CircuitEvalF::ZERO; msg_size]; + let mut eval_row = vec![SimdEvalF::ZERO; msg_size]; let eq_col_coeffs = EqPolynomial::build_eq_x_r(&point[point.len() - num_vars_in_row..]); luts.build(&eq_col_coeffs); packed_shuffled_evals - .chunks(tables_num * CircuitEvalF::PACK_SIZE) + .chunks(tables_num * SimdEvalF::PACK_SIZE) .zip(eval_row.iter_mut()) .for_each(|(p_col, res)| *res = luts.lookup_and_sum_simd(p_col)); // NOTE: draw random linear combination out // and compose proximity response(s) of tensor code IOP based PCS let proximity_test_num = pk.proximity_repetitions::<EvalF>(PCS_SOUNDNESS_BITS); - let mut proximity_rows = vec![vec![CircuitEvalF::ZERO; msg_size]; proximity_test_num]; + let mut proximity_rows = vec![vec![SimdEvalF::ZERO; msg_size]; proximity_test_num]; proximity_rows.iter_mut().for_each(|row_buffer| { let random_coeffs = transcript.generate_challenge_field_elements(row_num); luts.build(&random_coeffs); packed_shuffled_evals - .chunks(tables_num * CircuitEvalF::PACK_SIZE) + .chunks(tables_num * SimdEvalF::PACK_SIZE) .zip(row_buffer.iter_mut()) .for_each(|(p_col, res)| *res = luts.lookup_and_sum_simd(p_col)); }); From 4e08b599ec502778a10f1129ff4698d2bfd98fed Mon Sep 17 00:00:00 2001 From: Hang Su <tonyfloater@gmail.com> Date: Sun, 24 Nov 2024 08:20:42 -0500 Subject: [PATCH 09/65] minor, prototype for orion verify for simd field --- poly_commit/src/orion.rs | 4 +- poly_commit/src/orion/base_field_impl.rs | 17 ++-- poly_commit/src/orion/simd_field_impl.rs | 100 ++++++++++++++++++++++- poly_commit/src/orion/utils.rs | 17 ++++ 4 files changed, 124 insertions(+), 14 deletions(-) diff --git a/poly_commit/src/orion.rs b/poly_commit/src/orion.rs index bab8bec5..f1901385 100644 --- a/poly_commit/src/orion.rs +++ b/poly_commit/src/orion.rs @@ -13,7 +13,9 @@ pub use base_field_impl::{ }; mod simd_field_impl; -pub use simd_field_impl::{orion_commit_simd_field, orion_open_simd_field}; +pub use simd_field_impl::{ + orion_commit_simd_field, orion_open_simd_field, orion_verify_simd_field, +}; mod serde; diff --git a/poly_commit/src/orion/base_field_impl.rs b/poly_commit/src/orion/base_field_impl.rs index caa251c9..2819a686 100644 --- a/poly_commit/src/orion/base_field_impl.rs +++ b/poly_commit/src/orion/base_field_impl.rs @@ -7,7 +7,7 @@ use transcript::Transcript; use crate::{ orion::{ - utils::{commit_encoded, orion_mt_openings, transpose_in_place}, + utils::{commit_encoded, orion_mt_openings, orion_mt_verify, transpose_in_place}, OrionCommitment, OrionProof, OrionResult, OrionSRS, OrionScratchPad, }, traits::TensorCodeIOPPCS, @@ -86,6 +86,7 @@ where // NOTE: declare the look up tables for column sums let table_num = row_num / OpenPackF::PACK_SIZE; let mut luts = SubsetSumLUTs::<EvalF>::new(OpenPackF::PACK_SIZE, table_num); + assert_eq!(row_num % OpenPackF::PACK_SIZE, 0); // NOTE: working on evaluation response of tensor code IOP based PCS let mut eval_row = vec![EvalF::ZERO; msg_size]; @@ -175,16 +176,7 @@ where let query_indices = transcript.generate_challenge_index_vector(query_num); // NOTE: check consistency in MT in the opening trees and against the commitment tree - let leaf_range = row_num / tree::leaf_adic::<F>(); - let mt_consistency = - query_indices - .iter() - .zip(proof.query_openings.iter()) - .all(|(&qi, range_path)| { - let index = qi % vk.codeword_len(); - range_path.verify(commitment) && index == range_path.left / leaf_range - }); - if !mt_consistency { + if !orion_mt_verify(vk, &query_indices, &proof.query_openings, commitment) { return false; } @@ -204,7 +196,8 @@ where // NOTE: encode the proximity/evaluation responses, // check againts all challenged indices by check alphabets against // linear combined interleaved alphabet - let mut luts = SubsetSumLUTs::new(OpenPackF::PACK_SIZE, row_num / OpenPackF::PACK_SIZE); + let tables_num = row_num / OpenPackF::PACK_SIZE; + let mut luts = SubsetSumLUTs::new(OpenPackF::PACK_SIZE, tables_num); assert_eq!(row_num % OpenPackF::PACK_SIZE, 0); let eq_linear_combination = EqPolynomial::build_eq_x_r(&point[num_vars_in_msg..]); diff --git a/poly_commit/src/orion/simd_field_impl.rs b/poly_commit/src/orion/simd_field_impl.rs index 0f3b4cbc..4df14127 100644 --- a/poly_commit/src/orion/simd_field_impl.rs +++ b/poly_commit/src/orion/simd_field_impl.rs @@ -1,3 +1,4 @@ +use std::iter; use std::ops::Mul; use arith::{Field, SimdField}; @@ -6,7 +7,7 @@ use transcript::Transcript; use crate::{ orion::{ - utils::{commit_encoded, orion_mt_openings, transpose_in_place}, + utils::{commit_encoded, orion_mt_openings, orion_mt_verify, transpose_in_place}, OrionCommitment, OrionProof, OrionResult, OrionSRS, OrionScratchPad, }, traits::TensorCodeIOPPCS, @@ -128,6 +129,7 @@ where // NOTE: declare the look up tables for column sums let tables_num = row_num / OpenPackF::PACK_SIZE; let mut luts = SubsetSumLUTs::<EvalF>::new(OpenPackF::PACK_SIZE, tables_num); + assert_eq!(row_num % OpenPackF::PACK_SIZE, 0); // NOTE: working on evaluation response of tensor code IOP based PCS let mut eval_row = vec![SimdEvalF::ZERO; msg_size]; @@ -165,3 +167,99 @@ where query_openings, } } + +pub fn orion_verify_simd_field<F, SimdF, EvalF, SimdEvalF, ComPackF, OpenPackF, T>( + vk: &OrionSRS, + commitment: &OrionCommitment, + point: &[EvalF], + evaluation: EvalF, + transcript: &mut T, + proof: &OrionProof<SimdEvalF>, +) -> bool +where + F: Field, + SimdF: SimdField<Scalar = F>, + EvalF: Field + From<F> + Mul<F, Output = EvalF>, + SimdEvalF: SimdField<Scalar = EvalF>, + ComPackF: SimdField<Scalar = F>, + OpenPackF: SimdField<Scalar = F>, + T: Transcript<EvalF>, +{ + let (row_num, msg_size) = OrionSRS::evals_shape::<SimdF>(point.len()); + let num_vars_in_msg = msg_size.ilog2() as usize; + + // NOTE: working on evaluation response, evaluate the rest of the response + let eval_unpacked: Vec<_> = proof.eval_row.iter().flat_map(|e| e.unpack()).collect(); + let mut scratch = vec![EvalF::ZERO; msg_size * SimdEvalF::PACK_SIZE]; + let final_eval = MultiLinearPoly::evaluate_with_buffer( + &eval_unpacked, + &point[..num_vars_in_msg], + &mut scratch, + ); + if final_eval != evaluation { + return false; + } + + // NOTE: working on proximity responses, draw random linear combinations + // then draw query points from fiat shamir transcripts + let proximity_test_num = vk.proximity_repetitions::<EvalF>(PCS_SOUNDNESS_BITS); + let random_linear_combinations: Vec<Vec<EvalF>> = (0..proximity_test_num) + .map(|_| transcript.generate_challenge_field_elements(row_num)) + .collect(); + let query_num = vk.query_complexity(PCS_SOUNDNESS_BITS); + let query_indices = transcript.generate_challenge_index_vector(query_num); + + // NOTE: check consistency in MT in the opening trees and against the commitment tree + if !orion_mt_verify(vk, &query_indices, &proof.query_openings, commitment) { + return false; + } + + // NOTE: prepare the interleaved alphabets from the MT paths, + // but reshuffle the packed elements into another direction + let mut scratch = vec![F::ZERO; SimdF::PACK_SIZE * OpenPackF::PACK_SIZE]; + let shuffled_interleaved_alphabet: Vec<Vec<OpenPackF>> = proof + .query_openings + .iter() + .map(|c| -> Vec<_> { + let mut unpacked = c.unpack_field_elems::<F, ComPackF>(); + unpacked + .chunks_mut(SimdF::PACK_SIZE * OpenPackF::PACK_SIZE) + .flat_map(|circuit_simd_chunk| -> Vec<OpenPackF> { + transpose_in_place(circuit_simd_chunk, &mut scratch, SimdF::PACK_SIZE); + circuit_simd_chunk + .chunks(OpenPackF::PACK_SIZE) + .map(OpenPackF::pack) + .collect() + }) + .collect() + }) + .collect(); + + // NOTE: declare the look up tables for column sums + let tables_num = row_num / OpenPackF::PACK_SIZE; + let mut luts = SubsetSumLUTs::<EvalF>::new(OpenPackF::PACK_SIZE, tables_num); + assert_eq!(row_num % OpenPackF::PACK_SIZE, 0); + + let eq_linear_combination = EqPolynomial::build_eq_x_r(&point[num_vars_in_msg..]); + random_linear_combinations + .iter() + .zip(proof.proximity_rows.iter()) + .chain(iter::once((&eq_linear_combination, &proof.eval_row))) + .all(|(rl, msg)| { + let codeword = match vk.code_instance.encode(msg) { + Ok(c) => c, + _ => return false, + }; + + luts.build(rl); + + query_indices + .iter() + .zip(shuffled_interleaved_alphabet.iter()) + .all(|(&qi, interleaved_alphabet)| { + let index = qi % vk.codeword_len(); + let alphabet: SimdEvalF = luts.lookup_and_sum_simd(interleaved_alphabet); + alphabet == codeword[index] + }) + }) +} diff --git a/poly_commit/src/orion/utils.rs b/poly_commit/src/orion/utils.rs index 09a5819f..62abad97 100644 --- a/poly_commit/src/orion/utils.rs +++ b/poly_commit/src/orion/utils.rs @@ -171,6 +171,23 @@ where .collect() } +#[inline(always)] +pub(crate) fn orion_mt_verify( + vk: &OrionSRS, + query_indices: &[usize], + range_openings: &[tree::RangePath], + root: &OrionCommitment, +) -> bool { + let leaves_in_range_opening = OrionSRS::LEAVES_IN_RANGE_OPENING; + query_indices + .iter() + .zip(range_openings.iter()) + .all(|(&qi, range_path)| { + let index = qi % vk.codeword_len(); + range_path.verify(root) && index == range_path.left / leaves_in_range_opening + }) +} + /* * IMPLEMENTATIONS FOR MATRIX TRANSPOSE */ From ad2581d141e966606646e290154a1d74a1008a84 Mon Sep 17 00:00:00 2001 From: Hang Su <tonyfloater@gmail.com> Date: Sun, 24 Nov 2024 18:21:05 -0500 Subject: [PATCH 10/65] minor, typo --- poly_commit/src/orion/base_field_impl.rs | 2 +- poly_commit/src/orion/simd_field_impl.rs | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/poly_commit/src/orion/base_field_impl.rs b/poly_commit/src/orion/base_field_impl.rs index 2819a686..70fce1ba 100644 --- a/poly_commit/src/orion/base_field_impl.rs +++ b/poly_commit/src/orion/base_field_impl.rs @@ -197,7 +197,7 @@ where // check againts all challenged indices by check alphabets against // linear combined interleaved alphabet let tables_num = row_num / OpenPackF::PACK_SIZE; - let mut luts = SubsetSumLUTs::new(OpenPackF::PACK_SIZE, tables_num); + let mut luts = SubsetSumLUTs::<EvalF>::new(OpenPackF::PACK_SIZE, tables_num); assert_eq!(row_num % OpenPackF::PACK_SIZE, 0); let eq_linear_combination = EqPolynomial::build_eq_x_r(&point[num_vars_in_msg..]); diff --git a/poly_commit/src/orion/simd_field_impl.rs b/poly_commit/src/orion/simd_field_impl.rs index 4df14127..0b23267f 100644 --- a/poly_commit/src/orion/simd_field_impl.rs +++ b/poly_commit/src/orion/simd_field_impl.rs @@ -221,11 +221,10 @@ where .query_openings .iter() .map(|c| -> Vec<_> { - let mut unpacked = c.unpack_field_elems::<F, ComPackF>(); - unpacked + c.unpack_field_elems::<F, ComPackF>() .chunks_mut(SimdF::PACK_SIZE * OpenPackF::PACK_SIZE) .flat_map(|circuit_simd_chunk| -> Vec<OpenPackF> { - transpose_in_place(circuit_simd_chunk, &mut scratch, SimdF::PACK_SIZE); + transpose_in_place(circuit_simd_chunk, &mut scratch, OpenPackF::PACK_SIZE); circuit_simd_chunk .chunks(OpenPackF::PACK_SIZE) .map(OpenPackF::pack) From 23ce12a422aa2229cd3172e65b497cf513665b08 Mon Sep 17 00:00:00 2001 From: Hang Su <tonyfloater@gmail.com> Date: Mon, 25 Nov 2024 15:57:06 -0500 Subject: [PATCH 11/65] move test to base field test, working on simd test --- poly_commit/src/orion.rs | 9 ++++++--- poly_commit/src/orion/{tests.rs => base_field_tests.rs} | 0 poly_commit/src/orion/simd_field_tests.rs | 1 + 3 files changed, 7 insertions(+), 3 deletions(-) rename poly_commit/src/orion/{tests.rs => base_field_tests.rs} (100%) create mode 100644 poly_commit/src/orion/simd_field_tests.rs diff --git a/poly_commit/src/orion.rs b/poly_commit/src/orion.rs index f1901385..3d3dc626 100644 --- a/poly_commit/src/orion.rs +++ b/poly_commit/src/orion.rs @@ -12,12 +12,15 @@ pub use base_field_impl::{ orion_commit_base_field, orion_open_base_field, orion_verify_base_field, }; +#[cfg(test)] +mod base_field_tests; + mod simd_field_impl; pub use simd_field_impl::{ orion_commit_simd_field, orion_open_simd_field, orion_verify_simd_field, }; -mod serde; - #[cfg(test)] -mod tests; +mod simd_field_tests; + +mod serde; diff --git a/poly_commit/src/orion/tests.rs b/poly_commit/src/orion/base_field_tests.rs similarity index 100% rename from poly_commit/src/orion/tests.rs rename to poly_commit/src/orion/base_field_tests.rs diff --git a/poly_commit/src/orion/simd_field_tests.rs b/poly_commit/src/orion/simd_field_tests.rs new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/poly_commit/src/orion/simd_field_tests.rs @@ -0,0 +1 @@ + From a065d8b6a49029648f95d8000bdd0dc6765f1511 Mon Sep 17 00:00:00 2001 From: Hang Su <tonyfloater@gmail.com> Date: Mon, 25 Nov 2024 18:03:29 -0500 Subject: [PATCH 12/65] orion simd commitment consistency test --- poly_commit/src/orion/base_field_tests.rs | 2 +- poly_commit/src/orion/simd_field_impl.rs | 2 +- poly_commit/src/orion/simd_field_tests.rs | 73 +++++++++++++++++++++++ 3 files changed, 75 insertions(+), 2 deletions(-) diff --git a/poly_commit/src/orion/base_field_tests.rs b/poly_commit/src/orion/base_field_tests.rs index 0fe139f9..eefe65d7 100644 --- a/poly_commit/src/orion/base_field_tests.rs +++ b/poly_commit/src/orion/base_field_tests.rs @@ -106,7 +106,7 @@ where if !interleaved_codewords.len().is_power_of_two() { let aligned_po2_len = interleaved_codewords.len().next_power_of_two(); - interleaved_codewords.resize(aligned_po2_len, F::default()); + interleaved_codewords.resize(aligned_po2_len, F::ZERO); } let interleaved_alphabet_tree = diff --git a/poly_commit/src/orion/simd_field_impl.rs b/poly_commit/src/orion/simd_field_impl.rs index 0b23267f..fd79ece1 100644 --- a/poly_commit/src/orion/simd_field_impl.rs +++ b/poly_commit/src/orion/simd_field_impl.rs @@ -30,7 +30,7 @@ where let relative_pack_size = PackF::PACK_SIZE / SimdF::PACK_SIZE; evaluations .chunks(relative_pack_size) - .map(SimdField::pack_from_simd) + .map(PackF::pack_from_simd) .collect() } diff --git a/poly_commit/src/orion/simd_field_tests.rs b/poly_commit/src/orion/simd_field_tests.rs index 8b137891..e053a019 100644 --- a/poly_commit/src/orion/simd_field_tests.rs +++ b/poly_commit/src/orion/simd_field_tests.rs @@ -1 +1,74 @@ +use arith::{Field, SimdField}; +use ark_std::test_rng; +use gf2::{GF2x128, GF2x64, GF2x8, GF2}; +use polynomials::MultiLinearPoly; +use crate::{ + orion::{simd_field_impl::*, utils::*}, + traits::TensorCodeIOPPCS, + ORION_CODE_PARAMETER_INSTANCE, +}; + +fn dumb_commit_simd_field<F, SimdF, ComPackF>( + orion_srs: &OrionSRS, + poly: &MultiLinearPoly<SimdF>, +) -> OrionCommitment +where + F: Field, + SimdF: SimdField<Scalar = F>, + ComPackF: SimdField<Scalar = F>, +{ + let (row_num, msg_size) = OrionSRS::evals_shape::<SimdF>(poly.get_num_vars()); + + let mut interleaved_codewords: Vec<_> = poly + .coeffs + .chunks(msg_size) + .flat_map(|msg| orion_srs.code_instance.encode(&msg).unwrap()) + .collect(); + + let mut scratch = vec![SimdF::ZERO; row_num * orion_srs.codeword_len()]; + transpose_in_place(&mut interleaved_codewords, &mut scratch, row_num); + drop(scratch); + + let mut packed_interleaved_codeword: Vec<_> = interleaved_codewords + .chunks(ComPackF::PACK_SIZE / SimdF::PACK_SIZE) + .map(ComPackF::pack_from_simd) + .collect(); + drop(interleaved_codewords); + + if !packed_interleaved_codeword.len().is_power_of_two() { + let aligned_po2_len = packed_interleaved_codeword.len().next_power_of_two(); + packed_interleaved_codeword.resize(aligned_po2_len, ComPackF::ZERO); + } + + let interleaved_alphabet_tree = + tree::Tree::compact_new_with_packed_field_elems::<F, ComPackF>(packed_interleaved_codeword); + + interleaved_alphabet_tree.root() +} + +fn test_orion_commit_simd_field_consistency_generic<F, SimdF, ComPackF>(num_vars: usize) +where + F: Field, + SimdF: SimdField<Scalar = F>, + ComPackF: SimdField<Scalar = F>, +{ + let mut rng = test_rng(); + + let random_poly = MultiLinearPoly::<SimdF>::random(num_vars, &mut rng); + let srs = OrionSRS::from_random::<SimdF>(num_vars, ORION_CODE_PARAMETER_INSTANCE, &mut rng); + let mut scratch_pad = OrionScratchPad::<F, ComPackF>::default(); + + let real_commitment = orion_commit_simd_field(&srs, &random_poly, &mut scratch_pad).unwrap(); + let dumb_commitment = dumb_commit_simd_field::<F, SimdF, ComPackF>(&srs, &random_poly); + + assert_eq!(real_commitment, dumb_commitment); +} + +#[test] +fn test_orion_commit_simd_field_consistency() { + (16..=22).for_each(|num_vars| { + test_orion_commit_simd_field_consistency_generic::<GF2, GF2x8, GF2x64>(num_vars); + test_orion_commit_simd_field_consistency_generic::<GF2, GF2x8, GF2x128>(num_vars); + }); +} From e664a4b4f70257f7993eefa6b2c7679dcceff395 Mon Sep 17 00:00:00 2001 From: Hang Su <tonyfloater@gmail.com> Date: Mon, 25 Nov 2024 18:36:29 -0500 Subject: [PATCH 13/65] started working on test orion pcs simd full e2e --- poly_commit/src/orion.rs | 3 + poly_commit/src/orion/base_field_tests.rs | 75 +-------------------- poly_commit/src/orion/linear_code_tests.rs | 78 ++++++++++++++++++++++ poly_commit/src/orion/simd_field_tests.rs | 33 +++++++++ 4 files changed, 116 insertions(+), 73 deletions(-) create mode 100644 poly_commit/src/orion/linear_code_tests.rs diff --git a/poly_commit/src/orion.rs b/poly_commit/src/orion.rs index 3d3dc626..e8101327 100644 --- a/poly_commit/src/orion.rs +++ b/poly_commit/src/orion.rs @@ -7,6 +7,9 @@ pub use utils::{ mod linear_code; pub use linear_code::{OrionCodeParameter, ORION_CODE_PARAMETER_INSTANCE}; +#[cfg(test)] +mod linear_code_tests; + mod base_field_impl; pub use base_field_impl::{ orion_commit_base_field, orion_open_base_field, orion_verify_base_field, diff --git a/poly_commit/src/orion/base_field_tests.rs b/poly_commit/src/orion/base_field_tests.rs index eefe65d7..e85fee58 100644 --- a/poly_commit/src/orion/base_field_tests.rs +++ b/poly_commit/src/orion/base_field_tests.rs @@ -8,82 +8,11 @@ use polynomials::MultiLinearPoly; use transcript::{BytesHashTranscript, Keccak256hasher, Transcript}; use crate::{ - orion::{ - base_field_impl::*, - linear_code::{OrionCode, ORION_CODE_PARAMETER_INSTANCE}, - utils::*, - }, + orion::{base_field_impl::*, utils::*}, traits::TensorCodeIOPPCS, + ORION_CODE_PARAMETER_INSTANCE, }; -fn column_combination<F, PackF>(mat: &[F], combination: &[F]) -> Vec<F> -where - F: Field, - PackF: SimdField<Scalar = F>, -{ - assert_eq!(combination.len() % PackF::PACK_SIZE, 0); - - let mut luts = SubsetSumLUTs::new(PackF::PACK_SIZE, combination.len() / PackF::PACK_SIZE); - luts.build(combination); - - mat.chunks(combination.len()) - .map(|p_col| { - let packed: Vec<_> = p_col.chunks(PackF::PACK_SIZE).map(PackF::pack).collect(); - luts.lookup_and_sum(&packed) - }) - .collect() -} - -fn test_orion_code_generic<F, PackF>(msg_len: usize) -where - F: Field, - PackF: SimdField<Scalar = F>, -{ - let mut rng = test_rng(); - - let orion_code = OrionCode::new(ORION_CODE_PARAMETER_INSTANCE, msg_len, &mut rng); - - let row_num = 1024 / F::FIELD_SIZE; - let weights: Vec<_> = (0..row_num).map(|_| F::random_unsafe(&mut rng)).collect(); - - // NOTE: generate message and codeword in the slice buffer - let mut message_mat = vec![F::ZERO; row_num * orion_code.msg_len()]; - let mut codeword_mat = vec![F::ZERO; row_num * orion_code.code_len()]; - - message_mat - .chunks_mut(orion_code.msg_len()) - .zip(codeword_mat.chunks_mut(orion_code.code_len())) - .for_each(|(msg, codeword)| { - msg.fill_with(|| F::random_unsafe(&mut rng)); - orion_code.encode_in_place(msg, codeword).unwrap() - }); - - // NOTE: transpose message and codeword matrix - let mut message_scratch = vec![F::ZERO; row_num * orion_code.msg_len()]; - transpose_in_place(&mut message_mat, &mut message_scratch, row_num); - drop(message_scratch); - - let mut codeword_scratch = vec![F::ZERO; row_num * orion_code.code_len()]; - transpose_in_place(&mut codeword_mat, &mut codeword_scratch, row_num); - drop(codeword_scratch); - - // NOTE: message and codeword matrix linear combination with weights - let msg_linear_combined = column_combination::<F, PackF>(&message_mat, &weights); - let codeword_linear_combined = column_combination::<F, PackF>(&codeword_mat, &weights); - - let codeword_computed = orion_code.encode(&msg_linear_combined).unwrap(); - - assert_eq!(codeword_linear_combined, codeword_computed); -} - -#[test] -fn test_orion_code() { - (5..=15).for_each(|num_vars| { - let msg_len = 1usize << num_vars; - test_orion_code_generic::<GF2, GF2x8>(msg_len); - }); -} - fn dumb_commit_base_field<F, ComPackF>( orion_srs: &OrionSRS, poly: &MultiLinearPoly<F>, diff --git a/poly_commit/src/orion/linear_code_tests.rs b/poly_commit/src/orion/linear_code_tests.rs new file mode 100644 index 00000000..4aaf9590 --- /dev/null +++ b/poly_commit/src/orion/linear_code_tests.rs @@ -0,0 +1,78 @@ +use arith::{Field, SimdField}; +use ark_std::test_rng; +use gf2::{GF2x8, GF2}; + +use crate::{ + orion::{linear_code::OrionCode, utils::transpose_in_place}, + traits::TensorCodeIOPPCS, + OrionSRS, SubsetSumLUTs, ORION_CODE_PARAMETER_INSTANCE, +}; + +fn column_combination<F, PackF>(mat: &[F], combination: &[F]) -> Vec<F> +where + F: Field, + PackF: SimdField<Scalar = F>, +{ + assert_eq!(combination.len() % PackF::PACK_SIZE, 0); + + let mut luts = SubsetSumLUTs::new(PackF::PACK_SIZE, combination.len() / PackF::PACK_SIZE); + luts.build(combination); + + mat.chunks(combination.len()) + .map(|p_col| { + let packed: Vec<_> = p_col.chunks(PackF::PACK_SIZE).map(PackF::pack).collect(); + luts.lookup_and_sum(&packed) + }) + .collect() +} + +fn test_orion_code_generic<F, PackF>(msg_len: usize) +where + F: Field, + PackF: SimdField<Scalar = F>, +{ + let mut rng = test_rng(); + + let orion_code = OrionCode::new(ORION_CODE_PARAMETER_INSTANCE, msg_len, &mut rng); + + let row_bits = OrionSRS::LEAVES_IN_RANGE_OPENING * tree::LEAF_BYTES * 8; + let row_num = row_bits / F::FIELD_SIZE; + let weights: Vec<_> = (0..row_num).map(|_| F::random_unsafe(&mut rng)).collect(); + + // NOTE: generate message and codeword in the slice buffer + let mut message_mat = vec![F::ZERO; row_num * orion_code.msg_len()]; + let mut codeword_mat = vec![F::ZERO; row_num * orion_code.code_len()]; + + message_mat + .chunks_mut(orion_code.msg_len()) + .zip(codeword_mat.chunks_mut(orion_code.code_len())) + .for_each(|(msg, codeword)| { + msg.fill_with(|| F::random_unsafe(&mut rng)); + orion_code.encode_in_place(msg, codeword).unwrap() + }); + + // NOTE: transpose message and codeword matrix + let mut message_scratch = vec![F::ZERO; row_num * orion_code.msg_len()]; + transpose_in_place(&mut message_mat, &mut message_scratch, row_num); + drop(message_scratch); + + let mut codeword_scratch = vec![F::ZERO; row_num * orion_code.code_len()]; + transpose_in_place(&mut codeword_mat, &mut codeword_scratch, row_num); + drop(codeword_scratch); + + // NOTE: message and codeword matrix linear combination with weights + let msg_linear_combined = column_combination::<F, PackF>(&message_mat, &weights); + let codeword_linear_combined = column_combination::<F, PackF>(&codeword_mat, &weights); + + let codeword_computed = orion_code.encode(&msg_linear_combined).unwrap(); + + assert_eq!(codeword_linear_combined, codeword_computed); +} + +#[test] +fn test_orion_code() { + (5..=15).for_each(|num_vars| { + let msg_len = 1usize << num_vars; + test_orion_code_generic::<GF2, GF2x8>(msg_len); + }); +} diff --git a/poly_commit/src/orion/simd_field_tests.rs b/poly_commit/src/orion/simd_field_tests.rs index e053a019..f249a28f 100644 --- a/poly_commit/src/orion/simd_field_tests.rs +++ b/poly_commit/src/orion/simd_field_tests.rs @@ -1,7 +1,11 @@ +use std::ops::Mul; + use arith::{Field, SimdField}; use ark_std::test_rng; use gf2::{GF2x128, GF2x64, GF2x8, GF2}; +use gf2_128::{GF2_128x8, GF2_128}; use polynomials::MultiLinearPoly; +use transcript::{BytesHashTranscript, Keccak256hasher, Transcript}; use crate::{ orion::{simd_field_impl::*, utils::*}, @@ -68,7 +72,36 @@ where #[test] fn test_orion_commit_simd_field_consistency() { (16..=22).for_each(|num_vars| { + test_orion_commit_simd_field_consistency_generic::<GF2, GF2x8, GF2x8>(num_vars); test_orion_commit_simd_field_consistency_generic::<GF2, GF2x8, GF2x64>(num_vars); test_orion_commit_simd_field_consistency_generic::<GF2, GF2x8, GF2x128>(num_vars); }); } + +fn test_orion_pcs_simd_full_e2e_generics<F, SimdF, EvalF, SimdEvalF, ComPackF, OpenPackF, T>( + _num_vars: usize, +) where + F: Field, + SimdF: SimdField<Scalar = F>, + EvalF: Field + From<F> + Mul<F, Output = EvalF>, + SimdEvalF: SimdField<Scalar = EvalF>, + ComPackF: SimdField<Scalar = F>, + OpenPackF: SimdField<Scalar = F>, + T: Transcript<EvalF>, +{ +} + +#[test] +fn test_orion_pcs_simd_full_e2e() { + (16..=22).for_each(|num_vars| { + test_orion_pcs_simd_full_e2e_generics::< + GF2, + GF2x8, + GF2_128, + GF2_128x8, + GF2x128, + GF2x8, + BytesHashTranscript<_, Keccak256hasher>, + >(num_vars); + }) +} From 955d2b4af9fdfd85ea892ce9e76922fc82bb72d6 Mon Sep 17 00:00:00 2001 From: Hang Su <tonyfloater@gmail.com> Date: Mon, 25 Nov 2024 23:40:46 -0500 Subject: [PATCH 14/65] e2e test finalized, prototype first round of polishing starts --- poly_commit/src/orion/simd_field_impl.rs | 37 +++++++--- poly_commit/src/orion/simd_field_tests.rs | 83 +++++++++++++++++++---- 2 files changed, 97 insertions(+), 23 deletions(-) diff --git a/poly_commit/src/orion/simd_field_impl.rs b/poly_commit/src/orion/simd_field_impl.rs index fd79ece1..c4dc995b 100644 --- a/poly_commit/src/orion/simd_field_impl.rs +++ b/poly_commit/src/orion/simd_field_impl.rs @@ -44,7 +44,13 @@ where SimdF: SimdField<Scalar = F>, ComPackF: SimdField<Scalar = F>, { - let (row_num, msg_size) = OrionSRS::evals_shape::<SimdF>(poly.get_num_vars()); + let (row_num, msg_size) = { + let num_vars = poly.get_num_vars() + SimdF::PACK_SIZE.ilog2() as usize; + let (row_field_elems, msg_size) = OrionSRS::evals_shape::<F>(num_vars); + let row_num = row_field_elems / SimdF::PACK_SIZE; + (row_num, msg_size) + }; + let relative_pack_size = ComPackF::PACK_SIZE / SimdF::PACK_SIZE; assert_eq!(ComPackF::PACK_SIZE % SimdF::PACK_SIZE, 0); @@ -102,7 +108,7 @@ pub fn orion_open_simd_field<F, SimdF, EvalF, SimdEvalF, ComPackF, OpenPackF, T> poly: &MultiLinearPoly<SimdF>, point: &[EvalF], transcript: &mut T, - scratch_pad: &mut OrionScratchPad<F, ComPackF>, + scratch_pad: &OrionScratchPad<F, ComPackF>, ) -> OrionProof<SimdEvalF> where F: Field, @@ -115,7 +121,15 @@ where { assert_eq!(SimdF::PACK_SIZE, SimdEvalF::PACK_SIZE); - let (row_num, msg_size) = OrionSRS::evals_shape::<SimdF>(poly.get_num_vars()); + let (row_num, msg_size) = { + let num_vars = poly.get_num_vars() + SimdF::PACK_SIZE.ilog2() as usize; + assert_eq!(num_vars, point.len()); + + let (row_field_elems, msg_size) = OrionSRS::evals_shape::<F>(num_vars); + let row_num = row_field_elems / SimdF::PACK_SIZE; + (row_num, msg_size) + }; + let num_vars_in_row = row_num.ilog2() as usize; // NOTE: transpose and shuffle evaluations (repack evaluations in another direction) @@ -134,8 +148,8 @@ where // NOTE: working on evaluation response of tensor code IOP based PCS let mut eval_row = vec![SimdEvalF::ZERO; msg_size]; - let eq_col_coeffs = EqPolynomial::build_eq_x_r(&point[point.len() - num_vars_in_row..]); - luts.build(&eq_col_coeffs); + let eq_coeffs = EqPolynomial::build_eq_x_r(&point[point.len() - num_vars_in_row..]); + luts.build(&eq_coeffs); packed_shuffled_evals .chunks(tables_num * SimdEvalF::PACK_SIZE) @@ -185,15 +199,20 @@ where OpenPackF: SimdField<Scalar = F>, T: Transcript<EvalF>, { - let (row_num, msg_size) = OrionSRS::evals_shape::<SimdF>(point.len()); - let num_vars_in_msg = msg_size.ilog2() as usize; + let (row_num, msg_size) = { + let (row_field_elems, msg_size) = OrionSRS::evals_shape::<F>(point.len()); + let row_num = row_field_elems / SimdF::PACK_SIZE; + (row_num, msg_size) + }; + + let num_vars_in_row = row_num.ilog2() as usize; // NOTE: working on evaluation response, evaluate the rest of the response let eval_unpacked: Vec<_> = proof.eval_row.iter().flat_map(|e| e.unpack()).collect(); let mut scratch = vec![EvalF::ZERO; msg_size * SimdEvalF::PACK_SIZE]; let final_eval = MultiLinearPoly::evaluate_with_buffer( &eval_unpacked, - &point[..num_vars_in_msg], + &point[..point.len() - num_vars_in_row], &mut scratch, ); if final_eval != evaluation { @@ -239,7 +258,7 @@ where let mut luts = SubsetSumLUTs::<EvalF>::new(OpenPackF::PACK_SIZE, tables_num); assert_eq!(row_num % OpenPackF::PACK_SIZE, 0); - let eq_linear_combination = EqPolynomial::build_eq_x_r(&point[num_vars_in_msg..]); + let eq_linear_combination = EqPolynomial::build_eq_x_r(&point[point.len() - num_vars_in_row..]); random_linear_combinations .iter() .zip(proof.proximity_rows.iter()) diff --git a/poly_commit/src/orion/simd_field_tests.rs b/poly_commit/src/orion/simd_field_tests.rs index f249a28f..11d6b9e6 100644 --- a/poly_commit/src/orion/simd_field_tests.rs +++ b/poly_commit/src/orion/simd_field_tests.rs @@ -22,7 +22,12 @@ where SimdF: SimdField<Scalar = F>, ComPackF: SimdField<Scalar = F>, { - let (row_num, msg_size) = OrionSRS::evals_shape::<SimdF>(poly.get_num_vars()); + let (row_num, msg_size) = { + let num_vars = poly.get_num_vars() + SimdF::PACK_SIZE.ilog2() as usize; + let (row_field_elems, msg_size) = OrionSRS::evals_shape::<F>(num_vars); + let row_num = row_field_elems / SimdF::PACK_SIZE; + (row_num, msg_size) + }; let mut interleaved_codewords: Vec<_> = poly .coeffs @@ -60,7 +65,9 @@ where let mut rng = test_rng(); let random_poly = MultiLinearPoly::<SimdF>::random(num_vars, &mut rng); - let srs = OrionSRS::from_random::<SimdF>(num_vars, ORION_CODE_PARAMETER_INSTANCE, &mut rng); + let real_num_vars = num_vars + SimdF::PACK_SIZE.ilog2() as usize; + let srs = + OrionSRS::from_random::<SimdF>(real_num_vars, ORION_CODE_PARAMETER_INSTANCE, &mut rng); let mut scratch_pad = OrionScratchPad::<F, ComPackF>::default(); let real_commitment = orion_commit_simd_field(&srs, &random_poly, &mut scratch_pad).unwrap(); @@ -78,8 +85,8 @@ fn test_orion_commit_simd_field_consistency() { }); } -fn test_orion_pcs_simd_full_e2e_generics<F, SimdF, EvalF, SimdEvalF, ComPackF, OpenPackF, T>( - _num_vars: usize, +fn test_orion_pcs_simd_full_e2e_generics<F, SimdF, EvalF, SimdEvalF, ComPackF, OpenPackF>( + num_vars: usize, ) where F: Field, SimdF: SimdField<Scalar = F>, @@ -87,21 +94,69 @@ fn test_orion_pcs_simd_full_e2e_generics<F, SimdF, EvalF, SimdEvalF, ComPackF, O SimdEvalF: SimdField<Scalar = EvalF>, ComPackF: SimdField<Scalar = F>, OpenPackF: SimdField<Scalar = F>, - T: Transcript<EvalF>, { + let mut rng = test_rng(); + + let random_poly = MultiLinearPoly::<SimdF>::random(num_vars, &mut rng); + let random_poly_unpacked = MultiLinearPoly::<EvalF>::new( + random_poly + .coeffs + .iter() + .flat_map(|p| -> Vec<_> { p.unpack().iter().map(|t| EvalF::from(*t)).collect() }) + .collect(), + ); + let real_num_vars = num_vars + SimdF::PACK_SIZE.ilog2() as usize; + let random_point: Vec<_> = (0..real_num_vars) + .map(|_| EvalF::random_unsafe(&mut rng)) + .collect(); + + let mut scratch = vec![EvalF::ZERO; random_poly_unpacked.coeffs.len()]; + let expected_eval = MultiLinearPoly::evaluate_with_buffer( + &random_poly_unpacked.coeffs, + &random_point, + &mut scratch, + ); + drop(scratch); + + let srs = + OrionSRS::from_random::<SimdF>(real_num_vars, ORION_CODE_PARAMETER_INSTANCE, &mut rng); + let mut scratch_pad = OrionScratchPad::<F, ComPackF>::default(); + let mut transcript: BytesHashTranscript<EvalF, Keccak256hasher> = BytesHashTranscript::new(); + let mut transcript_cloned = transcript.clone(); + + let commitment = orion_commit_simd_field(&srs, &random_poly, &mut scratch_pad).unwrap(); + + let opening = orion_open_simd_field::<F, SimdF, EvalF, SimdEvalF, ComPackF, OpenPackF, _>( + &srs, + &random_poly, + &random_point, + &mut transcript, + &scratch_pad, + ); + + assert!(orion_verify_simd_field::< + F, + SimdF, + EvalF, + SimdEvalF, + ComPackF, + OpenPackF, + _, + >( + &srs, + &commitment, + &random_point, + expected_eval, + &mut transcript_cloned, + &opening + )); } #[test] fn test_orion_pcs_simd_full_e2e() { (16..=22).for_each(|num_vars| { - test_orion_pcs_simd_full_e2e_generics::< - GF2, - GF2x8, - GF2_128, - GF2_128x8, - GF2x128, - GF2x8, - BytesHashTranscript<_, Keccak256hasher>, - >(num_vars); + test_orion_pcs_simd_full_e2e_generics::<GF2, GF2x8, GF2_128, GF2_128x8, GF2x128, GF2x8>( + num_vars, + ); }) } From 164ff4cb71d71cfc9c226a42d0e9af4ebb4f1354 Mon Sep 17 00:00:00 2001 From: Hang Su <tonyfloater@gmail.com> Date: Tue, 26 Nov 2024 02:13:54 -0500 Subject: [PATCH 15/65] minor polishing and benchmarks for orion over simd polynomial --- poly_commit/benches/orion.rs | 169 +++++++++++++++++++--- poly_commit/src/orion/simd_field_impl.rs | 8 +- poly_commit/src/orion/simd_field_tests.rs | 3 + 3 files changed, 156 insertions(+), 24 deletions(-) diff --git a/poly_commit/benches/orion.rs b/poly_commit/benches/orion.rs index 0d28f545..eb8f0c37 100644 --- a/poly_commit/benches/orion.rs +++ b/poly_commit/benches/orion.rs @@ -4,25 +4,22 @@ use arith::{Field, SimdField}; use ark_std::test_rng; use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion}; use gf2::{GF2x128, GF2x8, GF2}; -use gf2_128::GF2_128; +use gf2_128::{GF2_128x8, GF2_128}; use poly_commit::*; use polynomials::MultiLinearPoly; use transcript::{BytesHashTranscript, Keccak256hasher, Transcript}; use tynm::type_name; -fn committing_benchmark_helper<F, EvalF, ComPackF, OpenPackF, T>( +fn base_field_committing_benchmark_helper<F, ComPackF>( c: &mut Criterion, lowest_num_vars: usize, highest_num_vars: usize, ) where F: Field, - EvalF: Field + From<F> + Mul<F, Output = EvalF>, ComPackF: SimdField<Scalar = F>, - OpenPackF: SimdField<Scalar = F>, - T: Transcript<EvalF>, { let mut group = c.benchmark_group(format!( - "Orion PCS committing benchmarking: F = {}, ComPackF = {}", + "Orion PCS base field committing: F = {}, ComPackF = {}", type_name::<F>(), type_name::<ComPackF>(), )); @@ -50,17 +47,60 @@ fn committing_benchmark_helper<F, EvalF, ComPackF, OpenPackF, T>( } } -fn orion_committing_benchmark(c: &mut Criterion) { - committing_benchmark_helper::< - GF2, - GF2_128, - GF2x128, - GF2x8, - BytesHashTranscript<_, Keccak256hasher>, - >(c, 19, 30); +fn orion_base_field_committing_benchmark(c: &mut Criterion) { + base_field_committing_benchmark_helper::<GF2, GF2x128>(c, 19, 30); } -fn opening_benchmark_helper<F, EvalF, ComPackF, OpenPackF, T>( +fn simd_field_committing_benchmark_helper<F, SimdF, ComPackF>( + c: &mut Criterion, + lowest_num_vars: usize, + highest_num_vars: usize, +) where + F: Field, + SimdF: SimdField<Scalar = F>, + ComPackF: SimdField<Scalar = F>, +{ + let mut group = c.benchmark_group(format!( + "Orion PCS SIMD field committing: F = {}, SIMD-F = {}, ComPackF = {}", + type_name::<F>(), + type_name::<SimdF>(), + type_name::<ComPackF>(), + )); + + let mut rng = test_rng(); + let mut scratch_pad = OrionScratchPad::<F, ComPackF>::default(); + + for num_vars in lowest_num_vars..=highest_num_vars { + let packed_num_vars = num_vars - SimdF::PACK_SIZE.ilog2() as usize; + let poly = MultiLinearPoly::<SimdF>::random(packed_num_vars, &mut rng); + + let srs = OrionSRS::from_random::<F>(num_vars, ORION_CODE_PARAMETER_INSTANCE, &mut rng); + + group + .bench_function( + BenchmarkId::new( + format!( + "{num_vars} variables with {packed_num_vars} being packed poly num vars" + ), + num_vars, + ), + |b| { + b.iter(|| { + _ = black_box( + orion_commit_simd_field(&srs, &poly, &mut scratch_pad).unwrap(), + ) + }) + }, + ) + .sample_size(10); + } +} + +fn orion_simd_field_committing_benchmark(c: &mut Criterion) { + simd_field_committing_benchmark_helper::<GF2, GF2x8, GF2x128>(c, 19, 30); +} + +fn base_field_opening_benchmark_helper<F, EvalF, ComPackF, OpenPackF, T>( c: &mut Criterion, lowest_num_vars: usize, highest_num_vars: usize, @@ -72,10 +112,11 @@ fn opening_benchmark_helper<F, EvalF, ComPackF, OpenPackF, T>( T: Transcript<EvalF>, { let mut group = c.benchmark_group(format!( - "Orion PCS opening benchmarking: F = {}, EvalF = {}, ComPackF = {}", + "Orion PCS base field opening: F = {}, EvalF = {}, ComPackF = {}, OpenPackF = {}", type_name::<F>(), type_name::<EvalF>(), type_name::<ComPackF>(), + type_name::<OpenPackF>(), )); let mut rng = test_rng(); @@ -111,11 +152,97 @@ fn opening_benchmark_helper<F, EvalF, ComPackF, OpenPackF, T>( } } -fn orion_opening_benchmark(c: &mut Criterion) { - opening_benchmark_helper::<GF2, GF2_128, GF2x128, GF2x8, BytesHashTranscript<_, Keccak256hasher>>( - c, 19, 30, - ); +fn orion_base_field_opening_benchmark(c: &mut Criterion) { + base_field_opening_benchmark_helper::< + GF2, + GF2_128, + GF2x128, + GF2x8, + BytesHashTranscript<_, Keccak256hasher>, + >(c, 19, 30); +} + +fn simd_field_opening_benchmark_helper<F, SimdF, EvalF, SimdEvalF, ComPackF, OpenPackF, T>( + c: &mut Criterion, + lowest_num_vars: usize, + highest_num_vars: usize, +) where + F: Field, + SimdF: SimdField<Scalar = F>, + EvalF: Field + From<F> + Mul<F, Output = EvalF>, + SimdEvalF: SimdField<Scalar = EvalF>, + ComPackF: SimdField<Scalar = F>, + OpenPackF: SimdField<Scalar = F>, + T: Transcript<EvalF>, +{ + let mut group = c.benchmark_group(format!( + "Orion PCS SIMD field opening: SIMD-F = {}, EvalF = {}, ComPackF = {}, OpenPackF = {}", + type_name::<SimdF>(), + type_name::<EvalF>(), + type_name::<ComPackF>(), + type_name::<OpenPackF>() + )); + + let mut rng = test_rng(); + let mut transcript = T::new(); + let mut scratch_pad = OrionScratchPad::<F, ComPackF>::default(); + + for num_vars in lowest_num_vars..=highest_num_vars { + let packed_num_vars = num_vars - SimdF::PACK_SIZE.ilog2() as usize; + let poly = MultiLinearPoly::<SimdF>::random(packed_num_vars, &mut rng); + let eval_point: Vec<_> = (0..num_vars) + .map(|_| EvalF::random_unsafe(&mut rng)) + .collect(); + + let srs = OrionSRS::from_random::<F>(num_vars, ORION_CODE_PARAMETER_INSTANCE, &mut rng); + + let _commitment = orion_commit_simd_field(&srs, &poly, &mut scratch_pad).unwrap(); + + group + .bench_function( + BenchmarkId::new( + format!( + "{num_vars} variables with {packed_num_vars} being packed poly num vars" + ), + num_vars, + ), + |b| { + b.iter(|| { + _ = black_box(orion_open_simd_field::< + F, + SimdF, + EvalF, + SimdEvalF, + ComPackF, + OpenPackF, + T, + >( + &srs, &poly, &eval_point, &mut transcript, &scratch_pad + )) + }) + }, + ) + .sample_size(10); + } +} + +fn orion_simd_field_opening_benchmark(c: &mut Criterion) { + simd_field_opening_benchmark_helper::< + GF2, + GF2x8, + GF2_128, + GF2_128x8, + GF2x128, + GF2x8, + BytesHashTranscript<_, Keccak256hasher>, + >(c, 19, 30); } -criterion_group!(bench, orion_committing_benchmark, orion_opening_benchmark); +criterion_group!( + bench, + orion_base_field_committing_benchmark, + orion_base_field_opening_benchmark, + orion_simd_field_committing_benchmark, + orion_simd_field_opening_benchmark, +); criterion_main!(bench); diff --git a/poly_commit/src/orion/simd_field_impl.rs b/poly_commit/src/orion/simd_field_impl.rs index c4dc995b..23a5dda0 100644 --- a/poly_commit/src/orion/simd_field_impl.rs +++ b/poly_commit/src/orion/simd_field_impl.rs @@ -131,6 +131,7 @@ where }; let num_vars_in_row = row_num.ilog2() as usize; + let num_vars_in_unpacked_msg = point.len() - num_vars_in_row; // NOTE: transpose and shuffle evaluations (repack evaluations in another direction) // for linear combinations in evaulation/proximity tests @@ -148,7 +149,7 @@ where // NOTE: working on evaluation response of tensor code IOP based PCS let mut eval_row = vec![SimdEvalF::ZERO; msg_size]; - let eq_coeffs = EqPolynomial::build_eq_x_r(&point[point.len() - num_vars_in_row..]); + let eq_coeffs = EqPolynomial::build_eq_x_r(&point[num_vars_in_unpacked_msg..]); luts.build(&eq_coeffs); packed_shuffled_evals @@ -206,13 +207,14 @@ where }; let num_vars_in_row = row_num.ilog2() as usize; + let num_vars_in_unpacked_msg = point.len() - num_vars_in_row; // NOTE: working on evaluation response, evaluate the rest of the response let eval_unpacked: Vec<_> = proof.eval_row.iter().flat_map(|e| e.unpack()).collect(); let mut scratch = vec![EvalF::ZERO; msg_size * SimdEvalF::PACK_SIZE]; let final_eval = MultiLinearPoly::evaluate_with_buffer( &eval_unpacked, - &point[..point.len() - num_vars_in_row], + &point[..num_vars_in_unpacked_msg], &mut scratch, ); if final_eval != evaluation { @@ -258,7 +260,7 @@ where let mut luts = SubsetSumLUTs::<EvalF>::new(OpenPackF::PACK_SIZE, tables_num); assert_eq!(row_num % OpenPackF::PACK_SIZE, 0); - let eq_linear_combination = EqPolynomial::build_eq_x_r(&point[point.len() - num_vars_in_row..]); + let eq_linear_combination = EqPolynomial::build_eq_x_r(&point[num_vars_in_unpacked_msg..]); random_linear_combinations .iter() .zip(proof.proximity_rows.iter()) diff --git a/poly_commit/src/orion/simd_field_tests.rs b/poly_commit/src/orion/simd_field_tests.rs index 11d6b9e6..c36ac6c7 100644 --- a/poly_commit/src/orion/simd_field_tests.rs +++ b/poly_commit/src/orion/simd_field_tests.rs @@ -155,6 +155,9 @@ fn test_orion_pcs_simd_full_e2e_generics<F, SimdF, EvalF, SimdEvalF, ComPackF, O #[test] fn test_orion_pcs_simd_full_e2e() { (16..=22).for_each(|num_vars| { + test_orion_pcs_simd_full_e2e_generics::<GF2, GF2x8, GF2_128, GF2_128x8, GF2x64, GF2x8>( + num_vars, + ); test_orion_pcs_simd_full_e2e_generics::<GF2, GF2x8, GF2_128, GF2_128x8, GF2x128, GF2x8>( num_vars, ); From ba2ce01fe443fc37d0529245db1df19c5392f4de Mon Sep 17 00:00:00 2001 From: Hang Su <tonyfloater@gmail.com> Date: Tue, 26 Nov 2024 03:06:59 -0500 Subject: [PATCH 16/65] minor, remove old cloned iterator --- poly_commit/src/orion/base_field_tests.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/poly_commit/src/orion/base_field_tests.rs b/poly_commit/src/orion/base_field_tests.rs index e85fee58..19b5a5fa 100644 --- a/poly_commit/src/orion/base_field_tests.rs +++ b/poly_commit/src/orion/base_field_tests.rs @@ -79,14 +79,8 @@ where let mut rng = test_rng(); let random_poly = MultiLinearPoly::<F>::random(num_vars, &mut rng); - let random_poly_ext = MultiLinearPoly::new( - random_poly - .coeffs - .iter() - .cloned() - .map(EvalF::from) - .collect(), - ); + let random_poly_ext = + MultiLinearPoly::new(random_poly.coeffs.iter().map(|t| EvalF::from(*t)).collect()); let random_point: Vec<_> = (0..num_vars) .map(|_| EvalF::random_unsafe(&mut rng)) .collect(); From 7838aad79033485af27eab2039d70e33853ccd28 Mon Sep 17 00:00:00 2001 From: Hang Su <tonyfloater@gmail.com> Date: Tue, 26 Nov 2024 04:04:21 -0500 Subject: [PATCH 17/65] standard pcs trait implementation for both flavors of orion impls --- poly_commit/src/orion.rs | 3 + poly_commit/src/orion/base_field_tests.rs | 14 +- poly_commit/src/orion/expander_integration.rs | 226 ++++++++++++++++++ poly_commit/src/orion/utils.rs | 14 +- poly_commit/src/raw.rs | 4 +- poly_commit/src/traits.rs | 4 +- poly_commit/tests/common.rs | 15 +- poly_commit/tests/test_raw.rs | 6 +- 8 files changed, 261 insertions(+), 25 deletions(-) create mode 100644 poly_commit/src/orion/expander_integration.rs diff --git a/poly_commit/src/orion.rs b/poly_commit/src/orion.rs index e8101327..ff1ba022 100644 --- a/poly_commit/src/orion.rs +++ b/poly_commit/src/orion.rs @@ -26,4 +26,7 @@ pub use simd_field_impl::{ #[cfg(test)] mod simd_field_tests; +mod expander_integration; +pub use expander_integration::{OrionBaseFieldPCS, OrionSIMDFieldPCS}; + mod serde; diff --git a/poly_commit/src/orion/base_field_tests.rs b/poly_commit/src/orion/base_field_tests.rs index 19b5a5fa..bf754111 100644 --- a/poly_commit/src/orion/base_field_tests.rs +++ b/poly_commit/src/orion/base_field_tests.rs @@ -78,24 +78,26 @@ where { let mut rng = test_rng(); - let random_poly = MultiLinearPoly::<F>::random(num_vars, &mut rng); - let random_poly_ext = - MultiLinearPoly::new(random_poly.coeffs.iter().map(|t| EvalF::from(*t)).collect()); + let poly = MultiLinearPoly::<F>::random(num_vars, &mut rng); + let poly_ext_coeffs: Vec<_> = poly.coeffs.iter().map(|t| EvalF::from(*t)).collect(); let random_point: Vec<_> = (0..num_vars) .map(|_| EvalF::random_unsafe(&mut rng)) .collect(); - let expected_eval = random_poly_ext.evaluate_jolt(&random_point); + let mut scratch = vec![EvalF::ZERO; 1 << num_vars]; + let expected_eval = + MultiLinearPoly::evaluate_with_buffer(&poly_ext_coeffs, &random_point, &mut scratch); + drop(scratch); let mut transcript: BytesHashTranscript<EvalF, Keccak256hasher> = BytesHashTranscript::new(); let mut transcript_cloned = transcript.clone(); let srs = OrionSRS::from_random::<F>(num_vars, ORION_CODE_PARAMETER_INSTANCE, &mut rng); let mut scratch_pad = OrionScratchPad::<F, ComPackF>::default(); - let commitment = orion_commit_base_field(&srs, &random_poly, &mut scratch_pad).unwrap(); + let commitment = orion_commit_base_field(&srs, &poly, &mut scratch_pad).unwrap(); let (_, opening) = orion_open_base_field::<F, EvalF, ComPackF, OpenPackF, _>( &srs, - &random_poly, + &poly, &random_point, &mut transcript, &scratch_pad, diff --git a/poly_commit/src/orion/expander_integration.rs b/poly_commit/src/orion/expander_integration.rs new file mode 100644 index 00000000..54078b09 --- /dev/null +++ b/poly_commit/src/orion/expander_integration.rs @@ -0,0 +1,226 @@ +use std::marker::PhantomData; +use std::ops::Mul; + +use arith::{Field, SimdField}; +use polynomials::MultiLinearPoly; +use transcript::Transcript; + +use crate::{ + orion::{OrionCommitment, OrionProof, OrionSRS, OrionScratchPad}, + orion_commit_base_field, orion_open_simd_field, PolynomialCommitmentScheme, + StructuredReferenceString, ORION_CODE_PARAMETER_INSTANCE, +}; + +use super::{ + orion_commit_simd_field, orion_open_base_field, orion_verify_base_field, + orion_verify_simd_field, +}; + +impl StructuredReferenceString for OrionSRS { + type PKey = OrionSRS; + + type VKey = OrionSRS; + + fn into_keys(self) -> (Self::PKey, Self::VKey) { + (self.clone(), self.clone()) + } +} + +pub struct OrionBaseFieldPCS<F, EvalF, ComPackF, OpenPackF, T> +where + F: Field, + EvalF: Field + From<F> + Mul<F, Output = EvalF>, + ComPackF: SimdField<Scalar = F>, + OpenPackF: SimdField<Scalar = F>, + T: Transcript<EvalF>, +{ + _marker_f: PhantomData<F>, + _marker_eval_f: PhantomData<EvalF>, + _marker_commit_f: PhantomData<ComPackF>, + _marker_open_f: PhantomData<OpenPackF>, + _marker_t: PhantomData<T>, +} + +impl<F, EvalF, ComPackF, OpenPackF, T> PolynomialCommitmentScheme<EvalF, T> + for OrionBaseFieldPCS<F, EvalF, ComPackF, OpenPackF, T> +where + F: Field, + EvalF: Field + From<F> + Mul<F, Output = EvalF>, + ComPackF: SimdField<Scalar = F>, + OpenPackF: SimdField<Scalar = F>, + T: Transcript<EvalF>, +{ + const NAME: &'static str = "OrionBaseFieldPCS"; + + type Params = usize; + type Poly = MultiLinearPoly<F>; + type EvalPoint = Vec<EvalF>; + type ScratchPad = OrionScratchPad<F, ComPackF>; + + type SRS = OrionSRS; + type Commitment = OrionCommitment; + type Opening = OrionProof<EvalF>; + + fn gen_srs_for_testing(params: &Self::Params, rng: impl rand::RngCore) -> Self::SRS { + OrionSRS::from_random::<F>(*params, ORION_CODE_PARAMETER_INSTANCE, rng) + } + + fn init_scratch_pad(_params: &Self::Params) -> Self::ScratchPad { + OrionScratchPad::default() + } + + fn commit( + _params: &Self::Params, + proving_key: &<Self::SRS as StructuredReferenceString>::PKey, + poly: &Self::Poly, + scratch_pad: &mut Self::ScratchPad, + ) -> Self::Commitment { + orion_commit_base_field(proving_key, poly, scratch_pad).unwrap() + } + + fn open( + _params: &Self::Params, + proving_key: &<Self::SRS as StructuredReferenceString>::PKey, + poly: &Self::Poly, + x: &Self::EvalPoint, + scratch_pad: &mut Self::ScratchPad, + transcript: &mut T, + ) -> (EvalF, Self::Opening) { + orion_open_base_field::<F, EvalF, ComPackF, OpenPackF, T>( + proving_key, + poly, + x, + transcript, + scratch_pad, + ) + } + + fn verify( + _params: &Self::Params, + verifying_key: &<Self::SRS as StructuredReferenceString>::VKey, + commitment: &Self::Commitment, + x: &Self::EvalPoint, + v: EvalF, + opening: &Self::Opening, + transcript: &mut T, + ) -> bool { + orion_verify_base_field::<F, EvalF, ComPackF, OpenPackF, T>( + verifying_key, + commitment, + x, + v, + transcript, + opening, + ) + } +} + +pub struct OrionSIMDFieldPCS<F, SimdF, EvalF, SimdEvalF, ComPackF, OpenPackF, T> +where + F: Field, + SimdF: SimdField<Scalar = F>, + EvalF: Field + From<F> + Mul<F, Output = EvalF>, + SimdEvalF: SimdField<Scalar = EvalF>, + ComPackF: SimdField<Scalar = F>, + OpenPackF: SimdField<Scalar = F>, + T: Transcript<EvalF>, +{ + _marker_f: PhantomData<F>, + _marker_simd_f: PhantomData<SimdF>, + _marker_eval_f: PhantomData<EvalF>, + _marker_simd_eval_f: PhantomData<SimdEvalF>, + _marker_commit_f: PhantomData<ComPackF>, + _marker_open_f: PhantomData<OpenPackF>, + _marker_t: PhantomData<T>, +} + +impl<F, SimdF, EvalF, SimdEvalF, ComPackF, OpenPackF, T> PolynomialCommitmentScheme<EvalF, T> + for OrionSIMDFieldPCS<F, SimdF, EvalF, SimdEvalF, ComPackF, OpenPackF, T> +where + F: Field, + SimdF: SimdField<Scalar = F>, + EvalF: Field + From<F> + Mul<F, Output = EvalF>, + SimdEvalF: SimdField<Scalar = EvalF>, + ComPackF: SimdField<Scalar = F>, + OpenPackF: SimdField<Scalar = F>, + T: Transcript<EvalF>, +{ + const NAME: &'static str = "OrionSIMDFieldPCS"; + + type Params = usize; + type Poly = MultiLinearPoly<SimdF>; + type EvalPoint = Vec<EvalF>; + type ScratchPad = OrionScratchPad<F, ComPackF>; + + type SRS = OrionSRS; + type Commitment = OrionCommitment; + type Opening = OrionProof<SimdEvalF>; + + // NOTE: here we say the number of variables is the sum of 2 following things: + // - number of variables of the multilinear polynomial + // - number of variables reside in the SIMD field - e.g., 3 vars for a SIMD 8 field + fn gen_srs_for_testing(params: &Self::Params, rng: impl rand::RngCore) -> Self::SRS { + OrionSRS::from_random::<F>(*params, ORION_CODE_PARAMETER_INSTANCE, rng) + } + + fn init_scratch_pad(_params: &Self::Params) -> Self::ScratchPad { + OrionScratchPad::default() + } + + fn commit( + _params: &Self::Params, + proving_key: &<Self::SRS as StructuredReferenceString>::PKey, + poly: &Self::Poly, + scratch_pad: &mut Self::ScratchPad, + ) -> Self::Commitment { + orion_commit_simd_field(proving_key, poly, scratch_pad).unwrap() + } + + fn open( + _params: &Self::Params, + proving_key: &<Self::SRS as StructuredReferenceString>::PKey, + poly: &Self::Poly, + x: &Self::EvalPoint, + scratch_pad: &mut Self::ScratchPad, + transcript: &mut T, + ) -> (EvalF, Self::Opening) { + let opening = orion_open_simd_field::<F, SimdF, EvalF, SimdEvalF, ComPackF, OpenPackF, T>( + proving_key, + poly, + x, + transcript, + scratch_pad, + ); + + let poly_ext_coeffs: Vec<_> = poly + .coeffs + .iter() + .flat_map(|p| -> Vec<_> { p.unpack().iter().map(|t| EvalF::from(*t)).collect() }) + .collect(); + + let mut scratch = vec![EvalF::ZERO; 1 << poly.get_num_vars()]; + let eval = MultiLinearPoly::evaluate_with_buffer(&poly_ext_coeffs, x, &mut scratch); + drop(scratch); + + (eval, opening) + } + + fn verify( + _params: &Self::Params, + verifying_key: &<Self::SRS as StructuredReferenceString>::VKey, + commitment: &Self::Commitment, + x: &Self::EvalPoint, + v: EvalF, + opening: &Self::Opening, + transcript: &mut T, + ) -> bool { + orion_verify_simd_field::<F, SimdF, EvalF, SimdEvalF, ComPackF, OpenPackF, T>( + verifying_key, + commitment, + x, + v, + transcript, + opening, + ) + } +} diff --git a/poly_commit/src/orion/utils.rs b/poly_commit/src/orion/utils.rs index 62abad97..b9fe8627 100644 --- a/poly_commit/src/orion/utils.rs +++ b/poly_commit/src/orion/utils.rs @@ -4,7 +4,7 @@ use arith::{Field, FieldSerdeError, SimdField}; use thiserror::Error; use transcript::Transcript; -use crate::{traits::TensorCodeIOPPCS, StructuredReferenceString, PCS_SOUNDNESS_BITS}; +use crate::{traits::TensorCodeIOPPCS, PCS_SOUNDNESS_BITS}; use super::linear_code::{OrionCode, OrionCodeParameter}; @@ -27,7 +27,7 @@ pub type OrionResult<T> = std::result::Result<T, OrionPCSError>; * RELEVANT TYPES SETUP */ -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Default)] pub struct OrionSRS { pub num_variables: usize, pub code_instance: OrionCode, @@ -43,16 +43,6 @@ impl TensorCodeIOPPCS for OrionSRS { } } -impl StructuredReferenceString for OrionSRS { - type PKey = OrionSRS; - - type VKey = OrionSRS; - - fn into_keys(self) -> (Self::PKey, Self::VKey) { - (self.clone(), self.clone()) - } -} - impl OrionSRS { pub fn new<F: Field>(num_variables: usize, code_instance: OrionCode) -> OrionResult<Self> { let (_, msg_size) = Self::evals_shape::<F>(num_variables); diff --git a/poly_commit/src/raw.rs b/poly_commit/src/raw.rs index 3e59002b..2ecf48c7 100644 --- a/poly_commit/src/raw.rs +++ b/poly_commit/src/raw.rs @@ -23,7 +23,7 @@ pub struct RawMultiLinearScratchPad<F: Field> { // Raw commitment for multi-linear polynomials pub struct RawMultiLinear {} -impl<F: Field> PolynomialCommitmentScheme<F> for RawMultiLinear { +impl<F: Field, T: Transcript<F>> PolynomialCommitmentScheme<F, T> for RawMultiLinear { const NAME: &'static str = "RawMultiLinear"; type Params = RawMultiLinearParams; @@ -64,6 +64,7 @@ impl<F: Field> PolynomialCommitmentScheme<F> for RawMultiLinear { poly: &Self::Poly, x: &Self::EvalPoint, scratch_pad: &mut Self::ScratchPad, + _transcript: &mut T, ) -> (F, Self::Opening) { assert!(x.len() == params.n_vars); ( @@ -83,6 +84,7 @@ impl<F: Field> PolynomialCommitmentScheme<F> for RawMultiLinear { x: &Self::EvalPoint, v: F, _opening: &Self::Opening, + _transcript: &mut T, ) -> bool { assert!(x.len() == params.n_vars); MultiLinearPoly::<F>::evaluate_with_buffer( diff --git a/poly_commit/src/traits.rs b/poly_commit/src/traits.rs index 4491f84f..2016fa8c 100644 --- a/poly_commit/src/traits.rs +++ b/poly_commit/src/traits.rs @@ -16,7 +16,7 @@ pub trait StructuredReferenceString { } /// Standard Polynomial commitment scheme (PCS) trait. -pub trait PolynomialCommitmentScheme<F: Field> { +pub trait PolynomialCommitmentScheme<F: Field, T: Transcript<F>> { const NAME: &'static str; type Params: Clone + Debug + Default; @@ -50,6 +50,7 @@ pub trait PolynomialCommitmentScheme<F: Field> { poly: &Self::Poly, x: &Self::EvalPoint, scratch_pad: &mut Self::ScratchPad, + transcript: &mut T, ) -> (F, Self::Opening); /// Verify the opening of a polynomial at a point. @@ -60,6 +61,7 @@ pub trait PolynomialCommitmentScheme<F: Field> { x: &Self::EvalPoint, v: F, opening: &Self::Opening, + transcript: &mut T, ) -> bool; } diff --git a/poly_commit/tests/common.rs b/poly_commit/tests/common.rs index 541c44aa..85a2ac32 100644 --- a/poly_commit/tests/common.rs +++ b/poly_commit/tests/common.rs @@ -9,7 +9,7 @@ use polynomials::MultiLinearPoly; use rand::thread_rng; use transcript::Transcript; -pub fn test_pcs<F: Field, P: PolynomialCommitmentScheme<F>>( +pub fn test_pcs<F: Field, T: Transcript<F>, P: PolynomialCommitmentScheme<F, T>>( params: &P::Params, poly: &P::Poly, xs: &[P::EvalPoint], @@ -17,19 +17,28 @@ pub fn test_pcs<F: Field, P: PolynomialCommitmentScheme<F>>( let mut rng = thread_rng(); let srs = P::gen_srs_for_testing(params, &mut rng); let (proving_key, verification_key) = srs.into_keys(); + let mut transcript = T::new(); let mut scratch_pad = P::init_scratch_pad(params); let commitment = P::commit(params, &proving_key, poly, &mut scratch_pad); for x in xs { - let (v, opening) = P::open(params, &proving_key, poly, x, &mut scratch_pad); + let (v, opening) = P::open( + params, + &proving_key, + poly, + x, + &mut scratch_pad, + &mut transcript, + ); assert!(P::verify( params, &verification_key, &commitment, x, v, - &opening + &opening, + &mut transcript )); } } diff --git a/poly_commit/tests/test_raw.rs b/poly_commit/tests/test_raw.rs index b779f97b..b4990711 100644 --- a/poly_commit/tests/test_raw.rs +++ b/poly_commit/tests/test_raw.rs @@ -9,7 +9,7 @@ use poly_commit::{ }; use polynomials::MultiLinearPoly; use rand::thread_rng; -use transcript::{BytesHashTranscript, SHA256hasher, Transcript}; +use transcript::{BytesHashTranscript, Keccak256hasher, SHA256hasher, Transcript}; #[test] fn test_raw() { @@ -24,7 +24,9 @@ fn test_raw() { }) .collect::<Vec<Vec<BN254Fr>>>(); - common::test_pcs::<BN254Fr, RawMultiLinear>(¶ms, &poly, &xs); + common::test_pcs::<BN254Fr, BytesHashTranscript<_, Keccak256hasher>, RawMultiLinear>( + ¶ms, &poly, &xs, + ); } fn test_raw_gkr_helper<C: GKRFieldConfig, T: Transcript<C::ChallengeField>>( From a885ed5d1841f2fd46adf5dc653c083dc4b5ea46 Mon Sep 17 00:00:00 2001 From: Hang Su <tonyfloater@gmail.com> Date: Tue, 26 Nov 2024 20:14:06 -0500 Subject: [PATCH 18/65] orion simd pcs aligning with PCSForExpanderGKR, restrict constraint to ExtensionField --- poly_commit/benches/orion.rs | 8 +- poly_commit/src/orion/base_field_impl.rs | 7 +- poly_commit/src/orion/base_field_tests.rs | 6 +- poly_commit/src/orion/expander_integration.rs | 110 +++++++++++++++--- poly_commit/src/orion/simd_field_impl.rs | 7 +- poly_commit/src/orion/simd_field_tests.rs | 6 +- poly_commit/src/orion/utils.rs | 13 ++- 7 files changed, 118 insertions(+), 39 deletions(-) diff --git a/poly_commit/benches/orion.rs b/poly_commit/benches/orion.rs index eb8f0c37..09be2b8e 100644 --- a/poly_commit/benches/orion.rs +++ b/poly_commit/benches/orion.rs @@ -1,6 +1,6 @@ -use std::{hint::black_box, ops::Mul}; +use std::hint::black_box; -use arith::{Field, SimdField}; +use arith::{ExtensionField, Field, SimdField}; use ark_std::test_rng; use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion}; use gf2::{GF2x128, GF2x8, GF2}; @@ -106,7 +106,7 @@ fn base_field_opening_benchmark_helper<F, EvalF, ComPackF, OpenPackF, T>( highest_num_vars: usize, ) where F: Field, - EvalF: Field + From<F> + Mul<F, Output = EvalF>, + EvalF: ExtensionField<BaseField = F>, ComPackF: SimdField<Scalar = F>, OpenPackF: SimdField<Scalar = F>, T: Transcript<EvalF>, @@ -169,7 +169,7 @@ fn simd_field_opening_benchmark_helper<F, SimdF, EvalF, SimdEvalF, ComPackF, Ope ) where F: Field, SimdF: SimdField<Scalar = F>, - EvalF: Field + From<F> + Mul<F, Output = EvalF>, + EvalF: ExtensionField<BaseField = F>, SimdEvalF: SimdField<Scalar = EvalF>, ComPackF: SimdField<Scalar = F>, OpenPackF: SimdField<Scalar = F>, diff --git a/poly_commit/src/orion/base_field_impl.rs b/poly_commit/src/orion/base_field_impl.rs index 70fce1ba..bffe8281 100644 --- a/poly_commit/src/orion/base_field_impl.rs +++ b/poly_commit/src/orion/base_field_impl.rs @@ -1,7 +1,6 @@ use std::iter; -use std::ops::Mul; -use arith::{Field, SimdField}; +use arith::{ExtensionField, Field, SimdField}; use polynomials::{EqPolynomial, MultiLinearPoly}; use transcript::Transcript; @@ -68,7 +67,7 @@ pub fn orion_open_base_field<F, EvalF, ComPackF, OpenPackF, T>( ) -> (EvalF, OrionProof<EvalF>) where F: Field, - EvalF: Field + From<F> + Mul<F, Output = EvalF>, + EvalF: ExtensionField<BaseField = F>, ComPackF: SimdField<Scalar = F>, OpenPackF: SimdField<Scalar = F>, T: Transcript<EvalF>, @@ -147,7 +146,7 @@ pub fn orion_verify_base_field<F, EvalF, ComPackF, OpenPackF, T>( ) -> bool where F: Field, - EvalF: Field + From<F> + Mul<F, Output = EvalF>, + EvalF: ExtensionField<BaseField = F>, ComPackF: SimdField<Scalar = F>, OpenPackF: SimdField<Scalar = F>, T: Transcript<EvalF>, diff --git a/poly_commit/src/orion/base_field_tests.rs b/poly_commit/src/orion/base_field_tests.rs index bf754111..3d9e8a00 100644 --- a/poly_commit/src/orion/base_field_tests.rs +++ b/poly_commit/src/orion/base_field_tests.rs @@ -1,6 +1,4 @@ -use std::ops::Mul; - -use arith::{Field, SimdField}; +use arith::{ExtensionField, Field, SimdField}; use ark_std::test_rng; use gf2::{GF2x128, GF2x64, GF2x8, GF2}; use gf2_128::GF2_128; @@ -72,7 +70,7 @@ fn test_orion_commit_base_field_consistency() { fn test_orion_pcs_base_full_e2e_generics<F, EvalF, ComPackF, OpenPackF>(num_vars: usize) where F: Field, - EvalF: Field + Mul<F, Output = EvalF> + From<F>, + EvalF: ExtensionField<BaseField = F>, ComPackF: SimdField<Scalar = F>, OpenPackF: SimdField<Scalar = F>, { diff --git a/poly_commit/src/orion/expander_integration.rs b/poly_commit/src/orion/expander_integration.rs index 54078b09..dc45896d 100644 --- a/poly_commit/src/orion/expander_integration.rs +++ b/poly_commit/src/orion/expander_integration.rs @@ -1,20 +1,11 @@ use std::marker::PhantomData; -use std::ops::Mul; -use arith::{Field, SimdField}; +use arith::{ExtensionField, Field, SimdField}; +use gkr_field_config::GKRFieldConfig; use polynomials::MultiLinearPoly; use transcript::Transcript; -use crate::{ - orion::{OrionCommitment, OrionProof, OrionSRS, OrionScratchPad}, - orion_commit_base_field, orion_open_simd_field, PolynomialCommitmentScheme, - StructuredReferenceString, ORION_CODE_PARAMETER_INSTANCE, -}; - -use super::{ - orion_commit_simd_field, orion_open_base_field, orion_verify_base_field, - orion_verify_simd_field, -}; +use crate::{orion::*, PCSForExpanderGKR, PolynomialCommitmentScheme, StructuredReferenceString}; impl StructuredReferenceString for OrionSRS { type PKey = OrionSRS; @@ -29,7 +20,7 @@ impl StructuredReferenceString for OrionSRS { pub struct OrionBaseFieldPCS<F, EvalF, ComPackF, OpenPackF, T> where F: Field, - EvalF: Field + From<F> + Mul<F, Output = EvalF>, + EvalF: ExtensionField<BaseField = F>, ComPackF: SimdField<Scalar = F>, OpenPackF: SimdField<Scalar = F>, T: Transcript<EvalF>, @@ -45,7 +36,7 @@ impl<F, EvalF, ComPackF, OpenPackF, T> PolynomialCommitmentScheme<EvalF, T> for OrionBaseFieldPCS<F, EvalF, ComPackF, OpenPackF, T> where F: Field, - EvalF: Field + From<F> + Mul<F, Output = EvalF>, + EvalF: ExtensionField<BaseField = F>, ComPackF: SimdField<Scalar = F>, OpenPackF: SimdField<Scalar = F>, T: Transcript<EvalF>, @@ -119,7 +110,7 @@ pub struct OrionSIMDFieldPCS<F, SimdF, EvalF, SimdEvalF, ComPackF, OpenPackF, T> where F: Field, SimdF: SimdField<Scalar = F>, - EvalF: Field + From<F> + Mul<F, Output = EvalF>, + EvalF: ExtensionField<BaseField = F>, SimdEvalF: SimdField<Scalar = EvalF>, ComPackF: SimdField<Scalar = F>, OpenPackF: SimdField<Scalar = F>, @@ -139,7 +130,7 @@ impl<F, SimdF, EvalF, SimdEvalF, ComPackF, OpenPackF, T> PolynomialCommitmentSch where F: Field, SimdF: SimdField<Scalar = F>, - EvalF: Field + From<F> + Mul<F, Output = EvalF>, + EvalF: ExtensionField<BaseField = F>, SimdEvalF: SimdField<Scalar = EvalF>, ComPackF: SimdField<Scalar = F>, OpenPackF: SimdField<Scalar = F>, @@ -224,3 +215,90 @@ where ) } } + +impl<C, SimdEvalF, ComPackF, OpenPackF, T> PCSForExpanderGKR<C, T> + for OrionSIMDFieldPCS< + C::CircuitField, + C::SimdCircuitField, + C::ChallengeField, + SimdEvalF, + ComPackF, + OpenPackF, + T, + > +where + C: GKRFieldConfig, + T: Transcript<C::ChallengeField>, + SimdEvalF: SimdField<Scalar = C::ChallengeField>, + ComPackF: SimdField<Scalar = C::CircuitField>, + OpenPackF: SimdField<Scalar = C::CircuitField>, +{ + const NAME: &'static str = "OrionSIMDPCSForExpanderGKR"; + + type Params = usize; + type ScratchPad = OrionScratchPad<C::CircuitField, ComPackF>; + + type Commitment = OrionCommitment; + type Opening = OrionProof<SimdEvalF>; + type SRS = OrionSRS; + + #[allow(unused)] + fn gen_params(n_input_vars: usize) -> Self::Params { + todo!() + } + + #[allow(unused)] + fn gen_srs_for_testing( + params: &Self::Params, + mpi_config: &mpi_config::MPIConfig, + rng: impl rand::RngCore, + ) -> Self::SRS { + todo!() + } + + #[allow(unused)] + fn init_scratch_pad( + params: &Self::Params, + mpi_config: &mpi_config::MPIConfig, + ) -> Self::ScratchPad { + todo!() + } + + #[allow(unused)] + fn commit( + params: &Self::Params, + mpi_config: &mpi_config::MPIConfig, + proving_key: &<Self::SRS as StructuredReferenceString>::PKey, + poly: &MultiLinearPoly<<C as GKRFieldConfig>::SimdCircuitField>, + scratch_pad: &mut Self::ScratchPad, + ) -> Self::Commitment { + todo!() + } + + #[allow(unused)] + fn open( + params: &Self::Params, + mpi_config: &mpi_config::MPIConfig, + proving_key: &<Self::SRS as StructuredReferenceString>::PKey, + poly: &MultiLinearPoly<<C as GKRFieldConfig>::SimdCircuitField>, + x: &crate::ExpanderGKRChallenge<C>, + transcript: &mut T, // add transcript here to allow interactive arguments + scratch_pad: &mut Self::ScratchPad, + ) -> Self::Opening { + todo!() + } + + #[allow(unused)] + fn verify( + params: &Self::Params, + mpi_config: &mpi_config::MPIConfig, + verifying_key: &<Self::SRS as StructuredReferenceString>::VKey, + commitment: &Self::Commitment, + x: &crate::ExpanderGKRChallenge<C>, + v: <C as GKRFieldConfig>::ChallengeField, + transcript: &mut T, // add transcript here to allow interactive arguments + opening: &Self::Opening, + ) -> bool { + todo!() + } +} diff --git a/poly_commit/src/orion/simd_field_impl.rs b/poly_commit/src/orion/simd_field_impl.rs index 23a5dda0..f4d1fe0d 100644 --- a/poly_commit/src/orion/simd_field_impl.rs +++ b/poly_commit/src/orion/simd_field_impl.rs @@ -1,7 +1,6 @@ use std::iter; -use std::ops::Mul; -use arith::{Field, SimdField}; +use arith::{ExtensionField, Field, SimdField}; use polynomials::{EqPolynomial, MultiLinearPoly}; use transcript::Transcript; @@ -113,7 +112,7 @@ pub fn orion_open_simd_field<F, SimdF, EvalF, SimdEvalF, ComPackF, OpenPackF, T> where F: Field, SimdF: SimdField<Scalar = F>, - EvalF: Field + From<F> + Mul<F, Output = EvalF>, + EvalF: ExtensionField<BaseField = F>, SimdEvalF: SimdField<Scalar = EvalF>, ComPackF: SimdField<Scalar = F>, OpenPackF: SimdField<Scalar = F>, @@ -194,7 +193,7 @@ pub fn orion_verify_simd_field<F, SimdF, EvalF, SimdEvalF, ComPackF, OpenPackF, where F: Field, SimdF: SimdField<Scalar = F>, - EvalF: Field + From<F> + Mul<F, Output = EvalF>, + EvalF: ExtensionField<BaseField = F>, SimdEvalF: SimdField<Scalar = EvalF>, ComPackF: SimdField<Scalar = F>, OpenPackF: SimdField<Scalar = F>, diff --git a/poly_commit/src/orion/simd_field_tests.rs b/poly_commit/src/orion/simd_field_tests.rs index c36ac6c7..29996c82 100644 --- a/poly_commit/src/orion/simd_field_tests.rs +++ b/poly_commit/src/orion/simd_field_tests.rs @@ -1,6 +1,4 @@ -use std::ops::Mul; - -use arith::{Field, SimdField}; +use arith::{ExtensionField, Field, SimdField}; use ark_std::test_rng; use gf2::{GF2x128, GF2x64, GF2x8, GF2}; use gf2_128::{GF2_128x8, GF2_128}; @@ -90,7 +88,7 @@ fn test_orion_pcs_simd_full_e2e_generics<F, SimdF, EvalF, SimdEvalF, ComPackF, O ) where F: Field, SimdF: SimdField<Scalar = F>, - EvalF: Field + From<F> + Mul<F, Output = EvalF>, + EvalF: ExtensionField<BaseField = F>, SimdEvalF: SimdField<Scalar = EvalF>, ComPackF: SimdField<Scalar = F>, OpenPackF: SimdField<Scalar = F>, diff --git a/poly_commit/src/orion/utils.rs b/poly_commit/src/orion/utils.rs index b9fe8627..991341a6 100644 --- a/poly_commit/src/orion/utils.rs +++ b/poly_commit/src/orion/utils.rs @@ -1,6 +1,6 @@ -use std::{marker::PhantomData, ops::Mul}; +use std::marker::PhantomData; -use arith::{Field, FieldSerdeError, SimdField}; +use arith::{ExtensionField, Field, FieldSerdeError, SimdField}; use thiserror::Error; use transcript::Transcript; @@ -85,6 +85,13 @@ where pub _phantom: PhantomData<ComPackF>, } +unsafe impl<F, ComPackF> Send for OrionScratchPad<F, ComPackF> +where + F: Field, + ComPackF: SimdField<Scalar = F>, +{ +} + #[derive(Clone, Debug, Default)] pub struct OrionProof<EvalF: Field> { pub eval_row: Vec<EvalF>, @@ -138,7 +145,7 @@ pub(crate) fn orion_mt_openings<F, EvalF, ComPackF, T>( ) -> Vec<tree::RangePath> where F: Field, - EvalF: Field + From<F> + Mul<F, Output = EvalF>, + EvalF: ExtensionField<BaseField = F>, ComPackF: SimdField<Scalar = F>, T: Transcript<EvalF>, { From 0f4250729b7f4b61054764a7e8a63f382c9abb02 Mon Sep 17 00:00:00 2001 From: Hang Su <tonyfloater@gmail.com> Date: Wed, 27 Nov 2024 03:55:50 -0500 Subject: [PATCH 19/65] simplified type constraints for orion, no need for SimdEvalF --- poly_commit/benches/orion.rs | 7 +- poly_commit/src/orion/expander_integration.rs | 22 +++-- poly_commit/src/orion/simd_field_impl.rs | 80 ++++++++++--------- poly_commit/src/orion/simd_field_tests.rs | 19 ++--- poly_commit/src/orion/utils.rs | 25 ------ 5 files changed, 58 insertions(+), 95 deletions(-) diff --git a/poly_commit/benches/orion.rs b/poly_commit/benches/orion.rs index 09be2b8e..0e9fa0bd 100644 --- a/poly_commit/benches/orion.rs +++ b/poly_commit/benches/orion.rs @@ -4,7 +4,7 @@ use arith::{ExtensionField, Field, SimdField}; use ark_std::test_rng; use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion}; use gf2::{GF2x128, GF2x8, GF2}; -use gf2_128::{GF2_128x8, GF2_128}; +use gf2_128::GF2_128; use poly_commit::*; use polynomials::MultiLinearPoly; use transcript::{BytesHashTranscript, Keccak256hasher, Transcript}; @@ -162,7 +162,7 @@ fn orion_base_field_opening_benchmark(c: &mut Criterion) { >(c, 19, 30); } -fn simd_field_opening_benchmark_helper<F, SimdF, EvalF, SimdEvalF, ComPackF, OpenPackF, T>( +fn simd_field_opening_benchmark_helper<F, SimdF, EvalF, ComPackF, OpenPackF, T>( c: &mut Criterion, lowest_num_vars: usize, highest_num_vars: usize, @@ -170,7 +170,6 @@ fn simd_field_opening_benchmark_helper<F, SimdF, EvalF, SimdEvalF, ComPackF, Ope F: Field, SimdF: SimdField<Scalar = F>, EvalF: ExtensionField<BaseField = F>, - SimdEvalF: SimdField<Scalar = EvalF>, ComPackF: SimdField<Scalar = F>, OpenPackF: SimdField<Scalar = F>, T: Transcript<EvalF>, @@ -212,7 +211,6 @@ fn simd_field_opening_benchmark_helper<F, SimdF, EvalF, SimdEvalF, ComPackF, Ope F, SimdF, EvalF, - SimdEvalF, ComPackF, OpenPackF, T, @@ -231,7 +229,6 @@ fn orion_simd_field_opening_benchmark(c: &mut Criterion) { GF2, GF2x8, GF2_128, - GF2_128x8, GF2x128, GF2x8, BytesHashTranscript<_, Keccak256hasher>, diff --git a/poly_commit/src/orion/expander_integration.rs b/poly_commit/src/orion/expander_integration.rs index dc45896d..7573ed36 100644 --- a/poly_commit/src/orion/expander_integration.rs +++ b/poly_commit/src/orion/expander_integration.rs @@ -106,12 +106,11 @@ where } } -pub struct OrionSIMDFieldPCS<F, SimdF, EvalF, SimdEvalF, ComPackF, OpenPackF, T> +pub struct OrionSIMDFieldPCS<F, SimdF, EvalF, ComPackF, OpenPackF, T> where F: Field, SimdF: SimdField<Scalar = F>, EvalF: ExtensionField<BaseField = F>, - SimdEvalF: SimdField<Scalar = EvalF>, ComPackF: SimdField<Scalar = F>, OpenPackF: SimdField<Scalar = F>, T: Transcript<EvalF>, @@ -119,19 +118,17 @@ where _marker_f: PhantomData<F>, _marker_simd_f: PhantomData<SimdF>, _marker_eval_f: PhantomData<EvalF>, - _marker_simd_eval_f: PhantomData<SimdEvalF>, _marker_commit_f: PhantomData<ComPackF>, _marker_open_f: PhantomData<OpenPackF>, _marker_t: PhantomData<T>, } -impl<F, SimdF, EvalF, SimdEvalF, ComPackF, OpenPackF, T> PolynomialCommitmentScheme<EvalF, T> - for OrionSIMDFieldPCS<F, SimdF, EvalF, SimdEvalF, ComPackF, OpenPackF, T> +impl<F, SimdF, EvalF, ComPackF, OpenPackF, T> PolynomialCommitmentScheme<EvalF, T> + for OrionSIMDFieldPCS<F, SimdF, EvalF, ComPackF, OpenPackF, T> where F: Field, SimdF: SimdField<Scalar = F>, EvalF: ExtensionField<BaseField = F>, - SimdEvalF: SimdField<Scalar = EvalF>, ComPackF: SimdField<Scalar = F>, OpenPackF: SimdField<Scalar = F>, T: Transcript<EvalF>, @@ -145,7 +142,7 @@ where type SRS = OrionSRS; type Commitment = OrionCommitment; - type Opening = OrionProof<SimdEvalF>; + type Opening = OrionProof<EvalF>; // NOTE: here we say the number of variables is the sum of 2 following things: // - number of variables of the multilinear polynomial @@ -175,7 +172,7 @@ where scratch_pad: &mut Self::ScratchPad, transcript: &mut T, ) -> (EvalF, Self::Opening) { - let opening = orion_open_simd_field::<F, SimdF, EvalF, SimdEvalF, ComPackF, OpenPackF, T>( + let opening = orion_open_simd_field::<F, SimdF, EvalF, ComPackF, OpenPackF, T>( proving_key, poly, x, @@ -205,7 +202,7 @@ where opening: &Self::Opening, transcript: &mut T, ) -> bool { - orion_verify_simd_field::<F, SimdF, EvalF, SimdEvalF, ComPackF, OpenPackF, T>( + orion_verify_simd_field::<F, SimdF, EvalF, ComPackF, OpenPackF, T>( verifying_key, commitment, x, @@ -216,12 +213,12 @@ where } } -impl<C, SimdEvalF, ComPackF, OpenPackF, T> PCSForExpanderGKR<C, T> +// TODO ... +impl<C, ComPackF, OpenPackF, T> PCSForExpanderGKR<C, T> for OrionSIMDFieldPCS< C::CircuitField, C::SimdCircuitField, C::ChallengeField, - SimdEvalF, ComPackF, OpenPackF, T, @@ -229,7 +226,6 @@ impl<C, SimdEvalF, ComPackF, OpenPackF, T> PCSForExpanderGKR<C, T> where C: GKRFieldConfig, T: Transcript<C::ChallengeField>, - SimdEvalF: SimdField<Scalar = C::ChallengeField>, ComPackF: SimdField<Scalar = C::CircuitField>, OpenPackF: SimdField<Scalar = C::CircuitField>, { @@ -239,7 +235,7 @@ where type ScratchPad = OrionScratchPad<C::CircuitField, ComPackF>; type Commitment = OrionCommitment; - type Opening = OrionProof<SimdEvalF>; + type Opening = OrionProof<C::ChallengeField>; type SRS = OrionSRS; #[allow(unused)] diff --git a/poly_commit/src/orion/simd_field_impl.rs b/poly_commit/src/orion/simd_field_impl.rs index f4d1fe0d..b9f98db3 100644 --- a/poly_commit/src/orion/simd_field_impl.rs +++ b/poly_commit/src/orion/simd_field_impl.rs @@ -86,13 +86,13 @@ where drop(scratch); // NOTE: reshuffle the transposed matrix, from SIMD over row to SIMD over col - let mut scratch = vec![F::ZERO; SimdF::PACK_SIZE * PackF::PACK_SIZE]; + let mut scratch = vec![F::ZERO; SimdF::PACK_SIZE * row_num]; evaluations - .chunks(PackF::PACK_SIZE) - .flat_map(|circuit_simd_chunk| -> Vec<PackF> { - let mut temp: Vec<F> = circuit_simd_chunk.iter().flat_map(|c| c.unpack()).collect(); - transpose_in_place(&mut temp, &mut scratch, PackF::PACK_SIZE); - temp.chunks(PackF::PACK_SIZE).map(PackF::pack).collect() + .chunks(row_num) + .flat_map(|row_simds| -> Vec<_> { + let mut elts: Vec<_> = row_simds.iter().flat_map(|f| f.unpack()).collect(); + transpose_in_place(&mut elts, &mut scratch, row_num); + elts.chunks(PackF::PACK_SIZE).map(PackF::pack).collect() }) .collect() } @@ -102,24 +102,21 @@ where // as this directly plug into GKR argument system. // In that context, there is no need to evaluate, // as evaluation statement can be reduced on the verifier side. -pub fn orion_open_simd_field<F, SimdF, EvalF, SimdEvalF, ComPackF, OpenPackF, T>( +pub fn orion_open_simd_field<F, SimdF, EvalF, ComPackF, OpenPackF, T>( pk: &OrionSRS, poly: &MultiLinearPoly<SimdF>, point: &[EvalF], transcript: &mut T, scratch_pad: &OrionScratchPad<F, ComPackF>, -) -> OrionProof<SimdEvalF> +) -> OrionProof<EvalF> where F: Field, SimdF: SimdField<Scalar = F>, EvalF: ExtensionField<BaseField = F>, - SimdEvalF: SimdField<Scalar = EvalF>, ComPackF: SimdField<Scalar = F>, OpenPackF: SimdField<Scalar = F>, T: Transcript<EvalF>, { - assert_eq!(SimdF::PACK_SIZE, SimdEvalF::PACK_SIZE); - let (row_num, msg_size) = { let num_vars = poly.get_num_vars() + SimdF::PACK_SIZE.ilog2() as usize; assert_eq!(num_vars, point.len()); @@ -146,29 +143,30 @@ where assert_eq!(row_num % OpenPackF::PACK_SIZE, 0); // NOTE: working on evaluation response of tensor code IOP based PCS - let mut eval_row = vec![SimdEvalF::ZERO; msg_size]; + let mut eval_row = vec![EvalF::ZERO; msg_size * SimdF::PACK_SIZE]; let eq_coeffs = EqPolynomial::build_eq_x_r(&point[num_vars_in_unpacked_msg..]); luts.build(&eq_coeffs); packed_shuffled_evals - .chunks(tables_num * SimdEvalF::PACK_SIZE) + .chunks(tables_num) .zip(eval_row.iter_mut()) - .for_each(|(p_col, res)| *res = luts.lookup_and_sum_simd(p_col)); + .for_each(|(p_col, res)| *res = luts.lookup_and_sum(p_col)); // NOTE: draw random linear combination out // and compose proximity response(s) of tensor code IOP based PCS let proximity_test_num = pk.proximity_repetitions::<EvalF>(PCS_SOUNDNESS_BITS); - let mut proximity_rows = vec![vec![SimdEvalF::ZERO; msg_size]; proximity_test_num]; + let mut proximity_rows = + vec![vec![EvalF::ZERO; msg_size * SimdF::PACK_SIZE]; proximity_test_num]; proximity_rows.iter_mut().for_each(|row_buffer| { let random_coeffs = transcript.generate_challenge_field_elements(row_num); luts.build(&random_coeffs); packed_shuffled_evals - .chunks(tables_num * SimdEvalF::PACK_SIZE) + .chunks(tables_num) .zip(row_buffer.iter_mut()) - .for_each(|(p_col, res)| *res = luts.lookup_and_sum_simd(p_col)); + .for_each(|(p_col, res)| *res = luts.lookup_and_sum(p_col)); }); drop(luts); @@ -182,19 +180,18 @@ where } } -pub fn orion_verify_simd_field<F, SimdF, EvalF, SimdEvalF, ComPackF, OpenPackF, T>( +pub fn orion_verify_simd_field<F, SimdF, EvalF, ComPackF, OpenPackF, T>( vk: &OrionSRS, commitment: &OrionCommitment, point: &[EvalF], evaluation: EvalF, transcript: &mut T, - proof: &OrionProof<SimdEvalF>, + proof: &OrionProof<EvalF>, ) -> bool where F: Field, SimdF: SimdField<Scalar = F>, EvalF: ExtensionField<BaseField = F>, - SimdEvalF: SimdField<Scalar = EvalF>, ComPackF: SimdField<Scalar = F>, OpenPackF: SimdField<Scalar = F>, T: Transcript<EvalF>, @@ -209,13 +206,13 @@ where let num_vars_in_unpacked_msg = point.len() - num_vars_in_row; // NOTE: working on evaluation response, evaluate the rest of the response - let eval_unpacked: Vec<_> = proof.eval_row.iter().flat_map(|e| e.unpack()).collect(); - let mut scratch = vec![EvalF::ZERO; msg_size * SimdEvalF::PACK_SIZE]; + let mut scratch = vec![EvalF::ZERO; msg_size * SimdF::PACK_SIZE]; let final_eval = MultiLinearPoly::evaluate_with_buffer( - &eval_unpacked, + &proof.eval_row, &point[..num_vars_in_unpacked_msg], &mut scratch, ); + if final_eval != evaluation { return false; } @@ -236,20 +233,15 @@ where // NOTE: prepare the interleaved alphabets from the MT paths, // but reshuffle the packed elements into another direction - let mut scratch = vec![F::ZERO; SimdF::PACK_SIZE * OpenPackF::PACK_SIZE]; + let mut scratch = vec![F::ZERO; SimdF::PACK_SIZE * row_num]; let shuffled_interleaved_alphabet: Vec<Vec<OpenPackF>> = proof .query_openings .iter() .map(|c| -> Vec<_> { - c.unpack_field_elems::<F, ComPackF>() - .chunks_mut(SimdF::PACK_SIZE * OpenPackF::PACK_SIZE) - .flat_map(|circuit_simd_chunk| -> Vec<OpenPackF> { - transpose_in_place(circuit_simd_chunk, &mut scratch, OpenPackF::PACK_SIZE); - circuit_simd_chunk - .chunks(OpenPackF::PACK_SIZE) - .map(OpenPackF::pack) - .collect() - }) + let mut elts = c.unpack_field_elems::<F, ComPackF>(); + transpose_in_place(&mut elts, &mut scratch, row_num); + elts.chunks(OpenPackF::PACK_SIZE) + .map(OpenPackF::pack) .collect() }) .collect(); @@ -260,15 +252,20 @@ where assert_eq!(row_num % OpenPackF::PACK_SIZE, 0); let eq_linear_combination = EqPolynomial::build_eq_x_r(&point[num_vars_in_unpacked_msg..]); + let mut scratch_msg = vec![EvalF::ZERO; SimdF::PACK_SIZE * msg_size]; + let mut scratch_codeword = vec![EvalF::ZERO; SimdF::PACK_SIZE * vk.codeword_len()]; random_linear_combinations .iter() .zip(proof.proximity_rows.iter()) .chain(iter::once((&eq_linear_combination, &proof.eval_row))) .all(|(rl, msg)| { - let codeword = match vk.code_instance.encode(msg) { - Ok(c) => c, - _ => return false, - }; + let mut msg_cloned = msg.clone(); + transpose_in_place(&mut msg_cloned, &mut scratch_msg, msg_size); + let mut codeword: Vec<_> = msg_cloned + .chunks(msg_size) + .flat_map(|m| vk.code_instance.encode(m).unwrap()) + .collect(); + transpose_in_place(&mut codeword, &mut scratch_codeword, SimdF::PACK_SIZE); luts.build(rl); @@ -277,8 +274,13 @@ where .zip(shuffled_interleaved_alphabet.iter()) .all(|(&qi, interleaved_alphabet)| { let index = qi % vk.codeword_len(); - let alphabet: SimdEvalF = luts.lookup_and_sum_simd(interleaved_alphabet); - alphabet == codeword[index] + + (index * SimdF::PACK_SIZE..(index + 1) * SimdF::PACK_SIZE) + .zip(interleaved_alphabet.chunks(tables_num)) + .all(|(i, packed_index)| { + let alphabet = luts.lookup_and_sum(packed_index); + alphabet == codeword[i] + }) }) }) } diff --git a/poly_commit/src/orion/simd_field_tests.rs b/poly_commit/src/orion/simd_field_tests.rs index 29996c82..115af52c 100644 --- a/poly_commit/src/orion/simd_field_tests.rs +++ b/poly_commit/src/orion/simd_field_tests.rs @@ -1,7 +1,7 @@ use arith::{ExtensionField, Field, SimdField}; use ark_std::test_rng; use gf2::{GF2x128, GF2x64, GF2x8, GF2}; -use gf2_128::{GF2_128x8, GF2_128}; +use gf2_128::GF2_128; use polynomials::MultiLinearPoly; use transcript::{BytesHashTranscript, Keccak256hasher, Transcript}; @@ -83,13 +83,11 @@ fn test_orion_commit_simd_field_consistency() { }); } -fn test_orion_pcs_simd_full_e2e_generics<F, SimdF, EvalF, SimdEvalF, ComPackF, OpenPackF>( - num_vars: usize, -) where +fn test_orion_pcs_simd_full_e2e_generics<F, SimdF, EvalF, ComPackF, OpenPackF>(num_vars: usize) +where F: Field, SimdF: SimdField<Scalar = F>, EvalF: ExtensionField<BaseField = F>, - SimdEvalF: SimdField<Scalar = EvalF>, ComPackF: SimdField<Scalar = F>, OpenPackF: SimdField<Scalar = F>, { @@ -124,7 +122,7 @@ fn test_orion_pcs_simd_full_e2e_generics<F, SimdF, EvalF, SimdEvalF, ComPackF, O let commitment = orion_commit_simd_field(&srs, &random_poly, &mut scratch_pad).unwrap(); - let opening = orion_open_simd_field::<F, SimdF, EvalF, SimdEvalF, ComPackF, OpenPackF, _>( + let opening = orion_open_simd_field::<F, SimdF, EvalF, ComPackF, OpenPackF, _>( &srs, &random_poly, &random_point, @@ -136,7 +134,6 @@ fn test_orion_pcs_simd_full_e2e_generics<F, SimdF, EvalF, SimdEvalF, ComPackF, O F, SimdF, EvalF, - SimdEvalF, ComPackF, OpenPackF, _, @@ -153,11 +150,7 @@ fn test_orion_pcs_simd_full_e2e_generics<F, SimdF, EvalF, SimdEvalF, ComPackF, O #[test] fn test_orion_pcs_simd_full_e2e() { (16..=22).for_each(|num_vars| { - test_orion_pcs_simd_full_e2e_generics::<GF2, GF2x8, GF2_128, GF2_128x8, GF2x64, GF2x8>( - num_vars, - ); - test_orion_pcs_simd_full_e2e_generics::<GF2, GF2x8, GF2_128, GF2_128x8, GF2x128, GF2x8>( - num_vars, - ); + test_orion_pcs_simd_full_e2e_generics::<GF2, GF2x8, GF2_128, GF2x64, GF2x8>(num_vars); + test_orion_pcs_simd_full_e2e_generics::<GF2, GF2x8, GF2_128, GF2x128, GF2x8>(num_vars); }) } diff --git a/poly_commit/src/orion/utils.rs b/poly_commit/src/orion/utils.rs index 991341a6..03fc3288 100644 --- a/poly_commit/src/orion/utils.rs +++ b/poly_commit/src/orion/utils.rs @@ -277,29 +277,4 @@ impl<F: Field> SubsetSumLUTs<F> { .map(|(t_i, index)| t_i[index.as_u32_unchecked() as usize]) .sum() } - - #[inline] - pub fn lookup_and_sum_simd<BitF, EntryF, SimdF>(&self, simd_indices: &[EntryF]) -> SimdF - where - BitF: Field, - EntryF: SimdField<Scalar = BitF>, - SimdF: SimdField<Scalar = F>, - { - assert_eq!(EntryF::FIELD_SIZE, 1); - assert_eq!(EntryF::PACK_SIZE, self.entry_bits); - assert_eq!(simd_indices.len(), self.tables.len() * SimdF::PACK_SIZE); - - let mut elts = vec![F::ZERO; SimdF::PACK_SIZE]; - - self.tables - .iter() - .zip(simd_indices.chunks(SimdF::PACK_SIZE)) - .for_each(|(t_i, indices)| { - elts.iter_mut() - .zip(indices.iter()) - .for_each(|(elem, index)| *elem += t_i[index.as_u32_unchecked() as usize]) - }); - - SimdF::pack(&elts) - } } From 441482bef4da384e10786f2f2800e641e0f13c5b Mon Sep 17 00:00:00 2001 From: Hang Su <tonyfloater@gmail.com> Date: Wed, 27 Nov 2024 04:18:26 -0500 Subject: [PATCH 20/65] minor, fixing up a unattended impl for standard pcs for simd orion --- poly_commit/src/orion/expander_integration.rs | 25 ++++++++++++------- poly_commit/src/orion/simd_field_tests.rs | 25 ++++++++----------- 2 files changed, 26 insertions(+), 24 deletions(-) diff --git a/poly_commit/src/orion/expander_integration.rs b/poly_commit/src/orion/expander_integration.rs index 7573ed36..87e099c0 100644 --- a/poly_commit/src/orion/expander_integration.rs +++ b/poly_commit/src/orion/expander_integration.rs @@ -5,7 +5,10 @@ use gkr_field_config::GKRFieldConfig; use polynomials::MultiLinearPoly; use transcript::Transcript; -use crate::{orion::*, PCSForExpanderGKR, PolynomialCommitmentScheme, StructuredReferenceString}; +use crate::{ + orion::*, traits::TensorCodeIOPPCS, PCSForExpanderGKR, PolynomialCommitmentScheme, + StructuredReferenceString, +}; impl StructuredReferenceString for OrionSRS { type PKey = OrionSRS; @@ -180,14 +183,18 @@ where scratch_pad, ); - let poly_ext_coeffs: Vec<_> = poly - .coeffs - .iter() - .flat_map(|p| -> Vec<_> { p.unpack().iter().map(|t| EvalF::from(*t)).collect() }) - .collect(); - - let mut scratch = vec![EvalF::ZERO; 1 << poly.get_num_vars()]; - let eval = MultiLinearPoly::evaluate_with_buffer(&poly_ext_coeffs, x, &mut scratch); + let real_num_vars = poly.get_num_vars() + SimdF::PACK_SIZE.ilog2() as usize; + let num_vars_in_msg = { + let (_, m) = <Self::SRS as TensorCodeIOPPCS>::evals_shape::<F>(real_num_vars); + m + SimdF::PACK_SIZE.ilog2() as usize + }; + + let mut scratch = vec![EvalF::ZERO; 1 << num_vars_in_msg]; + let eval = MultiLinearPoly::evaluate_with_buffer( + &opening.eval_row, + &x[..num_vars_in_msg], + &mut scratch, + ); drop(scratch); (eval, opening) diff --git a/poly_commit/src/orion/simd_field_tests.rs b/poly_commit/src/orion/simd_field_tests.rs index 115af52c..0fd772bf 100644 --- a/poly_commit/src/orion/simd_field_tests.rs +++ b/poly_commit/src/orion/simd_field_tests.rs @@ -130,21 +130,16 @@ where &scratch_pad, ); - assert!(orion_verify_simd_field::< - F, - SimdF, - EvalF, - ComPackF, - OpenPackF, - _, - >( - &srs, - &commitment, - &random_point, - expected_eval, - &mut transcript_cloned, - &opening - )); + assert!( + orion_verify_simd_field::<F, SimdF, _, ComPackF, OpenPackF, _>( + &srs, + &commitment, + &random_point, + expected_eval, + &mut transcript_cloned, + &opening + ) + ); } #[test] From 73e8baa601decbb57d9e664215791f3495cf8a05 Mon Sep 17 00:00:00 2001 From: Hang Su <tonyfloater@gmail.com> Date: Wed, 27 Nov 2024 19:28:44 -0500 Subject: [PATCH 21/65] minor, one liner change --- poly_commit/benches/orion.rs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/poly_commit/benches/orion.rs b/poly_commit/benches/orion.rs index 0e9fa0bd..de3269f4 100644 --- a/poly_commit/benches/orion.rs +++ b/poly_commit/benches/orion.rs @@ -207,16 +207,15 @@ fn simd_field_opening_benchmark_helper<F, SimdF, EvalF, ComPackF, OpenPackF, T>( ), |b| { b.iter(|| { - _ = black_box(orion_open_simd_field::< - F, - SimdF, - EvalF, - ComPackF, - OpenPackF, - T, - >( - &srs, &poly, &eval_point, &mut transcript, &scratch_pad - )) + _ = black_box( + orion_open_simd_field::<F, SimdF, _, ComPackF, OpenPackF, T>( + &srs, + &poly, + &eval_point, + &mut transcript, + &scratch_pad, + ), + ) }) }, ) From a74cf763a3a26a807a16da1168fcc9db51a5b2f6 Mon Sep 17 00:00:00 2001 From: Hang Su <tonyfloater@gmail.com> Date: Thu, 28 Nov 2024 04:30:39 -0500 Subject: [PATCH 22/65] working on mpi versioned commit/open/verify algorithm --- config/mpi_config/src/lib.rs | 12 +-- poly_commit/src/orion/expander_integration.rs | 91 +++++++++++++------ poly_commit/src/orion/utils.rs | 7 +- poly_commit/src/traits.rs | 9 ++ 4 files changed, 81 insertions(+), 38 deletions(-) diff --git a/config/mpi_config/src/lib.rs b/config/mpi_config/src/lib.rs index 2fae9994..02716ba7 100644 --- a/config/mpi_config/src/lib.rs +++ b/config/mpi_config/src/lib.rs @@ -134,16 +134,16 @@ impl MPIConfig { /// Return an u8 vector sharing THE SAME MEMORY SLOT with the input. #[inline] - unsafe fn vec_to_u8_bytes<F: Field>(vec: &Vec<F>) -> Vec<u8> { + unsafe fn vec_to_u8_bytes<F: Sized>(vec: &Vec<F>) -> Vec<u8> { Vec::<u8>::from_raw_parts( vec.as_ptr() as *mut u8, - vec.len() * F::SIZE, - vec.capacity() * F::SIZE, + vec.len() * size_of::<F>(), + vec.capacity() * size_of::<F>(), ) } #[allow(clippy::collapsible_else_if)] - pub fn gather_vec<F: Field>(&self, local_vec: &Vec<F>, global_vec: &mut Vec<F>) { + pub fn gather_vec<F: Sized + Clone>(&self, local_vec: &Vec<F>, global_vec: &mut Vec<F>) { unsafe { if self.world_size == 1 { *global_vec = local_vec.clone() @@ -202,11 +202,11 @@ impl MPIConfig { /// Root process broadcase a value f into all the processes #[inline] - pub fn root_broadcast_f<F: Field>(&self, f: &mut F) { + pub fn root_broadcast_f<F: Sized + Clone>(&self, f: &mut F) { unsafe { if self.world_size == 1 { } else { - let mut vec_u8 = Self::elem_to_u8_bytes(f, F::SIZE); + let mut vec_u8 = Self::elem_to_u8_bytes(f, size_of::<F>()); self.root_process().broadcast_into(&mut vec_u8); vec_u8.leak(); } diff --git a/poly_commit/src/orion/expander_integration.rs b/poly_commit/src/orion/expander_integration.rs index 87e099c0..b0c3a87d 100644 --- a/poly_commit/src/orion/expander_integration.rs +++ b/poly_commit/src/orion/expander_integration.rs @@ -2,17 +2,17 @@ use std::marker::PhantomData; use arith::{ExtensionField, Field, SimdField}; use gkr_field_config::GKRFieldConfig; +use mpi_config::MPIConfig; use polynomials::MultiLinearPoly; use transcript::Transcript; use crate::{ - orion::*, traits::TensorCodeIOPPCS, PCSForExpanderGKR, PolynomialCommitmentScheme, - StructuredReferenceString, + orion::*, traits::TensorCodeIOPPCS, ExpanderGKRChallenge, PCSForExpanderGKR, + PolynomialCommitmentScheme, StructuredReferenceString, }; impl StructuredReferenceString for OrionSRS { type PKey = OrionSRS; - type VKey = OrionSRS; fn into_keys(self) -> (Self::PKey, Self::VKey) { @@ -232,9 +232,9 @@ impl<C, ComPackF, OpenPackF, T> PCSForExpanderGKR<C, T> > where C: GKRFieldConfig, - T: Transcript<C::ChallengeField>, ComPackF: SimdField<Scalar = C::CircuitField>, OpenPackF: SimdField<Scalar = C::CircuitField>, + T: Transcript<C::ChallengeField>, { const NAME: &'static str = "OrionSIMDPCSForExpanderGKR"; @@ -253,55 +253,94 @@ where #[allow(unused)] fn gen_srs_for_testing( params: &Self::Params, - mpi_config: &mpi_config::MPIConfig, + mpi_config: &MPIConfig, rng: impl rand::RngCore, ) -> Self::SRS { todo!() } - #[allow(unused)] - fn init_scratch_pad( - params: &Self::Params, - mpi_config: &mpi_config::MPIConfig, - ) -> Self::ScratchPad { - todo!() + fn init_scratch_pad(_params: &Self::Params, _mpi_config: &MPIConfig) -> Self::ScratchPad { + Self::ScratchPad::default() } - #[allow(unused)] fn commit( - params: &Self::Params, - mpi_config: &mpi_config::MPIConfig, + _params: &Self::Params, + mpi_config: &MPIConfig, proving_key: &<Self::SRS as StructuredReferenceString>::PKey, - poly: &MultiLinearPoly<<C as GKRFieldConfig>::SimdCircuitField>, + poly: &MultiLinearPoly<C::SimdCircuitField>, scratch_pad: &mut Self::ScratchPad, ) -> Self::Commitment { - todo!() + let commitment = orion_commit_simd_field(proving_key, poly, scratch_pad).unwrap(); + if mpi_config.world_size == 1 { + return commitment; + } + + let local_buffer = vec![commitment.clone()]; + let mut buffer = match mpi_config.is_root() { + true => vec![Self::Commitment::default(); mpi_config.world_size()], + _ => Vec::new(), + }; + mpi_config.gather_vec(&local_buffer, &mut buffer); + + let mut root = Self::Commitment::default(); + if mpi_config.is_root() { + let final_tree_height = 1 + buffer.len().ilog2() as u32; + let (internals, _) = tree::Tree::new_with_leaf_nodes(buffer.clone(), final_tree_height); + root = internals[0]; + } + mpi_config.root_broadcast_f(&mut root); + root } - #[allow(unused)] fn open( - params: &Self::Params, - mpi_config: &mpi_config::MPIConfig, + _params: &Self::Params, + mpi_config: &MPIConfig, proving_key: &<Self::SRS as StructuredReferenceString>::PKey, - poly: &MultiLinearPoly<<C as GKRFieldConfig>::SimdCircuitField>, - x: &crate::ExpanderGKRChallenge<C>, + poly: &MultiLinearPoly<C::SimdCircuitField>, + eval_point: &ExpanderGKRChallenge<C>, transcript: &mut T, // add transcript here to allow interactive arguments scratch_pad: &mut Self::ScratchPad, ) -> Self::Opening { + let local_xs = eval_point.local_xs(); + let local_opening = orion_open_simd_field::< + C::CircuitField, + C::SimdCircuitField, + C::ChallengeField, + ComPackF, + OpenPackF, + T, + >(proving_key, poly, &local_xs, transcript, scratch_pad); + if mpi_config.world_size == 1 { + return local_opening; + } + + // TODO ... is x_mpi right of (earlier evaluated than) x_simd and x? + todo!() } - #[allow(unused)] fn verify( - params: &Self::Params, - mpi_config: &mpi_config::MPIConfig, + _params: &Self::Params, + mpi_config: &MPIConfig, verifying_key: &<Self::SRS as StructuredReferenceString>::VKey, commitment: &Self::Commitment, - x: &crate::ExpanderGKRChallenge<C>, - v: <C as GKRFieldConfig>::ChallengeField, + eval_point: &ExpanderGKRChallenge<C>, + v: C::ChallengeField, transcript: &mut T, // add transcript here to allow interactive arguments opening: &Self::Opening, ) -> bool { + let local_xs = eval_point.local_xs(); + if mpi_config.world_size == 1 { + return orion_verify_simd_field::< + C::CircuitField, + C::SimdCircuitField, + C::ChallengeField, + ComPackF, + OpenPackF, + T, + >(verifying_key, commitment, &local_xs, v, transcript, opening); + } + todo!() } } diff --git a/poly_commit/src/orion/utils.rs b/poly_commit/src/orion/utils.rs index 03fc3288..11e15a89 100644 --- a/poly_commit/src/orion/utils.rs +++ b/poly_commit/src/orion/utils.rs @@ -85,12 +85,7 @@ where pub _phantom: PhantomData<ComPackF>, } -unsafe impl<F, ComPackF> Send for OrionScratchPad<F, ComPackF> -where - F: Field, - ComPackF: SimdField<Scalar = F>, -{ -} +unsafe impl<F: Field, ComPackF: SimdField<Scalar = F>> Send for OrionScratchPad<F, ComPackF> {} #[derive(Clone, Debug, Default)] pub struct OrionProof<EvalF: Field> { diff --git a/poly_commit/src/traits.rs b/poly_commit/src/traits.rs index 2016fa8c..73668ed8 100644 --- a/poly_commit/src/traits.rs +++ b/poly_commit/src/traits.rs @@ -71,6 +71,15 @@ pub struct ExpanderGKRChallenge<C: GKRFieldConfig> { pub x_mpi: Vec<C::ChallengeField>, } +impl<C: GKRFieldConfig> ExpanderGKRChallenge<C> { + pub fn local_xs(&self) -> Vec<C::ChallengeField> { + let mut local_xs = Vec::with_capacity(self.x_simd.len() + self.x.len()); + local_xs[..self.x_simd.len()].copy_from_slice(&self.x_simd); + local_xs[self.x_simd.len()..].copy_from_slice(&self.x); + local_xs + } +} + pub trait PCSForExpanderGKR<C: GKRFieldConfig, T: Transcript<C::ChallengeField>> { const NAME: &'static str; From 3d9006e64a80bf52bd2135225e084b2e2f05cd62 Mon Sep 17 00:00:00 2001 From: Hang Su <tonyfloater@gmail.com> Date: Thu, 28 Nov 2024 05:56:18 -0500 Subject: [PATCH 23/65] change base pcs impl testing to test_pcs from common --- poly_commit/src/orion/base_field_tests.rs | 58 +----------- poly_commit/src/orion/expander_integration.rs | 6 +- poly_commit/src/orion/simd_field_tests.rs | 71 +------------- poly_commit/tests/common.rs | 3 +- poly_commit/tests/test_orion.rs | 92 +++++++++++++++++++ 5 files changed, 100 insertions(+), 130 deletions(-) create mode 100644 poly_commit/tests/test_orion.rs diff --git a/poly_commit/src/orion/base_field_tests.rs b/poly_commit/src/orion/base_field_tests.rs index 3d9e8a00..5147f492 100644 --- a/poly_commit/src/orion/base_field_tests.rs +++ b/poly_commit/src/orion/base_field_tests.rs @@ -1,9 +1,7 @@ -use arith::{ExtensionField, Field, SimdField}; +use arith::{Field, SimdField}; use ark_std::test_rng; -use gf2::{GF2x128, GF2x64, GF2x8, GF2}; -use gf2_128::GF2_128; +use gf2::{GF2x128, GF2x64, GF2}; use polynomials::MultiLinearPoly; -use transcript::{BytesHashTranscript, Keccak256hasher, Transcript}; use crate::{ orion::{base_field_impl::*, utils::*}, @@ -66,55 +64,3 @@ fn test_orion_commit_base_field_consistency() { test_orion_commit_base_field_consistency_generic::<GF2, GF2x128>(num_vars); }); } - -fn test_orion_pcs_base_full_e2e_generics<F, EvalF, ComPackF, OpenPackF>(num_vars: usize) -where - F: Field, - EvalF: ExtensionField<BaseField = F>, - ComPackF: SimdField<Scalar = F>, - OpenPackF: SimdField<Scalar = F>, -{ - let mut rng = test_rng(); - - let poly = MultiLinearPoly::<F>::random(num_vars, &mut rng); - let poly_ext_coeffs: Vec<_> = poly.coeffs.iter().map(|t| EvalF::from(*t)).collect(); - let random_point: Vec<_> = (0..num_vars) - .map(|_| EvalF::random_unsafe(&mut rng)) - .collect(); - let mut scratch = vec![EvalF::ZERO; 1 << num_vars]; - let expected_eval = - MultiLinearPoly::evaluate_with_buffer(&poly_ext_coeffs, &random_point, &mut scratch); - drop(scratch); - - let mut transcript: BytesHashTranscript<EvalF, Keccak256hasher> = BytesHashTranscript::new(); - let mut transcript_cloned = transcript.clone(); - - let srs = OrionSRS::from_random::<F>(num_vars, ORION_CODE_PARAMETER_INSTANCE, &mut rng); - let mut scratch_pad = OrionScratchPad::<F, ComPackF>::default(); - let commitment = orion_commit_base_field(&srs, &poly, &mut scratch_pad).unwrap(); - - let (_, opening) = orion_open_base_field::<F, EvalF, ComPackF, OpenPackF, _>( - &srs, - &poly, - &random_point, - &mut transcript, - &scratch_pad, - ); - - assert!(orion_verify_base_field::<F, EvalF, ComPackF, OpenPackF, _>( - &srs, - &commitment, - &random_point, - expected_eval, - &mut transcript_cloned, - &opening, - )); -} - -#[test] -fn test_orion_pcs_base_full_e2e() { - (19..=25).for_each(|num_vars| { - test_orion_pcs_base_full_e2e_generics::<GF2, GF2_128, GF2x64, GF2x8>(num_vars); - test_orion_pcs_base_full_e2e_generics::<GF2, GF2_128, GF2x128, GF2x8>(num_vars); - }); -} diff --git a/poly_commit/src/orion/expander_integration.rs b/poly_commit/src/orion/expander_integration.rs index b0c3a87d..17f9d70a 100644 --- a/poly_commit/src/orion/expander_integration.rs +++ b/poly_commit/src/orion/expander_integration.rs @@ -186,7 +186,7 @@ where let real_num_vars = poly.get_num_vars() + SimdF::PACK_SIZE.ilog2() as usize; let num_vars_in_msg = { let (_, m) = <Self::SRS as TensorCodeIOPPCS>::evals_shape::<F>(real_num_vars); - m + SimdF::PACK_SIZE.ilog2() as usize + (m.ilog2() + SimdF::PACK_SIZE.ilog2()) as usize }; let mut scratch = vec![EvalF::ZERO; 1 << num_vars_in_msg]; @@ -275,7 +275,7 @@ where return commitment; } - let local_buffer = vec![commitment.clone()]; + let local_buffer = vec![commitment]; let mut buffer = match mpi_config.is_root() { true => vec![Self::Commitment::default(); mpi_config.world_size()], _ => Vec::new(), @@ -284,7 +284,7 @@ where let mut root = Self::Commitment::default(); if mpi_config.is_root() { - let final_tree_height = 1 + buffer.len().ilog2() as u32; + let final_tree_height = 1 + buffer.len().ilog2(); let (internals, _) = tree::Tree::new_with_leaf_nodes(buffer.clone(), final_tree_height); root = internals[0]; } diff --git a/poly_commit/src/orion/simd_field_tests.rs b/poly_commit/src/orion/simd_field_tests.rs index 0fd772bf..c7c02a65 100644 --- a/poly_commit/src/orion/simd_field_tests.rs +++ b/poly_commit/src/orion/simd_field_tests.rs @@ -1,9 +1,7 @@ -use arith::{ExtensionField, Field, SimdField}; +use arith::{Field, SimdField}; use ark_std::test_rng; use gf2::{GF2x128, GF2x64, GF2x8, GF2}; -use gf2_128::GF2_128; use polynomials::MultiLinearPoly; -use transcript::{BytesHashTranscript, Keccak256hasher, Transcript}; use crate::{ orion::{simd_field_impl::*, utils::*}, @@ -82,70 +80,3 @@ fn test_orion_commit_simd_field_consistency() { test_orion_commit_simd_field_consistency_generic::<GF2, GF2x8, GF2x128>(num_vars); }); } - -fn test_orion_pcs_simd_full_e2e_generics<F, SimdF, EvalF, ComPackF, OpenPackF>(num_vars: usize) -where - F: Field, - SimdF: SimdField<Scalar = F>, - EvalF: ExtensionField<BaseField = F>, - ComPackF: SimdField<Scalar = F>, - OpenPackF: SimdField<Scalar = F>, -{ - let mut rng = test_rng(); - - let random_poly = MultiLinearPoly::<SimdF>::random(num_vars, &mut rng); - let random_poly_unpacked = MultiLinearPoly::<EvalF>::new( - random_poly - .coeffs - .iter() - .flat_map(|p| -> Vec<_> { p.unpack().iter().map(|t| EvalF::from(*t)).collect() }) - .collect(), - ); - let real_num_vars = num_vars + SimdF::PACK_SIZE.ilog2() as usize; - let random_point: Vec<_> = (0..real_num_vars) - .map(|_| EvalF::random_unsafe(&mut rng)) - .collect(); - - let mut scratch = vec![EvalF::ZERO; random_poly_unpacked.coeffs.len()]; - let expected_eval = MultiLinearPoly::evaluate_with_buffer( - &random_poly_unpacked.coeffs, - &random_point, - &mut scratch, - ); - drop(scratch); - - let srs = - OrionSRS::from_random::<SimdF>(real_num_vars, ORION_CODE_PARAMETER_INSTANCE, &mut rng); - let mut scratch_pad = OrionScratchPad::<F, ComPackF>::default(); - let mut transcript: BytesHashTranscript<EvalF, Keccak256hasher> = BytesHashTranscript::new(); - let mut transcript_cloned = transcript.clone(); - - let commitment = orion_commit_simd_field(&srs, &random_poly, &mut scratch_pad).unwrap(); - - let opening = orion_open_simd_field::<F, SimdF, EvalF, ComPackF, OpenPackF, _>( - &srs, - &random_poly, - &random_point, - &mut transcript, - &scratch_pad, - ); - - assert!( - orion_verify_simd_field::<F, SimdF, _, ComPackF, OpenPackF, _>( - &srs, - &commitment, - &random_point, - expected_eval, - &mut transcript_cloned, - &opening - ) - ); -} - -#[test] -fn test_orion_pcs_simd_full_e2e() { - (16..=22).for_each(|num_vars| { - test_orion_pcs_simd_full_e2e_generics::<GF2, GF2x8, GF2_128, GF2x64, GF2x8>(num_vars); - test_orion_pcs_simd_full_e2e_generics::<GF2, GF2x8, GF2_128, GF2x128, GF2x8>(num_vars); - }) -} diff --git a/poly_commit/tests/common.rs b/poly_commit/tests/common.rs index 85a2ac32..3ebf4083 100644 --- a/poly_commit/tests/common.rs +++ b/poly_commit/tests/common.rs @@ -23,6 +23,7 @@ pub fn test_pcs<F: Field, T: Transcript<F>, P: PolynomialCommitmentScheme<F, T>> let commitment = P::commit(params, &proving_key, poly, &mut scratch_pad); for x in xs { + let mut transcript_cloned = transcript.clone(); let (v, opening) = P::open( params, &proving_key, @@ -38,7 +39,7 @@ pub fn test_pcs<F: Field, T: Transcript<F>, P: PolynomialCommitmentScheme<F, T>> x, v, &opening, - &mut transcript + &mut transcript_cloned )); } } diff --git a/poly_commit/tests/test_orion.rs b/poly_commit/tests/test_orion.rs new file mode 100644 index 00000000..4e3d3cd4 --- /dev/null +++ b/poly_commit/tests/test_orion.rs @@ -0,0 +1,92 @@ +mod common; + +use arith::{ExtensionField, Field, SimdField}; +use ark_std::test_rng; +use gf2::{GF2x128, GF2x64, GF2x8, GF2}; +use gf2_128::GF2_128; +use poly_commit::*; +use polynomials::MultiLinearPoly; +use transcript::{BytesHashTranscript, Keccak256hasher}; + +fn test_orion_base_field_pcs_generics<F, EvalF, ComPackF, OpenPackF>() +where + F: Field, + EvalF: ExtensionField<BaseField = F>, + ComPackF: SimdField<Scalar = F>, + OpenPackF: SimdField<Scalar = F>, +{ + let mut rng = test_rng(); + let test_repetition = 3; + + (19..=25).for_each(|num_vars| { + let xs: Vec<_> = (0..test_repetition) + .map(|_| -> Vec<EvalF> { + (0..num_vars) + .map(|_| EvalF::random_unsafe(&mut rng)) + .collect() + }) + .collect(); + let poly = MultiLinearPoly::<F>::random(num_vars, &mut rng); + + common::test_pcs::< + EvalF, + BytesHashTranscript<EvalF, Keccak256hasher>, + OrionBaseFieldPCS< + F, + EvalF, + ComPackF, + OpenPackF, + BytesHashTranscript<EvalF, Keccak256hasher>, + >, + >(&num_vars, &poly, &xs); + }) +} + +#[test] +fn test_orion_base_field_pcs_full_e2e() { + test_orion_base_field_pcs_generics::<GF2, GF2_128, GF2x64, GF2x8>(); + test_orion_base_field_pcs_generics::<GF2, GF2_128, GF2x128, GF2x8>() +} + +fn test_orion_simd_field_pcs_generics<F, SimdF, EvalF, ComPackF, OpenPackF>() +where + F: Field, + SimdF: SimdField<Scalar = F>, + EvalF: ExtensionField<BaseField = F>, + ComPackF: SimdField<Scalar = F>, + OpenPackF: SimdField<Scalar = F>, +{ + let mut rng = test_rng(); + let test_repetition = 3; + + (16..=22).for_each(|num_vars| { + let real_num_vars = num_vars + SimdF::PACK_SIZE.ilog2() as usize; + let xs: Vec<_> = (0..test_repetition) + .map(|_| -> Vec<EvalF> { + (0..real_num_vars) + .map(|_| EvalF::random_unsafe(&mut rng)) + .collect() + }) + .collect(); + let poly = MultiLinearPoly::<SimdF>::random(num_vars, &mut rng); + + common::test_pcs::< + EvalF, + BytesHashTranscript<EvalF, Keccak256hasher>, + OrionSIMDFieldPCS< + F, + SimdF, + EvalF, + ComPackF, + OpenPackF, + BytesHashTranscript<EvalF, Keccak256hasher>, + >, + >(&real_num_vars, &poly, &xs); + }) +} + +#[test] +fn test_orion_simd_field_pcs_full_e2e() { + test_orion_simd_field_pcs_generics::<GF2, GF2x8, GF2_128, GF2x64, GF2x8>(); + test_orion_simd_field_pcs_generics::<GF2, GF2x8, GF2_128, GF2x128, GF2x8>(); +} From f67463d7b28ae085ae555bd68130801d6c7dcbec Mon Sep 17 00:00:00 2001 From: Hang Su <tonyfloater@gmail.com> Date: Thu, 28 Nov 2024 06:24:15 -0500 Subject: [PATCH 24/65] tame the CI --- poly_commit/tests/common.rs | 1 + poly_commit/tests/test_orion.rs | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/poly_commit/tests/common.rs b/poly_commit/tests/common.rs index 3ebf4083..852617c2 100644 --- a/poly_commit/tests/common.rs +++ b/poly_commit/tests/common.rs @@ -44,6 +44,7 @@ pub fn test_pcs<F: Field, T: Transcript<F>, P: PolynomialCommitmentScheme<F, T>> } } +#[allow(unused)] pub fn test_gkr_pcs< C: GKRFieldConfig, T: Transcript<C::ChallengeField>, diff --git a/poly_commit/tests/test_orion.rs b/poly_commit/tests/test_orion.rs index 4e3d3cd4..ba44a104 100644 --- a/poly_commit/tests/test_orion.rs +++ b/poly_commit/tests/test_orion.rs @@ -8,6 +8,8 @@ use poly_commit::*; use polynomials::MultiLinearPoly; use transcript::{BytesHashTranscript, Keccak256hasher}; +const TEST_REPETITION: usize = 3; + fn test_orion_base_field_pcs_generics<F, EvalF, ComPackF, OpenPackF>() where F: Field, @@ -16,10 +18,9 @@ where OpenPackF: SimdField<Scalar = F>, { let mut rng = test_rng(); - let test_repetition = 3; (19..=25).for_each(|num_vars| { - let xs: Vec<_> = (0..test_repetition) + let xs: Vec<_> = (0..TEST_REPETITION) .map(|_| -> Vec<EvalF> { (0..num_vars) .map(|_| EvalF::random_unsafe(&mut rng)) @@ -57,11 +58,10 @@ where OpenPackF: SimdField<Scalar = F>, { let mut rng = test_rng(); - let test_repetition = 3; (16..=22).for_each(|num_vars| { let real_num_vars = num_vars + SimdF::PACK_SIZE.ilog2() as usize; - let xs: Vec<_> = (0..test_repetition) + let xs: Vec<_> = (0..TEST_REPETITION) .map(|_| -> Vec<EvalF> { (0..real_num_vars) .map(|_| EvalF::random_unsafe(&mut rng)) From 0e85b7a0c743acdad45b8c049ceedd673bd9155e Mon Sep 17 00:00:00 2001 From: Hang Su <tonyfloater@gmail.com> Date: Thu, 28 Nov 2024 19:32:21 -0500 Subject: [PATCH 25/65] testing harness minor changes --- poly_commit/tests/common.rs | 3 ++- poly_commit/tests/test_orion.rs | 10 +++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/poly_commit/tests/common.rs b/poly_commit/tests/common.rs index 852617c2..cf107ed0 100644 --- a/poly_commit/tests/common.rs +++ b/poly_commit/tests/common.rs @@ -75,6 +75,7 @@ pub fn test_gkr_pcs< for xx in xs { let ExpanderGKRChallenge { x, x_simd, x_mpi } = xx; + let mut transcript_cloned = transcript.clone(); transcript.lock_proof(); let opening = P::open( @@ -100,7 +101,7 @@ pub fn test_gkr_pcs< &commitment, xx, v, - transcript, + &mut transcript_cloned, &opening )); transcript.unlock_proof(); diff --git a/poly_commit/tests/test_orion.rs b/poly_commit/tests/test_orion.rs index ba44a104..a8f71b30 100644 --- a/poly_commit/tests/test_orion.rs +++ b/poly_commit/tests/test_orion.rs @@ -59,16 +59,16 @@ where { let mut rng = test_rng(); - (16..=22).for_each(|num_vars| { - let real_num_vars = num_vars + SimdF::PACK_SIZE.ilog2() as usize; + (19..=25).for_each(|num_vars| { + let poly_num_vars = num_vars - SimdF::PACK_SIZE.ilog2() as usize; let xs: Vec<_> = (0..TEST_REPETITION) .map(|_| -> Vec<EvalF> { - (0..real_num_vars) + (0..num_vars) .map(|_| EvalF::random_unsafe(&mut rng)) .collect() }) .collect(); - let poly = MultiLinearPoly::<SimdF>::random(num_vars, &mut rng); + let poly = MultiLinearPoly::<SimdF>::random(poly_num_vars, &mut rng); common::test_pcs::< EvalF, @@ -81,7 +81,7 @@ where OpenPackF, BytesHashTranscript<EvalF, Keccak256hasher>, >, - >(&real_num_vars, &poly, &xs); + >(&num_vars, &poly, &xs); }) } From 1673d3b7514850b325927f372d97d948b2b27f55 Mon Sep 17 00:00:00 2001 From: Hang Su <tonyfloater@gmail.com> Date: Fri, 29 Nov 2024 03:45:30 -0500 Subject: [PATCH 26/65] minor, renamings and using params in standard pcs trait --- poly_commit/src/orion/expander_integration.rs | 31 ++++++++++++++----- poly_commit/src/orion/serde.rs | 4 +-- poly_commit/src/orion/utils.rs | 10 +++--- 3 files changed, 31 insertions(+), 14 deletions(-) diff --git a/poly_commit/src/orion/expander_integration.rs b/poly_commit/src/orion/expander_integration.rs index 17f9d70a..d0f3302a 100644 --- a/poly_commit/src/orion/expander_integration.rs +++ b/poly_commit/src/orion/expander_integration.rs @@ -64,22 +64,24 @@ where } fn commit( - _params: &Self::Params, + params: &Self::Params, proving_key: &<Self::SRS as StructuredReferenceString>::PKey, poly: &Self::Poly, scratch_pad: &mut Self::ScratchPad, ) -> Self::Commitment { + assert_eq!(*params, proving_key.num_vars); orion_commit_base_field(proving_key, poly, scratch_pad).unwrap() } fn open( - _params: &Self::Params, + params: &Self::Params, proving_key: &<Self::SRS as StructuredReferenceString>::PKey, poly: &Self::Poly, x: &Self::EvalPoint, scratch_pad: &mut Self::ScratchPad, transcript: &mut T, ) -> (EvalF, Self::Opening) { + assert_eq!(*params, proving_key.num_vars); orion_open_base_field::<F, EvalF, ComPackF, OpenPackF, T>( proving_key, poly, @@ -90,7 +92,7 @@ where } fn verify( - _params: &Self::Params, + params: &Self::Params, verifying_key: &<Self::SRS as StructuredReferenceString>::VKey, commitment: &Self::Commitment, x: &Self::EvalPoint, @@ -98,6 +100,7 @@ where opening: &Self::Opening, transcript: &mut T, ) -> bool { + assert_eq!(*params, verifying_key.num_vars); orion_verify_base_field::<F, EvalF, ComPackF, OpenPackF, T>( verifying_key, commitment, @@ -159,22 +162,32 @@ where } fn commit( - _params: &Self::Params, + params: &Self::Params, proving_key: &<Self::SRS as StructuredReferenceString>::PKey, poly: &Self::Poly, scratch_pad: &mut Self::ScratchPad, ) -> Self::Commitment { + assert_eq!(*params, proving_key.num_vars); + assert_eq!( + poly.get_num_vars(), + proving_key.num_vars - SimdF::PACK_SIZE.ilog2() as usize + ); orion_commit_simd_field(proving_key, poly, scratch_pad).unwrap() } fn open( - _params: &Self::Params, + params: &Self::Params, proving_key: &<Self::SRS as StructuredReferenceString>::PKey, poly: &Self::Poly, x: &Self::EvalPoint, scratch_pad: &mut Self::ScratchPad, transcript: &mut T, ) -> (EvalF, Self::Opening) { + assert_eq!(*params, proving_key.num_vars); + assert_eq!( + poly.get_num_vars(), + proving_key.num_vars - SimdF::PACK_SIZE.ilog2() as usize + ); let opening = orion_open_simd_field::<F, SimdF, EvalF, ComPackF, OpenPackF, T>( proving_key, poly, @@ -201,7 +214,7 @@ where } fn verify( - _params: &Self::Params, + params: &Self::Params, verifying_key: &<Self::SRS as StructuredReferenceString>::VKey, commitment: &Self::Commitment, x: &Self::EvalPoint, @@ -209,6 +222,8 @@ where opening: &Self::Opening, transcript: &mut T, ) -> bool { + assert_eq!(*params, verifying_key.num_vars); + assert_eq!(x.len(), verifying_key.num_vars); orion_verify_simd_field::<F, SimdF, EvalF, ComPackF, OpenPackF, T>( verifying_key, commitment, @@ -220,7 +235,6 @@ where } } -// TODO ... impl<C, ComPackF, OpenPackF, T> PCSForExpanderGKR<C, T> for OrionSIMDFieldPCS< C::CircuitField, @@ -315,6 +329,7 @@ where } // TODO ... is x_mpi right of (earlier evaluated than) x_simd and x? + // seems so. todo!() } @@ -341,6 +356,8 @@ where >(verifying_key, commitment, &local_xs, v, transcript, opening); } + // TODO ... decide open and verify in distributed settings + todo!() } } diff --git a/poly_commit/src/orion/serde.rs b/poly_commit/src/orion/serde.rs index 46f498a2..b3630ce0 100644 --- a/poly_commit/src/orion/serde.rs +++ b/poly_commit/src/orion/serde.rs @@ -86,7 +86,7 @@ impl FieldSerde for OrionSRS { const SERIALIZED_SIZE: usize = unimplemented!(); fn serialize_into<W: Write>(&self, mut writer: W) -> FieldSerdeResult<()> { - self.num_variables.serialize_into(&mut writer)?; + self.num_vars.serialize_into(&mut writer)?; self.code_instance.serialize_into(&mut writer)?; Ok(()) } @@ -95,7 +95,7 @@ impl FieldSerde for OrionSRS { let num_variables = usize::deserialize_from(&mut reader)?; let code_instance = OrionCode::deserialize_from(&mut reader)?; Ok(Self { - num_variables, + num_vars: num_variables, code_instance, }) } diff --git a/poly_commit/src/orion/utils.rs b/poly_commit/src/orion/utils.rs index 11e15a89..6e9dc53f 100644 --- a/poly_commit/src/orion/utils.rs +++ b/poly_commit/src/orion/utils.rs @@ -29,7 +29,7 @@ pub type OrionResult<T> = std::result::Result<T, OrionPCSError>; #[derive(Clone, Debug, Default)] pub struct OrionSRS { - pub num_variables: usize, + pub num_vars: usize, pub code_instance: OrionCode, } @@ -44,8 +44,8 @@ impl TensorCodeIOPPCS for OrionSRS { } impl OrionSRS { - pub fn new<F: Field>(num_variables: usize, code_instance: OrionCode) -> OrionResult<Self> { - let (_, msg_size) = Self::evals_shape::<F>(num_variables); + pub fn new<F: Field>(num_vars: usize, code_instance: OrionCode) -> OrionResult<Self> { + let (_, msg_size) = Self::evals_shape::<F>(num_vars); if msg_size != code_instance.msg_len() { return Err(OrionPCSError::ParameterUnmatchError); } @@ -53,7 +53,7 @@ impl OrionSRS { // NOTE: we just move the instance of code, // don't think the instance of expander code will be used elsewhere Ok(Self { - num_variables, + num_vars, code_instance, }) } @@ -66,7 +66,7 @@ impl OrionSRS { let (_, msg_size) = Self::evals_shape::<F>(num_variables); Self { - num_variables, + num_vars: num_variables, code_instance: OrionCode::new(code_param_instance, msg_size, &mut rng), } } From e0007878acf245c3a0a85547ef815a6e6c80c209 Mon Sep 17 00:00:00 2001 From: Hang Su <tonyfloater@gmail.com> Date: Fri, 29 Nov 2024 09:41:59 -0500 Subject: [PATCH 27/65] pcs trait impl file too large, separate into 2 files --- poly_commit/src/orion.rs | 5 +- poly_commit/src/orion/pcs_for_expander_gkr.rs | 176 ++++++++++++++++++ ...ander_integration.rs => pcs_trait_impl.rs} | 132 +------------ 3 files changed, 180 insertions(+), 133 deletions(-) create mode 100644 poly_commit/src/orion/pcs_for_expander_gkr.rs rename poly_commit/src/orion/{expander_integration.rs => pcs_trait_impl.rs} (62%) diff --git a/poly_commit/src/orion.rs b/poly_commit/src/orion.rs index ff1ba022..5b07f2f6 100644 --- a/poly_commit/src/orion.rs +++ b/poly_commit/src/orion.rs @@ -26,7 +26,8 @@ pub use simd_field_impl::{ #[cfg(test)] mod simd_field_tests; -mod expander_integration; -pub use expander_integration::{OrionBaseFieldPCS, OrionSIMDFieldPCS}; +mod pcs_for_expander_gkr; +mod pcs_trait_impl; +pub use pcs_trait_impl::{OrionBaseFieldPCS, OrionSIMDFieldPCS}; mod serde; diff --git a/poly_commit/src/orion/pcs_for_expander_gkr.rs b/poly_commit/src/orion/pcs_for_expander_gkr.rs new file mode 100644 index 00000000..8c3aeea0 --- /dev/null +++ b/poly_commit/src/orion/pcs_for_expander_gkr.rs @@ -0,0 +1,176 @@ +use std::io::Cursor; + +use arith::{Field, FieldSerde, SimdField}; +use gkr_field_config::GKRFieldConfig; +use mpi_config::MPIConfig; +use polynomials::{EqPolynomial, MultiLinearPoly}; +use transcript::Transcript; + +use crate::{orion::*, ExpanderGKRChallenge, PCSForExpanderGKR, StructuredReferenceString}; + +impl<C, ComPackF, OpenPackF, T> PCSForExpanderGKR<C, T> + for OrionSIMDFieldPCS< + C::CircuitField, + C::SimdCircuitField, + C::ChallengeField, + ComPackF, + OpenPackF, + T, + > +where + C: GKRFieldConfig, + ComPackF: SimdField<Scalar = C::CircuitField>, + OpenPackF: SimdField<Scalar = C::CircuitField>, + T: Transcript<C::ChallengeField>, +{ + const NAME: &'static str = "OrionSIMDPCSForExpanderGKR"; + + type Params = usize; + type ScratchPad = OrionScratchPad<C::CircuitField, ComPackF>; + + type Commitment = OrionCommitment; + type Opening = OrionProof<C::ChallengeField>; + type SRS = OrionSRS; + + fn gen_params(n_input_vars: usize) -> Self::Params { + n_input_vars + } + + fn gen_srs_for_testing( + params: &Self::Params, + mpi_config: &MPIConfig, + rng: impl rand::RngCore, + ) -> Self::SRS { + let num_vars_each_core = *params - mpi_config.world_size(); + OrionSRS::from_random::<C::CircuitField>( + num_vars_each_core, + ORION_CODE_PARAMETER_INSTANCE, + rng, + ) + } + + fn init_scratch_pad(_params: &Self::Params, _mpi_config: &MPIConfig) -> Self::ScratchPad { + Self::ScratchPad::default() + } + + fn commit( + params: &Self::Params, + mpi_config: &MPIConfig, + proving_key: &<Self::SRS as StructuredReferenceString>::PKey, + poly: &MultiLinearPoly<C::SimdCircuitField>, + scratch_pad: &mut Self::ScratchPad, + ) -> Self::Commitment { + let num_vars_each_core = *params - mpi_config.world_size(); + assert_eq!(num_vars_each_core, proving_key.num_vars); + + let commitment = orion_commit_simd_field(proving_key, poly, scratch_pad).unwrap(); + if mpi_config.world_size == 1 { + return commitment; + } + + let local_buffer = vec![commitment]; + let mut buffer = match mpi_config.is_root() { + true => vec![Self::Commitment::default(); mpi_config.world_size()], + _ => Vec::new(), + }; + mpi_config.gather_vec(&local_buffer, &mut buffer); + + let mut root = Self::Commitment::default(); + if mpi_config.is_root() { + let final_tree_height = 1 + buffer.len().ilog2(); + let (internals, _) = tree::Tree::new_with_leaf_nodes(buffer.clone(), final_tree_height); + root = internals[0]; + } + mpi_config.root_broadcast_f(&mut root); + root + } + + fn open( + params: &Self::Params, + mpi_config: &MPIConfig, + proving_key: &<Self::SRS as StructuredReferenceString>::PKey, + poly: &MultiLinearPoly<C::SimdCircuitField>, + eval_point: &ExpanderGKRChallenge<C>, + transcript: &mut T, // add transcript here to allow interactive arguments + scratch_pad: &mut Self::ScratchPad, + ) -> Self::Opening { + let num_vars_each_core = *params - mpi_config.world_size(); + assert_eq!(num_vars_each_core, proving_key.num_vars); + + let local_xs = eval_point.local_xs(); + let local_opening = orion_open_simd_field::< + C::CircuitField, + C::SimdCircuitField, + C::ChallengeField, + ComPackF, + OpenPackF, + T, + >(proving_key, poly, &local_xs, transcript, scratch_pad); + if mpi_config.world_size == 1 { + return local_opening; + } + + let mpi_random_coeffs: Vec<_> = (0..local_opening.proximity_rows.len()) + .map(|_| transcript.generate_challenge_field_elements(mpi_config.world_size())) + .collect(); + let mpi_eq_coeffs = EqPolynomial::build_eq_x_r(&eval_point.x_mpi); + + let mut combined_eval_row = local_opening.eval_row.clone(); + mpi_linear_combine(mpi_config, &mut combined_eval_row, &mpi_eq_coeffs); + + let mut combined_proximity_rows = local_opening.proximity_rows.clone(); + combined_proximity_rows + .iter_mut() + .zip(mpi_random_coeffs.iter()) + .for_each(|(row, weights)| mpi_linear_combine(mpi_config, row, weights)); + + // TODO gather all merkle paths + + todo!() + } + + fn verify( + params: &Self::Params, + mpi_config: &MPIConfig, + verifying_key: &<Self::SRS as StructuredReferenceString>::VKey, + commitment: &Self::Commitment, + eval_point: &ExpanderGKRChallenge<C>, + v: C::ChallengeField, + transcript: &mut T, // add transcript here to allow interactive arguments + opening: &Self::Opening, + ) -> bool { + let num_vars_each_core = *params - mpi_config.world_size(); + assert_eq!(num_vars_each_core, verifying_key.num_vars); + + // TODO only verify the gathered orion opening + + let local_xs = eval_point.local_xs(); + if mpi_config.world_size == 1 { + return orion_verify_simd_field::< + C::CircuitField, + C::SimdCircuitField, + C::ChallengeField, + ComPackF, + OpenPackF, + T, + >(verifying_key, commitment, &local_xs, v, transcript, opening); + } + + // TODO ... decide open and verify in distributed settings + + todo!() + } +} + +fn mpi_linear_combine<F: Field>(mpi_comm: &MPIConfig, local_vec: &mut Vec<F>, weights: &[F]) { + let combined = mpi_comm.coef_combine_vec(local_vec, weights); + + let mut bytes: Vec<u8> = Vec::new(); + combined.serialize_into(&mut bytes).unwrap(); + mpi_comm.root_broadcast_bytes(&mut bytes); + + let cursor = Cursor::new(bytes); + let final_res = <Vec<F> as FieldSerde>::deserialize_from(cursor).unwrap(); + + local_vec.copy_from_slice(&final_res); +} diff --git a/poly_commit/src/orion/expander_integration.rs b/poly_commit/src/orion/pcs_trait_impl.rs similarity index 62% rename from poly_commit/src/orion/expander_integration.rs rename to poly_commit/src/orion/pcs_trait_impl.rs index d0f3302a..8ad1a212 100644 --- a/poly_commit/src/orion/expander_integration.rs +++ b/poly_commit/src/orion/pcs_trait_impl.rs @@ -1,14 +1,11 @@ use std::marker::PhantomData; use arith::{ExtensionField, Field, SimdField}; -use gkr_field_config::GKRFieldConfig; -use mpi_config::MPIConfig; use polynomials::MultiLinearPoly; use transcript::Transcript; use crate::{ - orion::*, traits::TensorCodeIOPPCS, ExpanderGKRChallenge, PCSForExpanderGKR, - PolynomialCommitmentScheme, StructuredReferenceString, + orion::*, traits::TensorCodeIOPPCS, PolynomialCommitmentScheme, StructuredReferenceString, }; impl StructuredReferenceString for OrionSRS { @@ -234,130 +231,3 @@ where ) } } - -impl<C, ComPackF, OpenPackF, T> PCSForExpanderGKR<C, T> - for OrionSIMDFieldPCS< - C::CircuitField, - C::SimdCircuitField, - C::ChallengeField, - ComPackF, - OpenPackF, - T, - > -where - C: GKRFieldConfig, - ComPackF: SimdField<Scalar = C::CircuitField>, - OpenPackF: SimdField<Scalar = C::CircuitField>, - T: Transcript<C::ChallengeField>, -{ - const NAME: &'static str = "OrionSIMDPCSForExpanderGKR"; - - type Params = usize; - type ScratchPad = OrionScratchPad<C::CircuitField, ComPackF>; - - type Commitment = OrionCommitment; - type Opening = OrionProof<C::ChallengeField>; - type SRS = OrionSRS; - - #[allow(unused)] - fn gen_params(n_input_vars: usize) -> Self::Params { - todo!() - } - - #[allow(unused)] - fn gen_srs_for_testing( - params: &Self::Params, - mpi_config: &MPIConfig, - rng: impl rand::RngCore, - ) -> Self::SRS { - todo!() - } - - fn init_scratch_pad(_params: &Self::Params, _mpi_config: &MPIConfig) -> Self::ScratchPad { - Self::ScratchPad::default() - } - - fn commit( - _params: &Self::Params, - mpi_config: &MPIConfig, - proving_key: &<Self::SRS as StructuredReferenceString>::PKey, - poly: &MultiLinearPoly<C::SimdCircuitField>, - scratch_pad: &mut Self::ScratchPad, - ) -> Self::Commitment { - let commitment = orion_commit_simd_field(proving_key, poly, scratch_pad).unwrap(); - if mpi_config.world_size == 1 { - return commitment; - } - - let local_buffer = vec![commitment]; - let mut buffer = match mpi_config.is_root() { - true => vec![Self::Commitment::default(); mpi_config.world_size()], - _ => Vec::new(), - }; - mpi_config.gather_vec(&local_buffer, &mut buffer); - - let mut root = Self::Commitment::default(); - if mpi_config.is_root() { - let final_tree_height = 1 + buffer.len().ilog2(); - let (internals, _) = tree::Tree::new_with_leaf_nodes(buffer.clone(), final_tree_height); - root = internals[0]; - } - mpi_config.root_broadcast_f(&mut root); - root - } - - fn open( - _params: &Self::Params, - mpi_config: &MPIConfig, - proving_key: &<Self::SRS as StructuredReferenceString>::PKey, - poly: &MultiLinearPoly<C::SimdCircuitField>, - eval_point: &ExpanderGKRChallenge<C>, - transcript: &mut T, // add transcript here to allow interactive arguments - scratch_pad: &mut Self::ScratchPad, - ) -> Self::Opening { - let local_xs = eval_point.local_xs(); - let local_opening = orion_open_simd_field::< - C::CircuitField, - C::SimdCircuitField, - C::ChallengeField, - ComPackF, - OpenPackF, - T, - >(proving_key, poly, &local_xs, transcript, scratch_pad); - if mpi_config.world_size == 1 { - return local_opening; - } - - // TODO ... is x_mpi right of (earlier evaluated than) x_simd and x? - // seems so. - - todo!() - } - - fn verify( - _params: &Self::Params, - mpi_config: &MPIConfig, - verifying_key: &<Self::SRS as StructuredReferenceString>::VKey, - commitment: &Self::Commitment, - eval_point: &ExpanderGKRChallenge<C>, - v: C::ChallengeField, - transcript: &mut T, // add transcript here to allow interactive arguments - opening: &Self::Opening, - ) -> bool { - let local_xs = eval_point.local_xs(); - if mpi_config.world_size == 1 { - return orion_verify_simd_field::< - C::CircuitField, - C::SimdCircuitField, - C::ChallengeField, - ComPackF, - OpenPackF, - T, - >(verifying_key, commitment, &local_xs, v, transcript, opening); - } - - // TODO ... decide open and verify in distributed settings - - todo!() - } -} From 1fd56550f6d1f99c91f63a79523801ee33921ac1 Mon Sep 17 00:00:00 2001 From: Hang Su <tonyfloater@gmail.com> Date: Fri, 29 Nov 2024 11:21:27 -0500 Subject: [PATCH 28/65] working on mpi version of open/verify, based on assumption that only leader machine's output matters --- poly_commit/src/orion/pcs_for_expander_gkr.rs | 75 +++++++++---------- tree/src/path.rs | 10 +++ 2 files changed, 47 insertions(+), 38 deletions(-) diff --git a/poly_commit/src/orion/pcs_for_expander_gkr.rs b/poly_commit/src/orion/pcs_for_expander_gkr.rs index 8c3aeea0..ca63e37a 100644 --- a/poly_commit/src/orion/pcs_for_expander_gkr.rs +++ b/poly_commit/src/orion/pcs_for_expander_gkr.rs @@ -1,6 +1,4 @@ -use std::io::Cursor; - -use arith::{Field, FieldSerde, SimdField}; +use arith::SimdField; use gkr_field_config::GKRFieldConfig; use mpi_config::MPIConfig; use polynomials::{EqPolynomial, MultiLinearPoly}; @@ -69,20 +67,18 @@ where } let local_buffer = vec![commitment]; - let mut buffer = match mpi_config.is_root() { - true => vec![Self::Commitment::default(); mpi_config.world_size()], - _ => Vec::new(), - }; + let mut buffer = vec![Self::Commitment::default(); mpi_config.world_size()]; mpi_config.gather_vec(&local_buffer, &mut buffer); - let mut root = Self::Commitment::default(); - if mpi_config.is_root() { - let final_tree_height = 1 + buffer.len().ilog2(); - let (internals, _) = tree::Tree::new_with_leaf_nodes(buffer.clone(), final_tree_height); - root = internals[0]; + // NOTE: Hang also assume that, linear GKR will take over the commitment + // and force sync transcript hash state of subordinate machines to be the same. + if !mpi_config.is_root() { + return commitment; } - mpi_config.root_broadcast_f(&mut root); - root + + let final_tree_height = 1 + buffer.len().ilog2(); + let (internals, _) = tree::Tree::new_with_leaf_nodes(buffer.clone(), final_tree_height); + internals[0] } fn open( @@ -110,23 +106,40 @@ where return local_opening; } + // NOTE: sample MPI linear combination coeffs for proximity rows and eval row let mpi_random_coeffs: Vec<_> = (0..local_opening.proximity_rows.len()) .map(|_| transcript.generate_challenge_field_elements(mpi_config.world_size())) .collect(); let mpi_eq_coeffs = EqPolynomial::build_eq_x_r(&eval_point.x_mpi); - let mut combined_eval_row = local_opening.eval_row.clone(); - mpi_linear_combine(mpi_config, &mut combined_eval_row, &mpi_eq_coeffs); + // NOTE: eval row combine from MPI + let eval_row = mpi_config.coef_combine_vec(&local_opening.eval_row, &mpi_eq_coeffs); - let mut combined_proximity_rows = local_opening.proximity_rows.clone(); - combined_proximity_rows - .iter_mut() + // NOTE: proximity rows combine from MPI + let proximity_rows = local_opening + .proximity_rows + .iter() .zip(mpi_random_coeffs.iter()) - .for_each(|(row, weights)| mpi_linear_combine(mpi_config, row, weights)); + .map(|(row, weights)| mpi_config.coef_combine_vec(row, weights)) + .collect(); - // TODO gather all merkle paths + // NOTE: gather all merkle paths + let mut query_openings = vec![ + tree::RangePath::default(); + mpi_config.world_size() * local_opening.query_openings.len() + ]; + mpi_config.gather_vec(&local_opening.query_openings, &mut query_openings); - todo!() + if !mpi_config.is_root() { + return local_opening; + } + + // NOTE: we only care about the root machine's opening as final proof, Hang assume. + OrionProof { + eval_row, + proximity_rows, + query_openings, + } } fn verify( @@ -142,8 +155,6 @@ where let num_vars_each_core = *params - mpi_config.world_size(); assert_eq!(num_vars_each_core, verifying_key.num_vars); - // TODO only verify the gathered orion opening - let local_xs = eval_point.local_xs(); if mpi_config.world_size == 1 { return orion_verify_simd_field::< @@ -156,21 +167,9 @@ where >(verifying_key, commitment, &local_xs, v, transcript, opening); } - // TODO ... decide open and verify in distributed settings + // NOTE: we now assume that the input opening is from the root machine, + // as proofs from other machines are typically undefined todo!() } } - -fn mpi_linear_combine<F: Field>(mpi_comm: &MPIConfig, local_vec: &mut Vec<F>, weights: &[F]) { - let combined = mpi_comm.coef_combine_vec(local_vec, weights); - - let mut bytes: Vec<u8> = Vec::new(); - combined.serialize_into(&mut bytes).unwrap(); - mpi_comm.root_broadcast_bytes(&mut bytes); - - let cursor = Cursor::new(bytes); - let final_res = <Vec<F> as FieldSerde>::deserialize_from(cursor).unwrap(); - - local_vec.copy_from_slice(&final_res); -} diff --git a/tree/src/path.rs b/tree/src/path.rs index 6e3b67ff..ad244c6b 100644 --- a/tree/src/path.rs +++ b/tree/src/path.rs @@ -81,6 +81,11 @@ impl Path { } } + #[inline] + pub fn root(&self) -> Node { + self.path_nodes[0] + } + #[inline] pub fn unpack_field_elems<F, PackF>(&self) -> Vec<F> where @@ -129,6 +134,11 @@ impl RangePath { (0..self.path_nodes.len() + 1).map(move |i| ((common_ancestor >> i) & 1) != 0) } + #[inline] + pub fn root(&self) -> Node { + self.path_nodes[0] + } + #[inline] pub fn unpack_field_elems<F, PackF>(&self) -> Vec<F> where From 2c771f57f8065d8530d82dc6db9df864a3e9dd5a Mon Sep 17 00:00:00 2001 From: Hang Su <tonyfloater@gmail.com> Date: Sat, 30 Nov 2024 08:07:03 -0500 Subject: [PATCH 29/65] prototype in orion simd verify aggregated version --- poly_commit/src/orion/base_field_impl.rs | 4 +- poly_commit/src/orion/pcs_for_expander_gkr.rs | 231 ++++++++++++++++-- poly_commit/src/orion/simd_field_impl.rs | 4 +- poly_commit/src/traits.rs | 4 + 4 files changed, 222 insertions(+), 21 deletions(-) diff --git a/poly_commit/src/orion/base_field_impl.rs b/poly_commit/src/orion/base_field_impl.rs index bffe8281..5edeeeca 100644 --- a/poly_commit/src/orion/base_field_impl.rs +++ b/poly_commit/src/orion/base_field_impl.rs @@ -167,8 +167,8 @@ where // NOTE: working on proximity responses, draw random linear combinations // then draw query points from fiat shamir transcripts - let proximity_test_num = vk.proximity_repetitions::<EvalF>(PCS_SOUNDNESS_BITS); - let random_linear_combinations: Vec<Vec<EvalF>> = (0..proximity_test_num) + let proximity_reps = vk.proximity_repetitions::<EvalF>(PCS_SOUNDNESS_BITS); + let random_linear_combinations: Vec<Vec<EvalF>> = (0..proximity_reps) .map(|_| transcript.generate_challenge_field_elements(row_num)) .collect(); let query_num = vk.query_complexity(PCS_SOUNDNESS_BITS); diff --git a/poly_commit/src/orion/pcs_for_expander_gkr.rs b/poly_commit/src/orion/pcs_for_expander_gkr.rs index ca63e37a..99f39afd 100644 --- a/poly_commit/src/orion/pcs_for_expander_gkr.rs +++ b/poly_commit/src/orion/pcs_for_expander_gkr.rs @@ -1,10 +1,16 @@ -use arith::SimdField; +use std::iter; + +use arith::{Field, SimdField}; use gkr_field_config::GKRFieldConfig; use mpi_config::MPIConfig; use polynomials::{EqPolynomial, MultiLinearPoly}; use transcript::Transcript; +use utils::{orion_mt_verify, transpose_in_place}; -use crate::{orion::*, ExpanderGKRChallenge, PCSForExpanderGKR, StructuredReferenceString}; +use crate::{ + orion::*, traits::TensorCodeIOPPCS, ExpanderGKRChallenge, PCSForExpanderGKR, + StructuredReferenceString, PCS_SOUNDNESS_BITS, +}; impl<C, ComPackF, OpenPackF, T> PCSForExpanderGKR<C, T> for OrionSIMDFieldPCS< @@ -106,21 +112,19 @@ where return local_opening; } - // NOTE: sample MPI linear combination coeffs for proximity rows and eval row - let mpi_random_coeffs: Vec<_> = (0..local_opening.proximity_rows.len()) - .map(|_| transcript.generate_challenge_field_elements(mpi_config.world_size())) - .collect(); - let mpi_eq_coeffs = EqPolynomial::build_eq_x_r(&eval_point.x_mpi); - // NOTE: eval row combine from MPI + let mpi_eq_coeffs = EqPolynomial::build_eq_x_r(&eval_point.x_mpi); let eval_row = mpi_config.coef_combine_vec(&local_opening.eval_row, &mpi_eq_coeffs); - // NOTE: proximity rows combine from MPI + // NOTE: sample MPI linear combination coeffs for proximity rows, + // and proximity rows combine with MPI let proximity_rows = local_opening .proximity_rows .iter() - .zip(mpi_random_coeffs.iter()) - .map(|(row, weights)| mpi_config.coef_combine_vec(row, weights)) + .map(|row| { + let weights = transcript.generate_challenge_field_elements(mpi_config.world_size()); + mpi_config.coef_combine_vec(row, &weights) + }) .collect(); // NOTE: gather all merkle paths @@ -148,14 +152,12 @@ where verifying_key: &<Self::SRS as StructuredReferenceString>::VKey, commitment: &Self::Commitment, eval_point: &ExpanderGKRChallenge<C>, - v: C::ChallengeField, + eval: C::ChallengeField, transcript: &mut T, // add transcript here to allow interactive arguments opening: &Self::Opening, ) -> bool { - let num_vars_each_core = *params - mpi_config.world_size(); - assert_eq!(num_vars_each_core, verifying_key.num_vars); + assert_eq!(*params, eval_point.num_vars()); - let local_xs = eval_point.local_xs(); if mpi_config.world_size == 1 { return orion_verify_simd_field::< C::CircuitField, @@ -164,12 +166,207 @@ where ComPackF, OpenPackF, T, - >(verifying_key, commitment, &local_xs, v, transcript, opening); + >( + verifying_key, + commitment, + &eval_point.local_xs(), + eval, + transcript, + opening, + ); } // NOTE: we now assume that the input opening is from the root machine, // as proofs from other machines are typically undefined + orion_verify_simd_field_aggregated::<C, ComPackF, OpenPackF, T>( + mpi_config.world_size(), + verifying_key, + commitment, + eval_point, + eval, + transcript, + opening, + ) + } +} + +fn orion_verify_simd_field_aggregated<C, ComPackF, OpenPackF, T>( + mpi_world_size: usize, + vk: &OrionSRS, + commitment: &OrionCommitment, + eval_point: &ExpanderGKRChallenge<C>, + eval: C::ChallengeField, + transcript: &mut T, + proof: &OrionProof<C::ChallengeField>, +) -> bool +where + C: GKRFieldConfig, + ComPackF: SimdField<Scalar = C::CircuitField>, + OpenPackF: SimdField<Scalar = C::CircuitField>, + T: Transcript<C::ChallengeField>, +{ + let local_num_vars = eval_point.num_vars() - mpi_world_size.ilog2() as usize; + assert_eq!(local_num_vars, vk.num_vars); + + let (row_num, msg_size) = { + let (row_field_elems, msg_size) = OrionSRS::evals_shape::<C::CircuitField>(local_num_vars); + let row_num = row_field_elems / C::SimdCircuitField::PACK_SIZE; + (row_num, msg_size) + }; + + let num_vars_in_local_rows = row_num.ilog2() as usize; + let num_vars_in_unpacked_msg = local_num_vars - num_vars_in_local_rows; + let local_xs = eval_point.local_xs(); - todo!() + // NOTE: working on evaluation response + let mut scratch = vec![C::ChallengeField::ZERO; msg_size * C::SimdCircuitField::PACK_SIZE]; + let final_eval = MultiLinearPoly::evaluate_with_buffer( + &proof.eval_row, + &local_xs[..num_vars_in_unpacked_msg], + &mut scratch, + ); + if final_eval != eval { + return false; } + + // NOTE: working on proximity responses, draw random linear combinations + // then draw query points from fiat shamir transcripts + let proximity_reps = vk.proximity_repetitions::<C::ChallengeField>(PCS_SOUNDNESS_BITS); + let proximity_coeffs: Vec<Vec<C::ChallengeField>> = (0..proximity_reps) + .map(|_| transcript.generate_challenge_field_elements(row_num)) + .collect(); + let query_num = vk.query_complexity(PCS_SOUNDNESS_BITS); + let query_indices = transcript.generate_challenge_index_vector(query_num); + + // NOTE: check all merkle paths + if !proof + .query_openings + .chunks(query_num) + .all(|queries| orion_mt_verify(vk, &query_indices, queries, &queries[0].root())) + { + return false; + } + + // NOTE: collect each merkle roots, build final root against commitment + let roots: Vec<_> = proof + .query_openings + .chunks(query_num) + .map(|p| p[0].root()) + .collect(); + let final_tree_height = 1 + roots.len().ilog2(); + let (internals, _) = tree::Tree::new_with_leaf_nodes(roots, final_tree_height); + if internals[0] != *commitment { + return false; + } + + // NOTE: prepare the interleaved alphabets from the MT paths, + // but reshuffle the packed elements into another direction + let mut scratch = vec![C::CircuitField::ZERO; C::SimdCircuitField::PACK_SIZE * row_num]; + let shuffled_interleaved_alphabet: Vec<_> = proof + .query_openings + .iter() + .map(|c| -> Vec<_> { + let mut elts = c.unpack_field_elems::<C::CircuitField, ComPackF>(); + transpose_in_place(&mut elts, &mut scratch, row_num); + elts.chunks(OpenPackF::PACK_SIZE) + .map(OpenPackF::pack) + .collect() + }) + .collect(); + + // NOTE: compute alphabets from proximity/evaluation coeffs + let table_num = row_num / OpenPackF::PACK_SIZE; + let mut luts = SubsetSumLUTs::<C::ChallengeField>::new(OpenPackF::PACK_SIZE, table_num); + assert_eq!(row_num % OpenPackF::PACK_SIZE, 0); + + let eq_local_coeffs = EqPolynomial::build_eq_x_r(&local_xs[num_vars_in_unpacked_msg..]); + luts.build(&eq_local_coeffs); + + let mut scratch = + vec![C::ChallengeField::ZERO; mpi_world_size * C::SimdCircuitField::PACK_SIZE * msg_size]; + + let mut eval_qs: Vec<_> = shuffled_interleaved_alphabet + .iter() + .flat_map(|c| -> Vec<_> { + c.chunks(table_num) + .map(|ts| luts.lookup_and_sum(ts)) + .collect() + }) + .collect(); + transpose_in_place(&mut eval_qs, &mut scratch, mpi_world_size); + + let proximity_qs: Vec<_> = proximity_coeffs + .iter() + .map(|ps| { + luts.build(ps); + let mut worlds_proximity_resps: Vec<_> = shuffled_interleaved_alphabet + .iter() + .flat_map(|c| -> Vec<_> { + c.chunks(table_num) + .map(|ts| luts.lookup_and_sum(ts)) + .collect() + }) + .collect(); + transpose_in_place(&mut worlds_proximity_resps, &mut scratch, mpi_world_size); + worlds_proximity_resps + }) + .collect(); + + // NOTE: sum up each worlds responses with weights + let eq_worlds_coeffs = EqPolynomial::build_eq_x_r(&eval_point.x_mpi); + let actual_evals: Vec<C::ChallengeField> = eval_qs + .chunks(mpi_world_size) + .map(|rs| inner_prod(rs, &eq_worlds_coeffs)) + .collect(); + + let actual_proximity_resps: Vec<Vec<C::ChallengeField>> = proximity_qs + .iter() + .map(|ps| { + let weights = transcript.generate_challenge_field_elements(mpi_world_size); + ps.chunks(mpi_world_size) + .map(|rs| inner_prod(rs, &weights)) + .collect() + }) + .collect(); + + // NOTE: decide if expected alphabet matches actual responses + let mut scratch_msg = vec![C::ChallengeField::ZERO; C::SimdCircuitField::PACK_SIZE * msg_size]; + let mut scratch_codeword = + vec![C::ChallengeField::ZERO; C::SimdCircuitField::PACK_SIZE * vk.codeword_len()]; + actual_proximity_resps + .iter() + .zip(proof.proximity_rows.iter()) + .chain(iter::once((&actual_evals, &proof.eval_row))) + .all(|(actual_alphabets, msg)| { + let mut msg_cloned = msg.clone(); + transpose_in_place(&mut msg_cloned, &mut scratch_msg, msg_size); + let mut codeword: Vec<_> = msg_cloned + .chunks(msg_size) + .flat_map(|m| vk.code_instance.encode(m).unwrap()) + .collect(); + transpose_in_place( + &mut codeword, + &mut scratch_codeword, + C::SimdCircuitField::PACK_SIZE, + ); + + query_indices + .iter() + .zip(actual_alphabets.chunks(C::SimdCircuitField::PACK_SIZE)) + .all(|(qi, simd_alphabets)| { + let index = qi % vk.codeword_len(); + + let simd_starts = index * C::SimdCircuitField::PACK_SIZE; + let simd_ends = (index + 1) * C::SimdCircuitField::PACK_SIZE; + + codeword[simd_starts..simd_ends] + .iter() + .zip(simd_alphabets.iter()) + .all(|(ec, ac)| ec == ac) + }) + }) +} + +fn inner_prod<F: Field>(ls: &[F], rs: &[F]) -> F { + ls.iter().zip(rs.iter()).map(|(&l, &r)| r * l).sum() } diff --git a/poly_commit/src/orion/simd_field_impl.rs b/poly_commit/src/orion/simd_field_impl.rs index b9f98db3..2d67a1ba 100644 --- a/poly_commit/src/orion/simd_field_impl.rs +++ b/poly_commit/src/orion/simd_field_impl.rs @@ -219,8 +219,8 @@ where // NOTE: working on proximity responses, draw random linear combinations // then draw query points from fiat shamir transcripts - let proximity_test_num = vk.proximity_repetitions::<EvalF>(PCS_SOUNDNESS_BITS); - let random_linear_combinations: Vec<Vec<EvalF>> = (0..proximity_test_num) + let proximity_reps = vk.proximity_repetitions::<EvalF>(PCS_SOUNDNESS_BITS); + let random_linear_combinations: Vec<Vec<EvalF>> = (0..proximity_reps) .map(|_| transcript.generate_challenge_field_elements(row_num)) .collect(); let query_num = vk.query_complexity(PCS_SOUNDNESS_BITS); diff --git a/poly_commit/src/traits.rs b/poly_commit/src/traits.rs index 73668ed8..b32277a4 100644 --- a/poly_commit/src/traits.rs +++ b/poly_commit/src/traits.rs @@ -78,6 +78,10 @@ impl<C: GKRFieldConfig> ExpanderGKRChallenge<C> { local_xs[self.x_simd.len()..].copy_from_slice(&self.x); local_xs } + + pub fn num_vars(&self) -> usize { + self.x.len() + self.x_simd.len() + self.x_mpi.len() + } } pub trait PCSForExpanderGKR<C: GKRFieldConfig, T: Transcript<C::ChallengeField>> { From 0112511c3061f596faff652784b04f64e732a501 Mon Sep 17 00:00:00 2001 From: Hang Su <tonyfloater@gmail.com> Date: Sat, 30 Nov 2024 08:29:03 -0500 Subject: [PATCH 30/65] separate aggregated verify for orion simd field into a standalone file --- poly_commit/src/orion.rs | 5 + poly_commit/src/orion/pcs_for_expander_gkr.rs | 190 +---------------- poly_commit/src/orion/simd_field_agg_impl.rs | 192 ++++++++++++++++++ poly_commit/src/orion/simd_field_agg_tests.rs | 2 + 4 files changed, 202 insertions(+), 187 deletions(-) create mode 100644 poly_commit/src/orion/simd_field_agg_impl.rs create mode 100644 poly_commit/src/orion/simd_field_agg_tests.rs diff --git a/poly_commit/src/orion.rs b/poly_commit/src/orion.rs index 5b07f2f6..32a500a1 100644 --- a/poly_commit/src/orion.rs +++ b/poly_commit/src/orion.rs @@ -26,6 +26,11 @@ pub use simd_field_impl::{ #[cfg(test)] mod simd_field_tests; +mod simd_field_agg_impl; + +#[cfg(test)] +mod simd_field_agg_tests; + mod pcs_for_expander_gkr; mod pcs_trait_impl; pub use pcs_trait_impl::{OrionBaseFieldPCS, OrionSIMDFieldPCS}; diff --git a/poly_commit/src/orion/pcs_for_expander_gkr.rs b/poly_commit/src/orion/pcs_for_expander_gkr.rs index 99f39afd..54400878 100644 --- a/poly_commit/src/orion/pcs_for_expander_gkr.rs +++ b/poly_commit/src/orion/pcs_for_expander_gkr.rs @@ -1,15 +1,12 @@ -use std::iter; - -use arith::{Field, SimdField}; +use arith::SimdField; use gkr_field_config::GKRFieldConfig; use mpi_config::MPIConfig; use polynomials::{EqPolynomial, MultiLinearPoly}; use transcript::Transcript; -use utils::{orion_mt_verify, transpose_in_place}; use crate::{ - orion::*, traits::TensorCodeIOPPCS, ExpanderGKRChallenge, PCSForExpanderGKR, - StructuredReferenceString, PCS_SOUNDNESS_BITS, + orion::{simd_field_agg_impl::*, *}, + ExpanderGKRChallenge, PCSForExpanderGKR, StructuredReferenceString, }; impl<C, ComPackF, OpenPackF, T> PCSForExpanderGKR<C, T> @@ -189,184 +186,3 @@ where ) } } - -fn orion_verify_simd_field_aggregated<C, ComPackF, OpenPackF, T>( - mpi_world_size: usize, - vk: &OrionSRS, - commitment: &OrionCommitment, - eval_point: &ExpanderGKRChallenge<C>, - eval: C::ChallengeField, - transcript: &mut T, - proof: &OrionProof<C::ChallengeField>, -) -> bool -where - C: GKRFieldConfig, - ComPackF: SimdField<Scalar = C::CircuitField>, - OpenPackF: SimdField<Scalar = C::CircuitField>, - T: Transcript<C::ChallengeField>, -{ - let local_num_vars = eval_point.num_vars() - mpi_world_size.ilog2() as usize; - assert_eq!(local_num_vars, vk.num_vars); - - let (row_num, msg_size) = { - let (row_field_elems, msg_size) = OrionSRS::evals_shape::<C::CircuitField>(local_num_vars); - let row_num = row_field_elems / C::SimdCircuitField::PACK_SIZE; - (row_num, msg_size) - }; - - let num_vars_in_local_rows = row_num.ilog2() as usize; - let num_vars_in_unpacked_msg = local_num_vars - num_vars_in_local_rows; - let local_xs = eval_point.local_xs(); - - // NOTE: working on evaluation response - let mut scratch = vec![C::ChallengeField::ZERO; msg_size * C::SimdCircuitField::PACK_SIZE]; - let final_eval = MultiLinearPoly::evaluate_with_buffer( - &proof.eval_row, - &local_xs[..num_vars_in_unpacked_msg], - &mut scratch, - ); - if final_eval != eval { - return false; - } - - // NOTE: working on proximity responses, draw random linear combinations - // then draw query points from fiat shamir transcripts - let proximity_reps = vk.proximity_repetitions::<C::ChallengeField>(PCS_SOUNDNESS_BITS); - let proximity_coeffs: Vec<Vec<C::ChallengeField>> = (0..proximity_reps) - .map(|_| transcript.generate_challenge_field_elements(row_num)) - .collect(); - let query_num = vk.query_complexity(PCS_SOUNDNESS_BITS); - let query_indices = transcript.generate_challenge_index_vector(query_num); - - // NOTE: check all merkle paths - if !proof - .query_openings - .chunks(query_num) - .all(|queries| orion_mt_verify(vk, &query_indices, queries, &queries[0].root())) - { - return false; - } - - // NOTE: collect each merkle roots, build final root against commitment - let roots: Vec<_> = proof - .query_openings - .chunks(query_num) - .map(|p| p[0].root()) - .collect(); - let final_tree_height = 1 + roots.len().ilog2(); - let (internals, _) = tree::Tree::new_with_leaf_nodes(roots, final_tree_height); - if internals[0] != *commitment { - return false; - } - - // NOTE: prepare the interleaved alphabets from the MT paths, - // but reshuffle the packed elements into another direction - let mut scratch = vec![C::CircuitField::ZERO; C::SimdCircuitField::PACK_SIZE * row_num]; - let shuffled_interleaved_alphabet: Vec<_> = proof - .query_openings - .iter() - .map(|c| -> Vec<_> { - let mut elts = c.unpack_field_elems::<C::CircuitField, ComPackF>(); - transpose_in_place(&mut elts, &mut scratch, row_num); - elts.chunks(OpenPackF::PACK_SIZE) - .map(OpenPackF::pack) - .collect() - }) - .collect(); - - // NOTE: compute alphabets from proximity/evaluation coeffs - let table_num = row_num / OpenPackF::PACK_SIZE; - let mut luts = SubsetSumLUTs::<C::ChallengeField>::new(OpenPackF::PACK_SIZE, table_num); - assert_eq!(row_num % OpenPackF::PACK_SIZE, 0); - - let eq_local_coeffs = EqPolynomial::build_eq_x_r(&local_xs[num_vars_in_unpacked_msg..]); - luts.build(&eq_local_coeffs); - - let mut scratch = - vec![C::ChallengeField::ZERO; mpi_world_size * C::SimdCircuitField::PACK_SIZE * msg_size]; - - let mut eval_qs: Vec<_> = shuffled_interleaved_alphabet - .iter() - .flat_map(|c| -> Vec<_> { - c.chunks(table_num) - .map(|ts| luts.lookup_and_sum(ts)) - .collect() - }) - .collect(); - transpose_in_place(&mut eval_qs, &mut scratch, mpi_world_size); - - let proximity_qs: Vec<_> = proximity_coeffs - .iter() - .map(|ps| { - luts.build(ps); - let mut worlds_proximity_resps: Vec<_> = shuffled_interleaved_alphabet - .iter() - .flat_map(|c| -> Vec<_> { - c.chunks(table_num) - .map(|ts| luts.lookup_and_sum(ts)) - .collect() - }) - .collect(); - transpose_in_place(&mut worlds_proximity_resps, &mut scratch, mpi_world_size); - worlds_proximity_resps - }) - .collect(); - - // NOTE: sum up each worlds responses with weights - let eq_worlds_coeffs = EqPolynomial::build_eq_x_r(&eval_point.x_mpi); - let actual_evals: Vec<C::ChallengeField> = eval_qs - .chunks(mpi_world_size) - .map(|rs| inner_prod(rs, &eq_worlds_coeffs)) - .collect(); - - let actual_proximity_resps: Vec<Vec<C::ChallengeField>> = proximity_qs - .iter() - .map(|ps| { - let weights = transcript.generate_challenge_field_elements(mpi_world_size); - ps.chunks(mpi_world_size) - .map(|rs| inner_prod(rs, &weights)) - .collect() - }) - .collect(); - - // NOTE: decide if expected alphabet matches actual responses - let mut scratch_msg = vec![C::ChallengeField::ZERO; C::SimdCircuitField::PACK_SIZE * msg_size]; - let mut scratch_codeword = - vec![C::ChallengeField::ZERO; C::SimdCircuitField::PACK_SIZE * vk.codeword_len()]; - actual_proximity_resps - .iter() - .zip(proof.proximity_rows.iter()) - .chain(iter::once((&actual_evals, &proof.eval_row))) - .all(|(actual_alphabets, msg)| { - let mut msg_cloned = msg.clone(); - transpose_in_place(&mut msg_cloned, &mut scratch_msg, msg_size); - let mut codeword: Vec<_> = msg_cloned - .chunks(msg_size) - .flat_map(|m| vk.code_instance.encode(m).unwrap()) - .collect(); - transpose_in_place( - &mut codeword, - &mut scratch_codeword, - C::SimdCircuitField::PACK_SIZE, - ); - - query_indices - .iter() - .zip(actual_alphabets.chunks(C::SimdCircuitField::PACK_SIZE)) - .all(|(qi, simd_alphabets)| { - let index = qi % vk.codeword_len(); - - let simd_starts = index * C::SimdCircuitField::PACK_SIZE; - let simd_ends = (index + 1) * C::SimdCircuitField::PACK_SIZE; - - codeword[simd_starts..simd_ends] - .iter() - .zip(simd_alphabets.iter()) - .all(|(ec, ac)| ec == ac) - }) - }) -} - -fn inner_prod<F: Field>(ls: &[F], rs: &[F]) -> F { - ls.iter().zip(rs.iter()).map(|(&l, &r)| r * l).sum() -} diff --git a/poly_commit/src/orion/simd_field_agg_impl.rs b/poly_commit/src/orion/simd_field_agg_impl.rs new file mode 100644 index 00000000..ff9375ef --- /dev/null +++ b/poly_commit/src/orion/simd_field_agg_impl.rs @@ -0,0 +1,192 @@ +use std::iter; + +use arith::{Field, SimdField}; +use gkr_field_config::GKRFieldConfig; +use polynomials::{EqPolynomial, MultiLinearPoly}; +use transcript::Transcript; + +use crate::{ + orion::utils::*, traits::TensorCodeIOPPCS, ExpanderGKRChallenge, OrionCommitment, OrionProof, + OrionSRS, PCS_SOUNDNESS_BITS, +}; + +pub(crate) fn orion_verify_simd_field_aggregated<C, ComPackF, OpenPackF, T>( + mpi_world_size: usize, + vk: &OrionSRS, + commitment: &OrionCommitment, + eval_point: &ExpanderGKRChallenge<C>, + eval: C::ChallengeField, + transcript: &mut T, + proof: &OrionProof<C::ChallengeField>, +) -> bool +where + C: GKRFieldConfig, + ComPackF: SimdField<Scalar = C::CircuitField>, + OpenPackF: SimdField<Scalar = C::CircuitField>, + T: Transcript<C::ChallengeField>, +{ + let local_num_vars = eval_point.num_vars() - mpi_world_size.ilog2() as usize; + assert_eq!(local_num_vars, vk.num_vars); + + let (row_num, msg_size) = { + let (row_field_elems, msg_size) = OrionSRS::evals_shape::<C::CircuitField>(local_num_vars); + let row_num = row_field_elems / C::SimdCircuitField::PACK_SIZE; + (row_num, msg_size) + }; + + let num_vars_in_local_rows = row_num.ilog2() as usize; + let num_vars_in_unpacked_msg = local_num_vars - num_vars_in_local_rows; + let local_xs = eval_point.local_xs(); + + // NOTE: working on evaluation response + let mut scratch = vec![C::ChallengeField::ZERO; msg_size * C::SimdCircuitField::PACK_SIZE]; + let final_eval = MultiLinearPoly::evaluate_with_buffer( + &proof.eval_row, + &local_xs[..num_vars_in_unpacked_msg], + &mut scratch, + ); + if final_eval != eval { + return false; + } + + // NOTE: working on proximity responses, draw random linear combinations + // then draw query points from fiat shamir transcripts + let proximity_reps = vk.proximity_repetitions::<C::ChallengeField>(PCS_SOUNDNESS_BITS); + let proximity_coeffs: Vec<Vec<C::ChallengeField>> = (0..proximity_reps) + .map(|_| transcript.generate_challenge_field_elements(row_num)) + .collect(); + let query_num = vk.query_complexity(PCS_SOUNDNESS_BITS); + let query_indices = transcript.generate_challenge_index_vector(query_num); + + // NOTE: check all merkle paths, and check merkle roots against commitment + let (mt_verify_res, roots): (Vec<_>, Vec<_>) = proof + .query_openings + .chunks(query_num) + .map(|queries| { + let root = queries[0].root(); + (orion_mt_verify(vk, &query_indices, queries, &root), root) + }) + .unzip(); + + if !mt_verify_res.iter().all(|v| *v) { + return false; + } + + let final_tree_height = 1 + roots.len().ilog2(); + let (internals, _) = tree::Tree::new_with_leaf_nodes(roots, final_tree_height); + if internals[0] != *commitment { + return false; + } + + // NOTE: prepare the interleaved alphabets from the MT paths, + // but reshuffle the packed elements into another direction + let mut scratch = vec![C::CircuitField::ZERO; C::SimdCircuitField::PACK_SIZE * row_num]; + let shuffled_interleaved_alphabet: Vec<_> = proof + .query_openings + .iter() + .map(|c| -> Vec<_> { + let mut elts = c.unpack_field_elems::<C::CircuitField, ComPackF>(); + transpose_in_place(&mut elts, &mut scratch, row_num); + elts.chunks(OpenPackF::PACK_SIZE) + .map(OpenPackF::pack) + .collect() + }) + .collect(); + + // NOTE: compute alphabets from proximity/evaluation coeffs + let table_num = row_num / OpenPackF::PACK_SIZE; + let mut luts = SubsetSumLUTs::<C::ChallengeField>::new(OpenPackF::PACK_SIZE, table_num); + assert_eq!(row_num % OpenPackF::PACK_SIZE, 0); + + let eq_local_coeffs = EqPolynomial::build_eq_x_r(&local_xs[num_vars_in_unpacked_msg..]); + luts.build(&eq_local_coeffs); + + let mut scratch = + vec![C::ChallengeField::ZERO; mpi_world_size * C::SimdCircuitField::PACK_SIZE * msg_size]; + + let mut eval_qs: Vec<_> = shuffled_interleaved_alphabet + .iter() + .flat_map(|c| -> Vec<_> { + c.chunks(table_num) + .map(|ts| luts.lookup_and_sum(ts)) + .collect() + }) + .collect(); + transpose_in_place(&mut eval_qs, &mut scratch, mpi_world_size); + + let proximity_qs: Vec<_> = proximity_coeffs + .iter() + .map(|ps| { + luts.build(ps); + let mut worlds_proximity_resps: Vec<_> = shuffled_interleaved_alphabet + .iter() + .flat_map(|c| -> Vec<_> { + c.chunks(table_num) + .map(|ts| luts.lookup_and_sum(ts)) + .collect() + }) + .collect(); + transpose_in_place(&mut worlds_proximity_resps, &mut scratch, mpi_world_size); + worlds_proximity_resps + }) + .collect(); + + // NOTE: sum up each worlds responses with weights + let eq_worlds_coeffs = EqPolynomial::build_eq_x_r(&eval_point.x_mpi); + let actual_evals: Vec<C::ChallengeField> = eval_qs + .chunks(mpi_world_size) + .map(|rs| inner_prod(rs, &eq_worlds_coeffs)) + .collect(); + + let actual_proximity_resps: Vec<Vec<C::ChallengeField>> = proximity_qs + .iter() + .map(|ps| { + let weights = transcript.generate_challenge_field_elements(mpi_world_size); + ps.chunks(mpi_world_size) + .map(|rs| inner_prod(rs, &weights)) + .collect() + }) + .collect(); + + // NOTE: decide if expected alphabet matches actual responses + let mut scratch_msg = vec![C::ChallengeField::ZERO; C::SimdCircuitField::PACK_SIZE * msg_size]; + let mut scratch_codeword = + vec![C::ChallengeField::ZERO; C::SimdCircuitField::PACK_SIZE * vk.codeword_len()]; + actual_proximity_resps + .iter() + .zip(proof.proximity_rows.iter()) + .chain(iter::once((&actual_evals, &proof.eval_row))) + .all(|(actual_alphabets, msg)| { + let mut msg_cloned = msg.clone(); + transpose_in_place(&mut msg_cloned, &mut scratch_msg, msg_size); + let mut codeword: Vec<_> = msg_cloned + .chunks(msg_size) + .flat_map(|m| vk.code_instance.encode(m).unwrap()) + .collect(); + transpose_in_place( + &mut codeword, + &mut scratch_codeword, + C::SimdCircuitField::PACK_SIZE, + ); + + query_indices + .iter() + .zip(actual_alphabets.chunks(C::SimdCircuitField::PACK_SIZE)) + .all(|(qi, simd_alphabets)| { + let index = qi % vk.codeword_len(); + + let simd_starts = index * C::SimdCircuitField::PACK_SIZE; + let simd_ends = (index + 1) * C::SimdCircuitField::PACK_SIZE; + + codeword[simd_starts..simd_ends] + .iter() + .zip(simd_alphabets.iter()) + .all(|(ec, ac)| ec == ac) + }) + }) +} + +#[inline] +fn inner_prod<F: Field>(ls: &[F], rs: &[F]) -> F { + ls.iter().zip(rs.iter()).map(|(&l, &r)| r * l).sum() +} diff --git a/poly_commit/src/orion/simd_field_agg_tests.rs b/poly_commit/src/orion/simd_field_agg_tests.rs new file mode 100644 index 00000000..d2548d59 --- /dev/null +++ b/poly_commit/src/orion/simd_field_agg_tests.rs @@ -0,0 +1,2 @@ +// TODO ... separate a large poly into parts, then separately commit/open, +// finally aggregate and verify From 41fb6540189335feabada491b37f1fb6ada3b161 Mon Sep 17 00:00:00 2001 From: Hang Su <tonyfloater@gmail.com> Date: Sat, 30 Nov 2024 12:55:19 -0500 Subject: [PATCH 31/65] minor, refactoring the prototype as a personal review --- Cargo.lock | 1 + poly_commit/Cargo.toml | 1 + poly_commit/src/orion/simd_field_agg_impl.rs | 173 +++++++++---------- 3 files changed, 82 insertions(+), 93 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e203cd90..8d849ab0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1799,6 +1799,7 @@ dependencies = [ "gf2", "gf2_128", "gkr_field_config", + "itertools 0.13.0", "mersenne31", "mpi_config", "polynomials", diff --git a/poly_commit/Cargo.toml b/poly_commit/Cargo.toml index e69708f2..988c9002 100644 --- a/poly_commit/Cargo.toml +++ b/poly_commit/Cargo.toml @@ -14,6 +14,7 @@ tree = { path = "../tree" } rand.workspace = true ark-std.workspace = true thiserror.workspace = true +itertools.workspace = true [dev-dependencies] gf2 = { path = "../arith/gf2" } diff --git a/poly_commit/src/orion/simd_field_agg_impl.rs b/poly_commit/src/orion/simd_field_agg_impl.rs index ff9375ef..c917f0cb 100644 --- a/poly_commit/src/orion/simd_field_agg_impl.rs +++ b/poly_commit/src/orion/simd_field_agg_impl.rs @@ -2,6 +2,7 @@ use std::iter; use arith::{Field, SimdField}; use gkr_field_config::GKRFieldConfig; +use itertools::izip; use polynomials::{EqPolynomial, MultiLinearPoly}; use transcript::Transcript; @@ -38,12 +39,16 @@ where let num_vars_in_unpacked_msg = local_num_vars - num_vars_in_local_rows; let local_xs = eval_point.local_xs(); + let eq_local_coeffs = EqPolynomial::build_eq_x_r(&local_xs[num_vars_in_unpacked_msg..]); + let eq_worlds_coeffs = EqPolynomial::build_eq_x_r(&eval_point.x_mpi); + // NOTE: working on evaluation response - let mut scratch = vec![C::ChallengeField::ZERO; msg_size * C::SimdCircuitField::PACK_SIZE]; + let mut scratch = + vec![C::ChallengeField::ZERO; mpi_world_size * C::SimdCircuitField::PACK_SIZE * msg_size]; let final_eval = MultiLinearPoly::evaluate_with_buffer( &proof.eval_row, &local_xs[..num_vars_in_unpacked_msg], - &mut scratch, + &mut scratch[..C::SimdCircuitField::PACK_SIZE * msg_size], ); if final_eval != eval { return false; @@ -52,14 +57,19 @@ where // NOTE: working on proximity responses, draw random linear combinations // then draw query points from fiat shamir transcripts let proximity_reps = vk.proximity_repetitions::<C::ChallengeField>(PCS_SOUNDNESS_BITS); - let proximity_coeffs: Vec<Vec<C::ChallengeField>> = (0..proximity_reps) + let proximity_local_coeffs: Vec<Vec<C::ChallengeField>> = (0..proximity_reps) .map(|_| transcript.generate_challenge_field_elements(row_num)) .collect(); + let query_num = vk.query_complexity(PCS_SOUNDNESS_BITS); let query_indices = transcript.generate_challenge_index_vector(query_num); + let proximity_worlds_coeffs: Vec<Vec<C::ChallengeField>> = (0..proximity_reps) + .map(|_| transcript.generate_challenge_field_elements(mpi_world_size)) + .collect(); + // NOTE: check all merkle paths, and check merkle roots against commitment - let (mt_verify_res, roots): (Vec<_>, Vec<_>) = proof + let (mt_verifications, roots): (Vec<_>, Vec<_>) = proof .query_openings .chunks(query_num) .map(|queries| { @@ -68,7 +78,7 @@ where }) .unzip(); - if !mt_verify_res.iter().all(|v| *v) { + if !mt_verifications.iter().all(|v| *v) { return false; } @@ -80,110 +90,87 @@ where // NOTE: prepare the interleaved alphabets from the MT paths, // but reshuffle the packed elements into another direction - let mut scratch = vec![C::CircuitField::ZERO; C::SimdCircuitField::PACK_SIZE * row_num]; + let mut scratch_f = vec![C::CircuitField::ZERO; C::SimdCircuitField::PACK_SIZE * row_num]; let shuffled_interleaved_alphabet: Vec<_> = proof .query_openings .iter() .map(|c| -> Vec<_> { let mut elts = c.unpack_field_elems::<C::CircuitField, ComPackF>(); - transpose_in_place(&mut elts, &mut scratch, row_num); + transpose_in_place(&mut elts, &mut scratch_f, row_num); elts.chunks(OpenPackF::PACK_SIZE) .map(OpenPackF::pack) .collect() }) .collect(); - // NOTE: compute alphabets from proximity/evaluation coeffs + // NOTE: decide if expected alphabet matches actual responses let table_num = row_num / OpenPackF::PACK_SIZE; let mut luts = SubsetSumLUTs::<C::ChallengeField>::new(OpenPackF::PACK_SIZE, table_num); assert_eq!(row_num % OpenPackF::PACK_SIZE, 0); - let eq_local_coeffs = EqPolynomial::build_eq_x_r(&local_xs[num_vars_in_unpacked_msg..]); - luts.build(&eq_local_coeffs); - - let mut scratch = - vec![C::ChallengeField::ZERO; mpi_world_size * C::SimdCircuitField::PACK_SIZE * msg_size]; - - let mut eval_qs: Vec<_> = shuffled_interleaved_alphabet - .iter() - .flat_map(|c| -> Vec<_> { - c.chunks(table_num) - .map(|ts| luts.lookup_and_sum(ts)) - .collect() - }) - .collect(); - transpose_in_place(&mut eval_qs, &mut scratch, mpi_world_size); - - let proximity_qs: Vec<_> = proximity_coeffs - .iter() - .map(|ps| { - luts.build(ps); - let mut worlds_proximity_resps: Vec<_> = shuffled_interleaved_alphabet - .iter() - .flat_map(|c| -> Vec<_> { - c.chunks(table_num) - .map(|ts| luts.lookup_and_sum(ts)) - .collect() - }) - .collect(); - transpose_in_place(&mut worlds_proximity_resps, &mut scratch, mpi_world_size); - worlds_proximity_resps - }) - .collect(); - - // NOTE: sum up each worlds responses with weights - let eq_worlds_coeffs = EqPolynomial::build_eq_x_r(&eval_point.x_mpi); - let actual_evals: Vec<C::ChallengeField> = eval_qs - .chunks(mpi_world_size) - .map(|rs| inner_prod(rs, &eq_worlds_coeffs)) - .collect(); - - let actual_proximity_resps: Vec<Vec<C::ChallengeField>> = proximity_qs - .iter() - .map(|ps| { - let weights = transcript.generate_challenge_field_elements(mpi_world_size); - ps.chunks(mpi_world_size) - .map(|rs| inner_prod(rs, &weights)) - .collect() - }) - .collect(); - - // NOTE: decide if expected alphabet matches actual responses - let mut scratch_msg = vec![C::ChallengeField::ZERO; C::SimdCircuitField::PACK_SIZE * msg_size]; - let mut scratch_codeword = + let mut codeword = vec![C::ChallengeField::ZERO; C::SimdCircuitField::PACK_SIZE * vk.codeword_len()]; - actual_proximity_resps - .iter() - .zip(proof.proximity_rows.iter()) - .chain(iter::once((&actual_evals, &proof.eval_row))) - .all(|(actual_alphabets, msg)| { - let mut msg_cloned = msg.clone(); - transpose_in_place(&mut msg_cloned, &mut scratch_msg, msg_size); - let mut codeword: Vec<_> = msg_cloned - .chunks(msg_size) - .flat_map(|m| vk.code_instance.encode(m).unwrap()) - .collect(); - transpose_in_place( - &mut codeword, - &mut scratch_codeword, - C::SimdCircuitField::PACK_SIZE, - ); - - query_indices - .iter() - .zip(actual_alphabets.chunks(C::SimdCircuitField::PACK_SIZE)) - .all(|(qi, simd_alphabets)| { - let index = qi % vk.codeword_len(); - - let simd_starts = index * C::SimdCircuitField::PACK_SIZE; - let simd_ends = (index + 1) * C::SimdCircuitField::PACK_SIZE; - - codeword[simd_starts..simd_ends] - .iter() - .zip(simd_alphabets.iter()) - .all(|(ec, ac)| ec == ac) - }) - }) + + izip!( + &proximity_local_coeffs, + &proximity_worlds_coeffs, + &proof.proximity_rows + ) + .chain(iter::once(( + &eq_local_coeffs, + &eq_worlds_coeffs, + &proof.eval_row, + ))) + .all(|(local_coeffs, worlds_coeffs, msg)| { + // NOTE: compute final actual alphabets cross worlds + luts.build(local_coeffs); + let mut each_world_alphabets: Vec<_> = shuffled_interleaved_alphabet + .iter() + .flat_map(|c| -> Vec<_> { + c.chunks(table_num) + .map(|ts| luts.lookup_and_sum(ts)) + .collect() + }) + .collect(); + transpose_in_place(&mut each_world_alphabets, &mut scratch, mpi_world_size); + let actual_alphabets: Vec<_> = each_world_alphabets + .chunks(mpi_world_size) + .map(|rs| inner_prod(rs, worlds_coeffs)) + .collect(); + + // NOTE: compute SIMD codewords from the message + let mut msg_cloned = msg.clone(); + transpose_in_place( + &mut msg_cloned, + &mut scratch[..C::SimdCircuitField::PACK_SIZE * msg_size], + msg_size, + ); + msg_cloned + .chunks(msg_size) + .zip(codeword.chunks_mut(vk.codeword_len())) + .for_each(|(msg, c)| vk.code_instance.encode_in_place(msg, c).unwrap()); + transpose_in_place( + &mut codeword, + &mut scratch[..C::SimdCircuitField::PACK_SIZE * vk.codeword_len()], + C::SimdCircuitField::PACK_SIZE, + ); + + // NOTE: check actual SIMD alphabets against expected SIMD alphabets + query_indices + .iter() + .zip(actual_alphabets.chunks(C::SimdCircuitField::PACK_SIZE)) + .all(|(qi, actual_alphabets)| { + let index = qi % vk.codeword_len(); + + let simd_starts = index * C::SimdCircuitField::PACK_SIZE; + let simd_ends = (index + 1) * C::SimdCircuitField::PACK_SIZE; + + codeword[simd_starts..simd_ends] + .iter() + .zip(actual_alphabets.iter()) + .all(|(ec, ac)| ec == ac) + }) + }) } #[inline] From d436436c91823ad8b90fbc52a6890590c10a3ca9 Mon Sep 17 00:00:00 2001 From: Hang Su <tonyfloater@gmail.com> Date: Sat, 30 Nov 2024 21:24:00 -0500 Subject: [PATCH 32/65] aggregate then verify test complete, tweaking mpi environment --- config/mpi_config/src/lib.rs | 4 +- poly_commit/src/orion/base_field_impl.rs | 23 +- poly_commit/src/orion/pcs_for_expander_gkr.rs | 2 +- poly_commit/src/orion/simd_field_agg_impl.rs | 44 ++-- poly_commit/src/orion/simd_field_agg_tests.rs | 196 +++++++++++++++++- poly_commit/src/orion/simd_field_impl.rs | 40 ++-- poly_commit/src/orion/utils.rs | 34 ++- poly_commit/src/traits.rs | 2 +- tree/src/path.rs | 46 ++-- 9 files changed, 292 insertions(+), 99 deletions(-) diff --git a/config/mpi_config/src/lib.rs b/config/mpi_config/src/lib.rs index 02716ba7..38eadd07 100644 --- a/config/mpi_config/src/lib.rs +++ b/config/mpi_config/src/lib.rs @@ -202,11 +202,11 @@ impl MPIConfig { /// Root process broadcase a value f into all the processes #[inline] - pub fn root_broadcast_f<F: Sized + Clone>(&self, f: &mut F) { + pub fn root_broadcast_f<F: Field>(&self, f: &mut F) { unsafe { if self.world_size == 1 { } else { - let mut vec_u8 = Self::elem_to_u8_bytes(f, size_of::<F>()); + let mut vec_u8 = Self::elem_to_u8_bytes(f, F::SIZE); self.root_process().broadcast_into(&mut vec_u8); vec_u8.leak(); } diff --git a/poly_commit/src/orion/base_field_impl.rs b/poly_commit/src/orion/base_field_impl.rs index 5edeeeca..c336784e 100644 --- a/poly_commit/src/orion/base_field_impl.rs +++ b/poly_commit/src/orion/base_field_impl.rs @@ -1,6 +1,7 @@ use std::iter; use arith::{ExtensionField, Field, SimdField}; +use itertools::izip; use polynomials::{EqPolynomial, MultiLinearPoly}; use transcript::Transcript; @@ -93,9 +94,7 @@ where let eq_col_coeffs = EqPolynomial::build_eq_x_r(&point[point.len() - num_vars_in_row..]); luts.build(&eq_col_coeffs); - packed_evals - .chunks(table_num) - .zip(eval_row.iter_mut()) + izip!(packed_evals.chunks(table_num), &mut eval_row) .for_each(|(p_col, res)| *res = luts.lookup_and_sum(p_col)); // NOTE: draw random linear combination out @@ -107,9 +106,7 @@ where let random_coeffs = transcript.generate_challenge_field_elements(row_num); luts.build(&random_coeffs); - packed_evals - .chunks(table_num) - .zip(row_buffer.iter_mut()) + izip!(packed_evals.chunks(table_num), row_buffer) .for_each(|(p_col, res)| *res = luts.lookup_and_sum(p_col)); }); drop(luts); @@ -200,9 +197,8 @@ where assert_eq!(row_num % OpenPackF::PACK_SIZE, 0); let eq_linear_combination = EqPolynomial::build_eq_x_r(&point[num_vars_in_msg..]); - random_linear_combinations - .iter() - .zip(proof.proximity_rows.iter()) + + izip!(&random_linear_combinations, &proof.proximity_rows) .chain(iter::once((&eq_linear_combination, &proof.eval_row))) .all(|(rl, msg)| { let codeword = match vk.code_instance.encode(msg) { @@ -212,13 +208,12 @@ where luts.build(rl); - query_indices - .iter() - .zip(packed_interleaved_alphabets.iter()) - .all(|(&qi, interleaved_alphabet)| { + izip!(&query_indices, &packed_interleaved_alphabets).all( + |(qi, interleaved_alphabet)| { let index = qi % vk.codeword_len(); let alphabet = luts.lookup_and_sum(interleaved_alphabet); alphabet == codeword[index] - }) + }, + ) }) } diff --git a/poly_commit/src/orion/pcs_for_expander_gkr.rs b/poly_commit/src/orion/pcs_for_expander_gkr.rs index 54400878..5ad631c9 100644 --- a/poly_commit/src/orion/pcs_for_expander_gkr.rs +++ b/poly_commit/src/orion/pcs_for_expander_gkr.rs @@ -42,7 +42,7 @@ where mpi_config: &MPIConfig, rng: impl rand::RngCore, ) -> Self::SRS { - let num_vars_each_core = *params - mpi_config.world_size(); + let num_vars_each_core = *params - mpi_config.world_size().ilog2() as usize; OrionSRS::from_random::<C::CircuitField>( num_vars_each_core, ORION_CODE_PARAMETER_INSTANCE, diff --git a/poly_commit/src/orion/simd_field_agg_impl.rs b/poly_commit/src/orion/simd_field_agg_impl.rs index c917f0cb..435c1df0 100644 --- a/poly_commit/src/orion/simd_field_agg_impl.rs +++ b/poly_commit/src/orion/simd_field_agg_impl.rs @@ -78,7 +78,7 @@ where }) .unzip(); - if !mt_verifications.iter().all(|v| *v) { + if !itertools::all(&mt_verifications, |v| *v) { return false; } @@ -108,6 +108,8 @@ where let mut luts = SubsetSumLUTs::<C::ChallengeField>::new(OpenPackF::PACK_SIZE, table_num); assert_eq!(row_num % OpenPackF::PACK_SIZE, 0); + let mut scratch_q = + vec![C::ChallengeField::ZERO; mpi_world_size * C::SimdCircuitField::PACK_SIZE * query_num]; let mut codeword = vec![C::ChallengeField::ZERO; C::SimdCircuitField::PACK_SIZE * vk.codeword_len()]; @@ -132,10 +134,10 @@ where .collect() }) .collect(); - transpose_in_place(&mut each_world_alphabets, &mut scratch, mpi_world_size); + transpose_in_place(&mut each_world_alphabets, &mut scratch_q, mpi_world_size); let actual_alphabets: Vec<_> = each_world_alphabets .chunks(mpi_world_size) - .map(|rs| inner_prod(rs, worlds_coeffs)) + .map(|rs| izip!(rs, worlds_coeffs).map(|(&l, &r)| l * r).sum()) .collect(); // NOTE: compute SIMD codewords from the message @@ -145,10 +147,11 @@ where &mut scratch[..C::SimdCircuitField::PACK_SIZE * msg_size], msg_size, ); - msg_cloned - .chunks(msg_size) - .zip(codeword.chunks_mut(vk.codeword_len())) - .for_each(|(msg, c)| vk.code_instance.encode_in_place(msg, c).unwrap()); + izip!( + msg_cloned.chunks(msg_size), + codeword.chunks_mut(vk.codeword_len()) + ) + .for_each(|(msg, c)| vk.code_instance.encode_in_place(msg, c).unwrap()); transpose_in_place( &mut codeword, &mut scratch[..C::SimdCircuitField::PACK_SIZE * vk.codeword_len()], @@ -156,24 +159,17 @@ where ); // NOTE: check actual SIMD alphabets against expected SIMD alphabets - query_indices - .iter() - .zip(actual_alphabets.chunks(C::SimdCircuitField::PACK_SIZE)) - .all(|(qi, actual_alphabets)| { - let index = qi % vk.codeword_len(); + izip!( + &query_indices, + actual_alphabets.chunks(C::SimdCircuitField::PACK_SIZE) + ) + .all(|(qi, actual_alphabets)| { + let index = qi % vk.codeword_len(); - let simd_starts = index * C::SimdCircuitField::PACK_SIZE; - let simd_ends = (index + 1) * C::SimdCircuitField::PACK_SIZE; + let simd_starts = index * C::SimdCircuitField::PACK_SIZE; + let simd_ends = (index + 1) * C::SimdCircuitField::PACK_SIZE; - codeword[simd_starts..simd_ends] - .iter() - .zip(actual_alphabets.iter()) - .all(|(ec, ac)| ec == ac) - }) + izip!(&codeword[simd_starts..simd_ends], actual_alphabets).all(|(ec, ac)| ec == ac) + }) }) } - -#[inline] -fn inner_prod<F: Field>(ls: &[F], rs: &[F]) -> F { - ls.iter().zip(rs.iter()).map(|(&l, &r)| r * l).sum() -} diff --git a/poly_commit/src/orion/simd_field_agg_tests.rs b/poly_commit/src/orion/simd_field_agg_tests.rs index d2548d59..c62bdfed 100644 --- a/poly_commit/src/orion/simd_field_agg_tests.rs +++ b/poly_commit/src/orion/simd_field_agg_tests.rs @@ -1,2 +1,194 @@ -// TODO ... separate a large poly into parts, then separately commit/open, -// finally aggregate and verify +use std::marker::PhantomData; + +use arith::{ExtensionField, Field, SimdField}; +use ark_std::test_rng; +use gf2::{GF2x128, GF2x8}; +use gf2_128::GF2_128; +use gkr_field_config::{GF2ExtConfig, GKRFieldConfig}; +use itertools::izip; +use polynomials::{EqPolynomial, MultiLinearPoly}; +use simd_field_agg_impl::orion_verify_simd_field_aggregated; +use transcript::{BytesHashTranscript, Keccak256hasher, Transcript}; +use utils::transpose_in_place; + +use crate::{orion::*, traits::TensorCodeIOPPCS, ExpanderGKRChallenge, PCS_SOUNDNESS_BITS}; + +#[derive(Clone)] +struct DistributedCommitter<F, EvalF, ComPackF, T> +where + F: Field, + EvalF: ExtensionField<BaseField = F>, + ComPackF: SimdField<Scalar = F>, + T: Transcript<EvalF>, +{ + pub scratch_pad: OrionScratchPad<F, ComPackF>, + pub transcript: T, + + _phantom: PhantomData<EvalF>, +} + +fn test_orion_simd_aggregate_verify_helper<C, ComPackF, OpenPackF, T>( + num_parties: usize, + num_vars: usize, +) where + C: GKRFieldConfig, + ComPackF: SimdField<Scalar = C::CircuitField>, + OpenPackF: SimdField<Scalar = C::CircuitField>, + T: Transcript<C::ChallengeField>, +{ + assert!(num_parties.is_power_of_two()); + + let mut rng = test_rng(); + + let simd_num_vars = C::SimdCircuitField::PACK_SIZE.ilog2() as usize; + let world_num_vars = num_parties.ilog2() as usize; + + let num_vars_in_unpacked_msg = { + let (row_field_elems, _) = + OrionSRS::evals_shape::<C::CircuitField>(num_vars - world_num_vars); + let row_num = row_field_elems / C::SimdCircuitField::PACK_SIZE; + let num_vars_in_row = row_num.ilog2() as usize; + num_vars - world_num_vars - num_vars_in_row + }; + + let global_poly = + MultiLinearPoly::<C::SimdCircuitField>::random(num_vars - simd_num_vars, &mut rng); + + let global_real_num_vars = global_poly.get_num_vars(); + let local_real_num_vars = global_real_num_vars - world_num_vars; + + let eval_point: Vec<_> = (0..num_vars) + .map(|_| C::ChallengeField::random_unsafe(&mut rng)) + .collect(); + + let gkr_challenge: ExpanderGKRChallenge<C> = ExpanderGKRChallenge { + x_mpi: eval_point[num_vars - world_num_vars..].to_vec(), + x: eval_point[simd_num_vars..num_vars - world_num_vars].to_vec(), + x_simd: eval_point[..simd_num_vars].to_vec(), + }; + + let mut committee = vec![ + DistributedCommitter { + scratch_pad: OrionScratchPad::<C::CircuitField, ComPackF>::default(), + transcript: T::new(), + _phantom: PhantomData, + }; + num_parties + ]; + let mut verifier_transcript = T::new(); + + let srs = OrionSRS::from_random::<C::CircuitField>( + num_vars - world_num_vars, + ORION_CODE_PARAMETER_INSTANCE, + &mut rng, + ); + + let final_commitment = { + let roots: Vec<_> = izip!( + &mut committee, + global_poly.coeffs.chunks(1 << local_real_num_vars) + ) + .map(|(committer, eval_slice)| { + let cloned_poly = MultiLinearPoly::new(eval_slice.to_vec()); + orion_commit_simd_field(&srs, &cloned_poly, &mut committer.scratch_pad).unwrap() + }) + .collect(); + + let final_tree_height = 1 + roots.len().ilog2(); + let (internals, _) = tree::Tree::new_with_leaf_nodes(roots, final_tree_height); + internals[0] + }; + + let openings: Vec<_> = izip!( + &mut committee, + global_poly.coeffs.chunks(1 << local_real_num_vars) + ) + .map(|(committer, eval_slice)| { + let cloned_poly = MultiLinearPoly::new(eval_slice.to_vec()); + orion_open_simd_field::< + C::CircuitField, + C::SimdCircuitField, + C::ChallengeField, + ComPackF, + OpenPackF, + T, + >( + &srs, + &cloned_poly, + &gkr_challenge.local_xs(), + &mut committer.transcript, + &committer.scratch_pad, + ) + }) + .collect(); + + let paths: Vec<_> = openings + .iter() + .flat_map(|o| o.query_openings.clone()) + .collect(); + + let mut aggregator_transcript = committee[0].transcript.clone(); + let proximity_reps = srs.proximity_repetitions::<C::ChallengeField>(PCS_SOUNDNESS_BITS); + let mut scratch = vec![C::ChallengeField::ZERO; num_parties * openings[0].eval_row.len()]; + + let aggregated_proximity_rows: Vec<Vec<C::ChallengeField>> = (0..proximity_reps) + .map(|i| { + let weights = aggregator_transcript.generate_challenge_field_elements(num_parties); + let mut rows: Vec<_> = openings + .iter() + .flat_map(|o| o.proximity_rows[i].clone()) + .collect(); + transpose_in_place(&mut rows, &mut scratch, num_parties); + rows.chunks(num_parties) + .map(|c| izip!(c, &weights).map(|(&l, &r)| l * r).sum()) + .collect() + }) + .collect(); + + let aggregated_eval_row: Vec<C::ChallengeField> = { + let eq_worlds_coeffs = EqPolynomial::build_eq_x_r(&gkr_challenge.x_mpi); + let mut rows: Vec<_> = openings.iter().flat_map(|o| o.eval_row.clone()).collect(); + transpose_in_place(&mut rows, &mut scratch, num_parties); + rows.chunks(num_parties) + .map(|c| izip!(c, &eq_worlds_coeffs).map(|(&l, &r)| l * r).sum()) + .collect() + }; + + let final_expected_eval = MultiLinearPoly::evaluate_with_buffer( + &aggregated_eval_row, + &gkr_challenge.local_xs()[..num_vars_in_unpacked_msg], + &mut scratch[..aggregated_eval_row.len()], + ); + + let agregated_proof = OrionProof { + eval_row: aggregated_eval_row, + proximity_rows: aggregated_proximity_rows, + query_openings: paths, + }; + + let res = orion_verify_simd_field_aggregated::<C, ComPackF, OpenPackF, T>( + num_parties, + &srs, + &final_commitment, + &gkr_challenge, + final_expected_eval, + &mut verifier_transcript, + &agregated_proof, + ); + + assert!(res); +} + +#[test] +fn test_orion_simd_aggregate_verify() { + let parties = 16; + + (25..30).for_each(|num_var| { + test_orion_simd_aggregate_verify_helper::< + GF2ExtConfig, + GF2x128, + GF2x8, + BytesHashTranscript<GF2_128, Keccak256hasher>, + >(parties, num_var) + }) +} diff --git a/poly_commit/src/orion/simd_field_impl.rs b/poly_commit/src/orion/simd_field_impl.rs index 2d67a1ba..c1951575 100644 --- a/poly_commit/src/orion/simd_field_impl.rs +++ b/poly_commit/src/orion/simd_field_impl.rs @@ -1,6 +1,7 @@ use std::iter; use arith::{ExtensionField, Field, SimdField}; +use itertools::izip; use polynomials::{EqPolynomial, MultiLinearPoly}; use transcript::Transcript; @@ -148,9 +149,7 @@ where let eq_coeffs = EqPolynomial::build_eq_x_r(&point[num_vars_in_unpacked_msg..]); luts.build(&eq_coeffs); - packed_shuffled_evals - .chunks(tables_num) - .zip(eval_row.iter_mut()) + izip!(packed_shuffled_evals.chunks(tables_num), &mut eval_row) .for_each(|(p_col, res)| *res = luts.lookup_and_sum(p_col)); // NOTE: draw random linear combination out @@ -163,9 +162,7 @@ where let random_coeffs = transcript.generate_challenge_field_elements(row_num); luts.build(&random_coeffs); - packed_shuffled_evals - .chunks(tables_num) - .zip(row_buffer.iter_mut()) + izip!(packed_shuffled_evals.chunks(tables_num), row_buffer) .for_each(|(p_col, res)| *res = luts.lookup_and_sum(p_col)); }); drop(luts); @@ -254,9 +251,8 @@ where let eq_linear_combination = EqPolynomial::build_eq_x_r(&point[num_vars_in_unpacked_msg..]); let mut scratch_msg = vec![EvalF::ZERO; SimdF::PACK_SIZE * msg_size]; let mut scratch_codeword = vec![EvalF::ZERO; SimdF::PACK_SIZE * vk.codeword_len()]; - random_linear_combinations - .iter() - .zip(proof.proximity_rows.iter()) + + izip!(&random_linear_combinations, &proof.proximity_rows) .chain(iter::once((&eq_linear_combination, &proof.eval_row))) .all(|(rl, msg)| { let mut msg_cloned = msg.clone(); @@ -269,18 +265,22 @@ where luts.build(rl); - query_indices - .iter() - .zip(shuffled_interleaved_alphabet.iter()) - .all(|(&qi, interleaved_alphabet)| { + izip!(&query_indices, &shuffled_interleaved_alphabet).all( + |(&qi, interleaved_alphabet)| { let index = qi % vk.codeword_len(); - (index * SimdF::PACK_SIZE..(index + 1) * SimdF::PACK_SIZE) - .zip(interleaved_alphabet.chunks(tables_num)) - .all(|(i, packed_index)| { - let alphabet = luts.lookup_and_sum(packed_index); - alphabet == codeword[i] - }) - }) + let simd_starts = index * SimdF::PACK_SIZE; + let simd_ends = (index + 1) * SimdF::PACK_SIZE; + + izip!( + &codeword[simd_starts..simd_ends], + interleaved_alphabet.chunks(tables_num) + ) + .all(|(expected_alphabet, packed_index)| { + let alphabet = luts.lookup_and_sum(packed_index); + alphabet == *expected_alphabet + }) + }, + ) }) } diff --git a/poly_commit/src/orion/utils.rs b/poly_commit/src/orion/utils.rs index 6e9dc53f..17757bcc 100644 --- a/poly_commit/src/orion/utils.rs +++ b/poly_commit/src/orion/utils.rs @@ -1,6 +1,7 @@ use std::marker::PhantomData; use arith::{ExtensionField, Field, FieldSerdeError, SimdField}; +use itertools::izip; use thiserror::Error; use transcript::Transcript; @@ -108,10 +109,11 @@ where { // NOTE: packed codeword buffer and encode over packed field let mut packed_interleaved_codewords = vec![PackF::ZERO; packed_rows * pk.codeword_len()]; - packed_evals - .chunks(msg_size) - .zip(packed_interleaved_codewords.chunks_mut(pk.codeword_len())) - .try_for_each(|(evals, codeword)| pk.code_instance.encode_in_place(evals, codeword))?; + izip!( + packed_evals.chunks(msg_size), + packed_interleaved_codewords.chunks_mut(pk.codeword_len()) + ) + .try_for_each(|(evals, codeword)| pk.code_instance.encode_in_place(evals, codeword))?; // NOTE: transpose codeword s.t., the matrix has codewords being columns let mut scratch = vec![PackF::ZERO; packed_rows * pk.codeword_len()]; @@ -171,13 +173,10 @@ pub(crate) fn orion_mt_verify( root: &OrionCommitment, ) -> bool { let leaves_in_range_opening = OrionSRS::LEAVES_IN_RANGE_OPENING; - query_indices - .iter() - .zip(range_openings.iter()) - .all(|(&qi, range_path)| { - let index = qi % vk.codeword_len(); - range_path.verify(root) && index == range_path.left / leaves_in_range_opening - }) + izip!(query_indices, range_openings).all(|(&qi, range_path)| { + let index = qi % vk.codeword_len(); + range_path.verify(root) && index == range_path.left / leaves_in_range_opening + }) } /* @@ -238,10 +237,8 @@ impl<F: Field> SubsetSumLUTs<F> { self.tables.iter_mut().for_each(|lut| lut.fill(F::ZERO)); // NOTE: we are assuming that the table is for {0, 1}-linear combination - self.tables - .iter_mut() - .zip(weights.chunks(self.entry_bits)) - .for_each(|(lut_i, sub_weights)| { + izip!(&mut self.tables, weights.chunks(self.entry_bits)).for_each( + |(lut_i, sub_weights)| { sub_weights.iter().enumerate().for_each(|(i, weight_i)| { let bit_mask = 1 << (self.entry_bits - i - 1); lut_i.iter_mut().enumerate().for_each(|(bit_map, li)| { @@ -250,7 +247,8 @@ impl<F: Field> SubsetSumLUTs<F> { } }); }); - }); + }, + ); } #[inline] @@ -266,9 +264,7 @@ impl<F: Field> SubsetSumLUTs<F> { assert_eq!(EntryF::PACK_SIZE, self.entry_bits); assert_eq!(indices.len(), self.tables.len()); - self.tables - .iter() - .zip(indices.iter()) + izip!(&self.tables, indices) .map(|(t_i, index)| t_i[index.as_u32_unchecked() as usize]) .sum() } diff --git a/poly_commit/src/traits.rs b/poly_commit/src/traits.rs index b32277a4..6ce0616f 100644 --- a/poly_commit/src/traits.rs +++ b/poly_commit/src/traits.rs @@ -73,7 +73,7 @@ pub struct ExpanderGKRChallenge<C: GKRFieldConfig> { impl<C: GKRFieldConfig> ExpanderGKRChallenge<C> { pub fn local_xs(&self) -> Vec<C::ChallengeField> { - let mut local_xs = Vec::with_capacity(self.x_simd.len() + self.x.len()); + let mut local_xs = vec![C::ChallengeField::ZERO; self.x_simd.len() + self.x.len()]; local_xs[..self.x_simd.len()].copy_from_slice(&self.x_simd); local_xs[self.x_simd.len()..].copy_from_slice(&self.x); local_xs diff --git a/tree/src/path.rs b/tree/src/path.rs index ad244c6b..9df83c7f 100644 --- a/tree/src/path.rs +++ b/tree/src/path.rs @@ -83,7 +83,21 @@ impl Path { #[inline] pub fn root(&self) -> Node { - self.path_nodes[0] + let position_list = self.position_list().collect::<Vec<_>>(); + // let leaf_node = self.leaf.leaf_hash(hasher); + let leaf_node = self.leaf.leaf_hash(); + let mut current_node = leaf_node; + + // Traverse the path from leaf to root + for (i, node) in self.path_nodes.iter().rev().enumerate() { + if position_list[i] { + current_node = Node::node_hash(node, ¤t_node) + } else { + current_node = Node::node_hash(¤t_node, node) + }; + } + + current_node } #[inline] @@ -136,20 +150,6 @@ impl RangePath { #[inline] pub fn root(&self) -> Node { - self.path_nodes[0] - } - - #[inline] - pub fn unpack_field_elems<F, PackF>(&self) -> Vec<F> - where - F: Field, - PackF: SimdField<Scalar = F>, - { - unpack_field_elems_from_bytes::<F, PackF>(&self.leaves) - } - - #[inline] - pub fn verify(&self, root: &Node) -> bool { let sub_tree = Tree::new_with_leaves(self.leaves.clone()); let tree_height = sub_tree.height() + self.path_nodes.len(); @@ -172,6 +172,20 @@ impl RangePath { current_node_index = parent_index(current_node_index).unwrap(); }); - current_node == *root + current_node + } + + #[inline] + pub fn unpack_field_elems<F, PackF>(&self) -> Vec<F> + where + F: Field, + PackF: SimdField<Scalar = F>, + { + unpack_field_elems_from_bytes::<F, PackF>(&self.leaves) + } + + #[inline] + pub fn verify(&self, root: &Node) -> bool { + self.root() == *root } } From 1cc23483f8b13621a30f37df119a5ab02661ffce Mon Sep 17 00:00:00 2001 From: Hang Su <tonyfloater@gmail.com> Date: Sun, 1 Dec 2024 06:30:36 -0500 Subject: [PATCH 33/65] minor, refactoring by abstracting out aggregation method --- poly_commit/src/orion/simd_field_agg_impl.rs | 51 ++++++++++++++ poly_commit/src/orion/simd_field_agg_tests.rs | 69 ++++++------------- 2 files changed, 73 insertions(+), 47 deletions(-) diff --git a/poly_commit/src/orion/simd_field_agg_impl.rs b/poly_commit/src/orion/simd_field_agg_impl.rs index 435c1df0..b146a810 100644 --- a/poly_commit/src/orion/simd_field_agg_impl.rs +++ b/poly_commit/src/orion/simd_field_agg_impl.rs @@ -11,6 +11,57 @@ use crate::{ OrionSRS, PCS_SOUNDNESS_BITS, }; +#[allow(unused)] +pub(crate) fn orion_proof_aggregate<C, ComPackF, OpenPackF, T>( + openings: &[OrionProof<C::ChallengeField>], + x_mpi: &[C::ChallengeField], + transcript: &mut T, +) -> OrionProof<C::ChallengeField> +where + C: GKRFieldConfig, + ComPackF: SimdField<Scalar = C::CircuitField>, + OpenPackF: SimdField<Scalar = C::CircuitField>, + T: Transcript<C::ChallengeField>, +{ + let paths = openings + .iter() + .flat_map(|o| o.query_openings.clone()) + .collect(); + let num_parties = 1 << x_mpi.len(); + + let proximity_reps = openings[0].proximity_rows.len(); + let mut scratch = vec![C::ChallengeField::ZERO; num_parties * openings[0].eval_row.len()]; + + let aggregated_proximity_rows = (0..proximity_reps) + .map(|i| { + let weights = transcript.generate_challenge_field_elements(num_parties); + let mut rows: Vec<_> = openings + .iter() + .flat_map(|o| o.proximity_rows[i].clone()) + .collect(); + transpose_in_place(&mut rows, &mut scratch, num_parties); + rows.chunks(num_parties) + .map(|c| izip!(c, &weights).map(|(&l, &r)| l * r).sum()) + .collect() + }) + .collect(); + + let aggregated_eval_row: Vec<_> = { + let eq_worlds_coeffs = EqPolynomial::build_eq_x_r(x_mpi); + let mut rows: Vec<_> = openings.iter().flat_map(|o| o.eval_row.clone()).collect(); + transpose_in_place(&mut rows, &mut scratch, num_parties); + rows.chunks(num_parties) + .map(|c| izip!(c, &eq_worlds_coeffs).map(|(&l, &r)| l * r).sum()) + .collect() + }; + + OrionProof { + eval_row: aggregated_eval_row, + proximity_rows: aggregated_proximity_rows, + query_openings: paths, + } +} + pub(crate) fn orion_verify_simd_field_aggregated<C, ComPackF, OpenPackF, T>( mpi_world_size: usize, vk: &OrionSRS, diff --git a/poly_commit/src/orion/simd_field_agg_tests.rs b/poly_commit/src/orion/simd_field_agg_tests.rs index c62bdfed..3ddfc015 100644 --- a/poly_commit/src/orion/simd_field_agg_tests.rs +++ b/poly_commit/src/orion/simd_field_agg_tests.rs @@ -6,12 +6,14 @@ use gf2::{GF2x128, GF2x8}; use gf2_128::GF2_128; use gkr_field_config::{GF2ExtConfig, GKRFieldConfig}; use itertools::izip; -use polynomials::{EqPolynomial, MultiLinearPoly}; -use simd_field_agg_impl::orion_verify_simd_field_aggregated; +use polynomials::MultiLinearPoly; use transcript::{BytesHashTranscript, Keccak256hasher, Transcript}; -use utils::transpose_in_place; -use crate::{orion::*, traits::TensorCodeIOPPCS, ExpanderGKRChallenge, PCS_SOUNDNESS_BITS}; +use crate::{ + orion::{simd_field_agg_impl::*, *}, + traits::TensorCodeIOPPCS, + ExpanderGKRChallenge, +}; #[derive(Clone)] struct DistributedCommitter<F, EvalF, ComPackF, T> @@ -122,61 +124,34 @@ fn test_orion_simd_aggregate_verify_helper<C, ComPackF, OpenPackF, T>( }) .collect(); - let paths: Vec<_> = openings - .iter() - .flat_map(|o| o.query_openings.clone()) - .collect(); - let mut aggregator_transcript = committee[0].transcript.clone(); - let proximity_reps = srs.proximity_repetitions::<C::ChallengeField>(PCS_SOUNDNESS_BITS); - let mut scratch = vec![C::ChallengeField::ZERO; num_parties * openings[0].eval_row.len()]; - - let aggregated_proximity_rows: Vec<Vec<C::ChallengeField>> = (0..proximity_reps) - .map(|i| { - let weights = aggregator_transcript.generate_challenge_field_elements(num_parties); - let mut rows: Vec<_> = openings - .iter() - .flat_map(|o| o.proximity_rows[i].clone()) - .collect(); - transpose_in_place(&mut rows, &mut scratch, num_parties); - rows.chunks(num_parties) - .map(|c| izip!(c, &weights).map(|(&l, &r)| l * r).sum()) - .collect() - }) - .collect(); - - let aggregated_eval_row: Vec<C::ChallengeField> = { - let eq_worlds_coeffs = EqPolynomial::build_eq_x_r(&gkr_challenge.x_mpi); - let mut rows: Vec<_> = openings.iter().flat_map(|o| o.eval_row.clone()).collect(); - transpose_in_place(&mut rows, &mut scratch, num_parties); - rows.chunks(num_parties) - .map(|c| izip!(c, &eq_worlds_coeffs).map(|(&l, &r)| l * r).sum()) - .collect() - }; + let aggregated_proof = orion_proof_aggregate::<C, ComPackF, OpenPackF, T>( + &openings, + &gkr_challenge.x_mpi, + &mut aggregator_transcript, + ); + let mut scratch = vec![C::ChallengeField::ZERO; 1 << num_vars_in_unpacked_msg]; let final_expected_eval = MultiLinearPoly::evaluate_with_buffer( - &aggregated_eval_row, + &aggregated_proof.eval_row, &gkr_challenge.local_xs()[..num_vars_in_unpacked_msg], - &mut scratch[..aggregated_eval_row.len()], + &mut scratch, ); - let agregated_proof = OrionProof { - eval_row: aggregated_eval_row, - proximity_rows: aggregated_proximity_rows, - query_openings: paths, - }; - - let res = orion_verify_simd_field_aggregated::<C, ComPackF, OpenPackF, T>( + assert!(orion_verify_simd_field_aggregated::< + C, + ComPackF, + OpenPackF, + T, + >( num_parties, &srs, &final_commitment, &gkr_challenge, final_expected_eval, &mut verifier_transcript, - &agregated_proof, - ); - - assert!(res); + &aggregated_proof, + )); } #[test] From 1ec5b7632faadb5c37c2289e449c598cdadd10c9 Mon Sep 17 00:00:00 2001 From: Hang Su <tonyfloater@gmail.com> Date: Sun, 1 Dec 2024 08:07:05 -0500 Subject: [PATCH 34/65] tame CI clippy --- poly_commit/src/orion/simd_field_agg_impl.rs | 4 +--- poly_commit/src/orion/simd_field_agg_tests.rs | 7 ++----- poly_commit/tests/test_orion.rs | 11 +++++++++++ 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/poly_commit/src/orion/simd_field_agg_impl.rs b/poly_commit/src/orion/simd_field_agg_impl.rs index b146a810..cdeaf83f 100644 --- a/poly_commit/src/orion/simd_field_agg_impl.rs +++ b/poly_commit/src/orion/simd_field_agg_impl.rs @@ -12,15 +12,13 @@ use crate::{ }; #[allow(unused)] -pub(crate) fn orion_proof_aggregate<C, ComPackF, OpenPackF, T>( +pub(crate) fn orion_proof_aggregate<C, T>( openings: &[OrionProof<C::ChallengeField>], x_mpi: &[C::ChallengeField], transcript: &mut T, ) -> OrionProof<C::ChallengeField> where C: GKRFieldConfig, - ComPackF: SimdField<Scalar = C::CircuitField>, - OpenPackF: SimdField<Scalar = C::CircuitField>, T: Transcript<C::ChallengeField>, { let paths = openings diff --git a/poly_commit/src/orion/simd_field_agg_tests.rs b/poly_commit/src/orion/simd_field_agg_tests.rs index 3ddfc015..29bd1ec3 100644 --- a/poly_commit/src/orion/simd_field_agg_tests.rs +++ b/poly_commit/src/orion/simd_field_agg_tests.rs @@ -125,11 +125,8 @@ fn test_orion_simd_aggregate_verify_helper<C, ComPackF, OpenPackF, T>( .collect(); let mut aggregator_transcript = committee[0].transcript.clone(); - let aggregated_proof = orion_proof_aggregate::<C, ComPackF, OpenPackF, T>( - &openings, - &gkr_challenge.x_mpi, - &mut aggregator_transcript, - ); + let aggregated_proof = + orion_proof_aggregate::<C, T>(&openings, &gkr_challenge.x_mpi, &mut aggregator_transcript); let mut scratch = vec![C::ChallengeField::ZERO; 1 << num_vars_in_unpacked_msg]; let final_expected_eval = MultiLinearPoly::evaluate_with_buffer( diff --git a/poly_commit/tests/test_orion.rs b/poly_commit/tests/test_orion.rs index a8f71b30..b29579f5 100644 --- a/poly_commit/tests/test_orion.rs +++ b/poly_commit/tests/test_orion.rs @@ -4,6 +4,7 @@ use arith::{ExtensionField, Field, SimdField}; use ark_std::test_rng; use gf2::{GF2x128, GF2x64, GF2x8, GF2}; use gf2_128::GF2_128; +use mpi_config::MPIConfig; use poly_commit::*; use polynomials::MultiLinearPoly; use transcript::{BytesHashTranscript, Keccak256hasher}; @@ -90,3 +91,13 @@ fn test_orion_simd_field_pcs_full_e2e() { test_orion_simd_field_pcs_generics::<GF2, GF2x8, GF2_128, GF2x64, GF2x8>(); test_orion_simd_field_pcs_generics::<GF2, GF2x8, GF2_128, GF2x128, GF2x8>(); } + +#[test] +fn test_orion_for_gkr_expander() { + #[allow(unused)] + let mpi_config = MPIConfig::new(); + + // TODO ... + + MPIConfig::finalize() +} From 27858e3f593bc714aa65dab9c31683284734b701 Mon Sep 17 00:00:00 2001 From: Hang Su <tonyfloater@gmail.com> Date: Tue, 3 Dec 2024 16:41:31 -0500 Subject: [PATCH 35/65] at least the mpi exec e2e runs on my machine for now... wait wtf mpi? --- poly_commit/src/orion/pcs_for_expander_gkr.rs | 45 +++-- poly_commit/tests/common.rs | 5 +- poly_commit/tests/test_orion.rs | 156 +++++++++++++++++- poly_commit/tests/test_raw.rs | 4 +- 4 files changed, 189 insertions(+), 21 deletions(-) diff --git a/poly_commit/src/orion/pcs_for_expander_gkr.rs b/poly_commit/src/orion/pcs_for_expander_gkr.rs index 5ad631c9..7b4c14d6 100644 --- a/poly_commit/src/orion/pcs_for_expander_gkr.rs +++ b/poly_commit/src/orion/pcs_for_expander_gkr.rs @@ -1,4 +1,6 @@ -use arith::SimdField; +use std::io::Cursor; + +use arith::{FieldSerde, SimdField}; use gkr_field_config::GKRFieldConfig; use mpi_config::MPIConfig; use polynomials::{EqPolynomial, MultiLinearPoly}; @@ -61,11 +63,14 @@ where poly: &MultiLinearPoly<C::SimdCircuitField>, scratch_pad: &mut Self::ScratchPad, ) -> Self::Commitment { - let num_vars_each_core = *params - mpi_config.world_size(); + let num_vars_each_core = *params - mpi_config.world_size().ilog2() as usize; assert_eq!(num_vars_each_core, proving_key.num_vars); let commitment = orion_commit_simd_field(proving_key, poly, scratch_pad).unwrap(); - if mpi_config.world_size == 1 { + + // NOTE: Hang also assume that, linear GKR will take over the commitment + // and force sync transcript hash state of subordinate machines to be the same. + if mpi_config.world_size() == 1 { return commitment; } @@ -73,8 +78,6 @@ where let mut buffer = vec![Self::Commitment::default(); mpi_config.world_size()]; mpi_config.gather_vec(&local_buffer, &mut buffer); - // NOTE: Hang also assume that, linear GKR will take over the commitment - // and force sync transcript hash state of subordinate machines to be the same. if !mpi_config.is_root() { return commitment; } @@ -93,7 +96,7 @@ where transcript: &mut T, // add transcript here to allow interactive arguments scratch_pad: &mut Self::ScratchPad, ) -> Self::Opening { - let num_vars_each_core = *params - mpi_config.world_size(); + let num_vars_each_core = *params - mpi_config.world_size().ilog2() as usize; assert_eq!(num_vars_each_core, proving_key.num_vars); let local_xs = eval_point.local_xs(); @@ -105,7 +108,7 @@ where OpenPackF, T, >(proving_key, poly, &local_xs, transcript, scratch_pad); - if mpi_config.world_size == 1 { + if mpi_config.world_size() == 1 { return local_opening; } @@ -124,12 +127,28 @@ where }) .collect(); + // NOTE: local query openings serialized to bytes + let mut local_query_openings_serialized = Vec::new(); + local_opening + .query_openings + .serialize_into(&mut local_query_openings_serialized) + .unwrap(); + // NOTE: gather all merkle paths - let mut query_openings = vec![ - tree::RangePath::default(); - mpi_config.world_size() * local_opening.query_openings.len() - ]; - mpi_config.gather_vec(&local_opening.query_openings, &mut query_openings); + let mut query_openings_serialized = + vec![0u8; mpi_config.world_size() * local_query_openings_serialized.len()]; + mpi_config.gather_vec( + &local_query_openings_serialized, + &mut query_openings_serialized, + ); + + let query_openings: Vec<tree::RangePath> = query_openings_serialized + .chunks(local_query_openings_serialized.len()) + .flat_map(|bs| { + let mut read_cursor = Cursor::new(bs); + Vec::deserialize_from(&mut read_cursor).unwrap() + }) + .collect(); if !mpi_config.is_root() { return local_opening; @@ -155,7 +174,7 @@ where ) -> bool { assert_eq!(*params, eval_point.num_vars()); - if mpi_config.world_size == 1 { + if mpi_config.world_size == 1 || !mpi_config.is_root() { return orion_verify_simd_field::< C::CircuitField, C::SimdCircuitField, diff --git a/poly_commit/tests/common.rs b/poly_commit/tests/common.rs index cf107ed0..6175842e 100644 --- a/poly_commit/tests/common.rs +++ b/poly_commit/tests/common.rs @@ -1,4 +1,5 @@ use arith::Field; +use ark_std::test_rng; use gkr_field_config::GKRFieldConfig; use mpi_config::MPIConfig; use poly_commit::raw::RawExpanderGKR; @@ -45,7 +46,7 @@ pub fn test_pcs<F: Field, T: Transcript<F>, P: PolynomialCommitmentScheme<F, T>> } #[allow(unused)] -pub fn test_gkr_pcs< +pub fn test_pcs_for_expander_gkr< C: GKRFieldConfig, T: Transcript<C::ChallengeField>, P: PCSForExpanderGKR<C, T>, @@ -56,7 +57,7 @@ pub fn test_gkr_pcs< poly: &MultiLinearPoly<C::SimdCircuitField>, xs: &[ExpanderGKRChallenge<C>], ) { - let mut rng = thread_rng(); + let mut rng = test_rng(); let srs = P::gen_srs_for_testing(params, mpi_config, &mut rng); let (proving_key, verification_key) = srs.into_keys(); let mut scratch_pad = P::init_scratch_pad(params, mpi_config); diff --git a/poly_commit/tests/test_orion.rs b/poly_commit/tests/test_orion.rs index b29579f5..6dcec575 100644 --- a/poly_commit/tests/test_orion.rs +++ b/poly_commit/tests/test_orion.rs @@ -4,10 +4,12 @@ use arith::{ExtensionField, Field, SimdField}; use ark_std::test_rng; use gf2::{GF2x128, GF2x64, GF2x8, GF2}; use gf2_128::GF2_128; +use gkr_field_config::{GF2ExtConfig, GKRFieldConfig}; use mpi_config::MPIConfig; use poly_commit::*; use polynomials::MultiLinearPoly; -use transcript::{BytesHashTranscript, Keccak256hasher}; +use raw::RawExpanderGKR; +use transcript::{BytesHashTranscript, Keccak256hasher, Transcript}; const TEST_REPETITION: usize = 3; @@ -92,12 +94,156 @@ fn test_orion_simd_field_pcs_full_e2e() { test_orion_simd_field_pcs_generics::<GF2, GF2x8, GF2_128, GF2x128, GF2x8>(); } -#[test] -fn test_orion_for_gkr_expander() { - #[allow(unused)] +fn test_orion_for_expander_gkr_generics<C, ComPackF, OpenPackF, T>(num_vars: usize) +where + C: GKRFieldConfig, + ComPackF: SimdField<Scalar = C::CircuitField>, + OpenPackF: SimdField<Scalar = C::CircuitField>, + T: Transcript<C::ChallengeField>, +{ + let mut rng = test_rng(); let mpi_config = MPIConfig::new(); - // TODO ... + // NOTE: generate global random polynomial + let num_vars_in_simd = C::SimdCircuitField::PACK_SIZE.ilog2() as usize; + let num_vars_in_mpi = mpi_config.world_size().ilog2() as usize; + let poly = + MultiLinearPoly::<C::SimdCircuitField>::random(num_vars - num_vars_in_simd, &mut rng); + + // NOTE generate srs for each party, and shared challenge point in each party + let srs = + <OrionSIMDFieldPCS< + C::CircuitField, + C::SimdCircuitField, + C::ChallengeField, + ComPackF, + OpenPackF, + T, + > as PCSForExpanderGKR<C, T>>::gen_srs_for_testing(&num_vars, &mpi_config, &mut rng); + let (pk, vk) = srs.into_keys(); + + let challenge_point = ExpanderGKRChallenge::<C> { + x_mpi: (0..num_vars_in_mpi) + .map(|_| C::ChallengeField::random_unsafe(&mut rng)) + .collect(), + x_simd: (0..num_vars_in_simd) + .map(|_| C::ChallengeField::random_unsafe(&mut rng)) + .collect(), + x: (0..(num_vars - num_vars_in_mpi - num_vars_in_simd)) + .map(|_| C::ChallengeField::random_unsafe(&mut rng)) + .collect(), + }; + + let mut scratch_pad = + <OrionSIMDFieldPCS< + C::CircuitField, + C::SimdCircuitField, + C::ChallengeField, + ComPackF, + OpenPackF, + T, + > as PCSForExpanderGKR<C, T>>::init_scratch_pad(&num_vars, &mpi_config); + + let mut local_prover_transcript = T::new(); + let mut local_verifier_transcript = T::new(); + + let expected_global_eval = RawExpanderGKR::<C, T>::eval( + &poly.coeffs, + &challenge_point.x, + &challenge_point.x_simd, + &challenge_point.x_mpi, + ); + + dbg!(poly.get_num_vars(), poly.coeffs[0]); + dbg!(pk.num_vars); + dbg!(&challenge_point.x_mpi); + dbg!(mpi_config.world_size(), mpi_config.world_rank()); + dbg!(expected_global_eval); + + // NOTE separate polynomial into different pieces by mpi rank + let poly_vars_stride = (1 << poly.get_num_vars()) / mpi_config.world_size(); + let poly_coeff_starts = mpi_config.world_rank() * poly_vars_stride; + let poly_coeff_ends = poly_coeff_starts + poly_vars_stride; + let local_poly = MultiLinearPoly::new(poly.coeffs[poly_coeff_starts..poly_coeff_ends].to_vec()); + + let expected_local_eval = RawExpanderGKR::<C, T>::eval_local( + &local_poly.coeffs, + &challenge_point.x, + &challenge_point.x_simd, + ); + + dbg!(local_poly.get_num_vars(), local_poly.coeffs[0]); + + // NOTE commit polynomial in different parts + let commitment = <OrionSIMDFieldPCS< + C::CircuitField, + C::SimdCircuitField, + C::ChallengeField, + ComPackF, + OpenPackF, + T, + > as PCSForExpanderGKR<C, T>>::commit( + &num_vars, + &mpi_config, + &pk, + &local_poly, + &mut scratch_pad, + ); + dbg!(commitment); + + // NOTE: open polynomial in different parts + let opening = <OrionSIMDFieldPCS< + C::CircuitField, + C::SimdCircuitField, + C::ChallengeField, + ComPackF, + OpenPackF, + T, + > as PCSForExpanderGKR<C, T>>::open( + &num_vars, + &mpi_config, + &pk, + &local_poly, + &challenge_point, + &mut local_prover_transcript, + &mut scratch_pad, + ); + dbg!(opening.query_openings.len()); + + // NOTE verify polynomial in different parts + let pass = <OrionSIMDFieldPCS< + C::CircuitField, + C::SimdCircuitField, + C::ChallengeField, + ComPackF, + OpenPackF, + T, + > as PCSForExpanderGKR<C, T>>::verify( + &num_vars, + &mpi_config, + &vk, + &commitment, + &challenge_point, + if mpi_config.is_root() { + expected_global_eval + } else { + expected_local_eval + }, + &mut local_verifier_transcript, + &opening, + ); + + assert!(pass); MPIConfig::finalize() } + +#[test] +fn test_orion_for_expander_gkr() { + test_orion_for_expander_gkr_generics::< + GF2ExtConfig, + GF2x128, + GF2x8, + BytesHashTranscript<_, Keccak256hasher>, + >(30); +} diff --git a/poly_commit/tests/test_raw.rs b/poly_commit/tests/test_raw.rs index b4990711..a0823b65 100644 --- a/poly_commit/tests/test_raw.rs +++ b/poly_commit/tests/test_raw.rs @@ -49,7 +49,9 @@ fn test_raw_gkr_helper<C: GKRFieldConfig, T: Transcript<C::ChallengeField>>( .collect::<Vec<C::ChallengeField>>(), }) .collect::<Vec<ExpanderGKRChallenge<C>>>(); - common::test_gkr_pcs::<C, T, RawExpanderGKR<C, T>>(¶ms, mpi_config, transcript, &poly, &xs); + common::test_pcs_for_expander_gkr::<C, T, RawExpanderGKR<C, T>>( + ¶ms, mpi_config, transcript, &poly, &xs, + ); } #[test] From edfb4e54c936b63709444a9999fdd64238cd9494 Mon Sep 17 00:00:00 2001 From: Hang Su <tonyfloater@gmail.com> Date: Tue, 3 Dec 2024 19:40:31 -0500 Subject: [PATCH 36/65] experimental move, should discuss with folks about if it is a good move --- poly_commit/src/orion/pcs_for_expander_gkr.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/poly_commit/src/orion/pcs_for_expander_gkr.rs b/poly_commit/src/orion/pcs_for_expander_gkr.rs index 7b4c14d6..c5db88e2 100644 --- a/poly_commit/src/orion/pcs_for_expander_gkr.rs +++ b/poly_commit/src/orion/pcs_for_expander_gkr.rs @@ -134,6 +134,14 @@ where .serialize_into(&mut local_query_openings_serialized) .unwrap(); + // NOTE: Hang does not think this is a good move, but this is mostly + // working with MPI behavior, so we align local MT openings serialization + // against power-of-2 bytes length. + local_query_openings_serialized.resize( + local_query_openings_serialized.len().next_power_of_two(), + 0u8, + ); + // NOTE: gather all merkle paths let mut query_openings_serialized = vec![0u8; mpi_config.world_size() * local_query_openings_serialized.len()]; From 9ab727a9be14ef4fac0e2aebb5118e7d641c9d14 Mon Sep 17 00:00:00 2001 From: Hang Su <tonyfloater@gmail.com> Date: Wed, 4 Dec 2024 18:50:50 -0500 Subject: [PATCH 37/65] experimental move, mpi opening each party precalculate weighted version --- poly_commit/src/orion/pcs_for_expander_gkr.rs | 52 ++-- poly_commit/src/orion/simd_field_agg_impl.rs | 250 +++++++++++++++--- poly_commit/src/orion/simd_field_agg_tests.rs | 65 +++-- poly_commit/src/orion/simd_field_impl.rs | 39 +-- poly_commit/src/orion/utils.rs | 29 ++ 5 files changed, 328 insertions(+), 107 deletions(-) diff --git a/poly_commit/src/orion/pcs_for_expander_gkr.rs b/poly_commit/src/orion/pcs_for_expander_gkr.rs index c5db88e2..cba6e1f6 100644 --- a/poly_commit/src/orion/pcs_for_expander_gkr.rs +++ b/poly_commit/src/orion/pcs_for_expander_gkr.rs @@ -3,7 +3,7 @@ use std::io::Cursor; use arith::{FieldSerde, SimdField}; use gkr_field_config::GKRFieldConfig; use mpi_config::MPIConfig; -use polynomials::{EqPolynomial, MultiLinearPoly}; +use polynomials::MultiLinearPoly; use transcript::Transcript; use crate::{ @@ -100,31 +100,36 @@ where assert_eq!(num_vars_each_core, proving_key.num_vars); let local_xs = eval_point.local_xs(); - let local_opening = orion_open_simd_field::< - C::CircuitField, - C::SimdCircuitField, - C::ChallengeField, - ComPackF, - OpenPackF, - T, - >(proving_key, poly, &local_xs, transcript, scratch_pad); if mpi_config.world_size() == 1 { - return local_opening; + return orion_open_simd_field::< + C::CircuitField, + C::SimdCircuitField, + C::ChallengeField, + ComPackF, + OpenPackF, + T, + >(proving_key, poly, &local_xs, transcript, scratch_pad); } + let local_opening = orion_open_simd_field_mpi::<C, ComPackF, OpenPackF, T>( + mpi_config.world_size(), + mpi_config.world_rank(), + proving_key, + poly, + eval_point, + transcript, + scratch_pad, + ); + // NOTE: eval row combine from MPI - let mpi_eq_coeffs = EqPolynomial::build_eq_x_r(&eval_point.x_mpi); - let eval_row = mpi_config.coef_combine_vec(&local_opening.eval_row, &mpi_eq_coeffs); + let eval_row = mpi_config.sum_vec(&local_opening.eval_row); // NOTE: sample MPI linear combination coeffs for proximity rows, // and proximity rows combine with MPI let proximity_rows = local_opening .proximity_rows .iter() - .map(|row| { - let weights = transcript.generate_challenge_field_elements(mpi_config.world_size()); - mpi_config.coef_combine_vec(row, &weights) - }) + .map(|v| mpi_config.sum_vec(v)) .collect(); // NOTE: local query openings serialized to bytes @@ -182,7 +187,7 @@ where ) -> bool { assert_eq!(*params, eval_point.num_vars()); - if mpi_config.world_size == 1 || !mpi_config.is_root() { + if mpi_config.world_size() == 1 { return orion_verify_simd_field::< C::CircuitField, C::SimdCircuitField, @@ -200,6 +205,19 @@ where ); } + if !mpi_config.is_root() { + return orion_verify_simd_field_mpi::<C, ComPackF, OpenPackF, T>( + mpi_config.world_size(), + mpi_config.world_rank(), + verifying_key, + commitment, + eval_point, + eval, + transcript, + opening, + ); + } + // NOTE: we now assume that the input opening is from the root machine, // as proofs from other machines are typically undefined orion_verify_simd_field_aggregated::<C, ComPackF, OpenPackF, T>( diff --git a/poly_commit/src/orion/simd_field_agg_impl.rs b/poly_commit/src/orion/simd_field_agg_impl.rs index cdeaf83f..e0b77058 100644 --- a/poly_commit/src/orion/simd_field_agg_impl.rs +++ b/poly_commit/src/orion/simd_field_agg_impl.rs @@ -11,53 +11,224 @@ use crate::{ OrionSRS, PCS_SOUNDNESS_BITS, }; -#[allow(unused)] -pub(crate) fn orion_proof_aggregate<C, T>( - openings: &[OrionProof<C::ChallengeField>], - x_mpi: &[C::ChallengeField], +// NOTE: We assume this API is only used under PCS integration for expander GKR, +// and this method represents the local work for PCS opening in a party in MPI. +pub(crate) fn orion_open_simd_field_mpi<C, ComPackF, OpenPackF, T>( + mpi_world_size: usize, + mpi_rank: usize, + pk: &OrionSRS, + poly: &MultiLinearPoly<C::SimdCircuitField>, + eval_point: &ExpanderGKRChallenge<C>, transcript: &mut T, + scratch_pad: &OrionScratchPad<C::CircuitField, ComPackF>, ) -> OrionProof<C::ChallengeField> where C: GKRFieldConfig, + ComPackF: SimdField<Scalar = C::CircuitField>, + OpenPackF: SimdField<Scalar = C::CircuitField>, T: Transcript<C::ChallengeField>, { - let paths = openings - .iter() - .flat_map(|o| o.query_openings.clone()) + let local_point = eval_point.local_xs(); + assert_eq!(eval_point.x_mpi.len(), mpi_world_size.ilog2() as usize); + + let (row_num, msg_size) = { + let num_vars = poly.get_num_vars() + C::SimdCircuitField::PACK_SIZE.ilog2() as usize; + assert_eq!(num_vars, local_point.len()); + + let (row_field_elems, msg_size) = OrionSRS::evals_shape::<C::CircuitField>(num_vars); + let row_num = row_field_elems / C::SimdCircuitField::PACK_SIZE; + (row_num, msg_size) + }; + + let num_vars_in_row = row_num.ilog2() as usize; + let num_vars_in_unpacked_msg = local_point.len() - num_vars_in_row; + + // NOTE: transpose and shuffle evaluations (repack evaluations in another direction) + // for linear combinations in evaulation/proximity tests + let mut evals = poly.coeffs.clone(); + let packed_shuffled_evals: Vec<OpenPackF> = transpose_and_shuffle_simd(&mut evals, row_num); + drop(evals); + + // NOTE: declare the look up tables for column sums + let tables_num = row_num / OpenPackF::PACK_SIZE; + let mut luts = SubsetSumLUTs::<C::ChallengeField>::new(OpenPackF::PACK_SIZE, tables_num); + assert_eq!(row_num % OpenPackF::PACK_SIZE, 0); + + // NOTE: working on evaluation response of tensor code IOP based PCS + // The difference is that, the LUTs multiply with MPI eq coeff weight + let mut eval_row = vec![C::ChallengeField::ZERO; msg_size * C::SimdCircuitField::PACK_SIZE]; + + let eq_coeffs: Vec<_> = { + let mpi_eq_coeffs = EqPolynomial::build_eq_x_r(&eval_point.x_mpi); + let mpi_eq_weight = mpi_eq_coeffs[mpi_rank]; + let eqs = EqPolynomial::build_eq_x_r(&local_point[num_vars_in_unpacked_msg..]); + eqs.iter().map(|t| *t * mpi_eq_weight).collect() + }; + luts.build(&eq_coeffs); + + izip!(packed_shuffled_evals.chunks(tables_num), &mut eval_row) + .for_each(|(p_col, res)| *res = luts.lookup_and_sum(p_col)); + + // NOTE: draw random linear combination out + // and compose proximity response(s) of tensor code IOP based PCS + let proximity_reps = pk.proximity_repetitions::<C::ChallengeField>(PCS_SOUNDNESS_BITS); + let mut proximity_rows = + vec![ + vec![C::ChallengeField::ZERO; msg_size * C::SimdCircuitField::PACK_SIZE]; + proximity_reps + ]; + + proximity_rows.iter_mut().for_each(|row_buffer| { + let random_coeffs: Vec<_> = { + let mpi_weights = transcript.generate_challenge_field_elements(mpi_world_size); + let mpi_weight = mpi_weights[mpi_rank]; + let random_coeffs = transcript.generate_challenge_field_elements(row_num); + random_coeffs.iter().map(|t| *t * mpi_weight).collect() + }; + luts.build(&random_coeffs); + + izip!(packed_shuffled_evals.chunks(tables_num), row_buffer) + .for_each(|(p_col, res)| *res = luts.lookup_and_sum(p_col)); + }); + drop(luts); + + // NOTE: MT opening for point queries + let query_openings = orion_mt_openings(pk, transcript, scratch_pad); + + OrionProof { + eval_row, + proximity_rows, + query_openings, + } +} + +#[allow(clippy::too_many_arguments)] +pub(crate) fn orion_verify_simd_field_mpi<C, ComPackF, OpenPackF, T>( + mpi_world_size: usize, + mpi_rank: usize, + vk: &OrionSRS, + commitment: &OrionCommitment, + point: &ExpanderGKRChallenge<C>, + evaluation: C::ChallengeField, + transcript: &mut T, + proof: &OrionProof<C::ChallengeField>, +) -> bool +where + C: GKRFieldConfig, + ComPackF: SimdField<Scalar = C::CircuitField>, + OpenPackF: SimdField<Scalar = C::CircuitField>, + T: Transcript<C::ChallengeField>, +{ + let local_xs = point.local_xs(); + + let (row_num, msg_size) = { + let (row_field_elems, msg_size) = OrionSRS::evals_shape::<C::CircuitField>(local_xs.len()); + let row_num = row_field_elems / C::SimdCircuitField::PACK_SIZE; + (row_num, msg_size) + }; + + let num_vars_in_row = row_num.ilog2() as usize; + let num_vars_in_unpacked_msg = local_xs.len() - num_vars_in_row; + + // NOTE: working on evaluation response, evaluate the rest of the response + let mut scratch = vec![C::ChallengeField::ZERO; msg_size * C::SimdCircuitField::PACK_SIZE]; + let final_eval = MultiLinearPoly::evaluate_with_buffer( + &proof.eval_row, + &local_xs[..num_vars_in_unpacked_msg], + &mut scratch, + ); + + let mpi_eq_coeffs = EqPolynomial::build_eq_x_r(&point.x_mpi); + let mpi_eq_weight = mpi_eq_coeffs[mpi_rank]; + if final_eval != evaluation * mpi_eq_weight { + return false; + } + + // NOTE: working on proximity responses, draw random linear combinations + // then draw query points from fiat shamir transcripts + let proximity_reps = vk.proximity_repetitions::<C::ChallengeField>(PCS_SOUNDNESS_BITS); + let random_linear_combinations: Vec<Vec<C::ChallengeField>> = (0..proximity_reps) + .map(|_| { + let mpi_weights = transcript.generate_challenge_field_elements(mpi_world_size); + let mpi_weight = mpi_weights[mpi_rank]; + let random_coeffs = transcript.generate_challenge_field_elements(row_num); + random_coeffs.iter().map(|t| *t * mpi_weight).collect() + }) .collect(); - let num_parties = 1 << x_mpi.len(); + let query_num = vk.query_complexity(PCS_SOUNDNESS_BITS); + let query_indices = transcript.generate_challenge_index_vector(query_num); - let proximity_reps = openings[0].proximity_rows.len(); - let mut scratch = vec![C::ChallengeField::ZERO; num_parties * openings[0].eval_row.len()]; + // NOTE: check consistency in MT in the opening trees and against the commitment tree + if !orion_mt_verify(vk, &query_indices, &proof.query_openings, commitment) { + return false; + } - let aggregated_proximity_rows = (0..proximity_reps) - .map(|i| { - let weights = transcript.generate_challenge_field_elements(num_parties); - let mut rows: Vec<_> = openings - .iter() - .flat_map(|o| o.proximity_rows[i].clone()) - .collect(); - transpose_in_place(&mut rows, &mut scratch, num_parties); - rows.chunks(num_parties) - .map(|c| izip!(c, &weights).map(|(&l, &r)| l * r).sum()) + // NOTE: prepare the interleaved alphabets from the MT paths, + // but reshuffle the packed elements into another direction + let mut scratch = vec![C::CircuitField::ZERO; C::SimdCircuitField::PACK_SIZE * row_num]; + let shuffled_interleaved_alphabet: Vec<Vec<OpenPackF>> = proof + .query_openings + .iter() + .map(|c| -> Vec<_> { + let mut elts = c.unpack_field_elems::<C::CircuitField, ComPackF>(); + transpose_in_place(&mut elts, &mut scratch, row_num); + elts.chunks(OpenPackF::PACK_SIZE) + .map(OpenPackF::pack) .collect() }) .collect(); - let aggregated_eval_row: Vec<_> = { - let eq_worlds_coeffs = EqPolynomial::build_eq_x_r(x_mpi); - let mut rows: Vec<_> = openings.iter().flat_map(|o| o.eval_row.clone()).collect(); - transpose_in_place(&mut rows, &mut scratch, num_parties); - rows.chunks(num_parties) - .map(|c| izip!(c, &eq_worlds_coeffs).map(|(&l, &r)| l * r).sum()) - .collect() + // NOTE: declare the look up tables for column sums + let tables_num = row_num / OpenPackF::PACK_SIZE; + let mut luts = SubsetSumLUTs::<C::ChallengeField>::new(OpenPackF::PACK_SIZE, tables_num); + assert_eq!(row_num % OpenPackF::PACK_SIZE, 0); + + let eq_linear_combination = { + let mpi_eq_coeffs = EqPolynomial::build_eq_x_r(&point.x_mpi); + let mpi_eq_weight = mpi_eq_coeffs[mpi_rank]; + let eqs = EqPolynomial::build_eq_x_r(&local_xs[num_vars_in_unpacked_msg..]); + eqs.iter().map(|t| *t * mpi_eq_weight).collect() }; - OrionProof { - eval_row: aggregated_eval_row, - proximity_rows: aggregated_proximity_rows, - query_openings: paths, - } + let mut scratch_msg = vec![C::ChallengeField::ZERO; C::SimdCircuitField::PACK_SIZE * msg_size]; + let mut scratch_codeword = + vec![C::ChallengeField::ZERO; C::SimdCircuitField::PACK_SIZE * vk.codeword_len()]; + + izip!(&random_linear_combinations, &proof.proximity_rows) + .chain(iter::once((&eq_linear_combination, &proof.eval_row))) + .all(|(rl, msg)| { + let mut msg_cloned = msg.clone(); + transpose_in_place(&mut msg_cloned, &mut scratch_msg, msg_size); + let mut codeword: Vec<_> = msg_cloned + .chunks(msg_size) + .flat_map(|m| vk.code_instance.encode(m).unwrap()) + .collect(); + transpose_in_place( + &mut codeword, + &mut scratch_codeword, + C::SimdCircuitField::PACK_SIZE, + ); + + luts.build(rl); + + izip!(&query_indices, &shuffled_interleaved_alphabet).all( + |(&qi, interleaved_alphabet)| { + let index = qi % vk.codeword_len(); + + let simd_starts = index * C::SimdCircuitField::PACK_SIZE; + let simd_ends = (index + 1) * C::SimdCircuitField::PACK_SIZE; + + izip!( + &codeword[simd_starts..simd_ends], + interleaved_alphabet.chunks(tables_num) + ) + .all(|(expected_alphabet, packed_index)| { + let alphabet = luts.lookup_and_sum(packed_index); + alphabet == *expected_alphabet + }) + }, + ) + }) } pub(crate) fn orion_verify_simd_field_aggregated<C, ComPackF, OpenPackF, T>( @@ -106,17 +277,18 @@ where // NOTE: working on proximity responses, draw random linear combinations // then draw query points from fiat shamir transcripts let proximity_reps = vk.proximity_repetitions::<C::ChallengeField>(PCS_SOUNDNESS_BITS); - let proximity_local_coeffs: Vec<Vec<C::ChallengeField>> = (0..proximity_reps) - .map(|_| transcript.generate_challenge_field_elements(row_num)) - .collect(); + let (proximity_worlds_coeffs, proximity_local_coeffs): (Vec<_>, Vec<_>) = (0..proximity_reps) + .map(|_| { + ( + transcript.generate_challenge_field_elements(mpi_world_size), + transcript.generate_challenge_field_elements(row_num), + ) + }) + .unzip(); let query_num = vk.query_complexity(PCS_SOUNDNESS_BITS); let query_indices = transcript.generate_challenge_index_vector(query_num); - let proximity_worlds_coeffs: Vec<Vec<C::ChallengeField>> = (0..proximity_reps) - .map(|_| transcript.generate_challenge_field_elements(mpi_world_size)) - .collect(); - // NOTE: check all merkle paths, and check merkle roots against commitment let (mt_verifications, roots): (Vec<_>, Vec<_>) = proof .query_openings diff --git a/poly_commit/src/orion/simd_field_agg_tests.rs b/poly_commit/src/orion/simd_field_agg_tests.rs index 29bd1ec3..78d9160a 100644 --- a/poly_commit/src/orion/simd_field_agg_tests.rs +++ b/poly_commit/src/orion/simd_field_agg_tests.rs @@ -10,7 +10,7 @@ use polynomials::MultiLinearPoly; use transcript::{BytesHashTranscript, Keccak256hasher, Transcript}; use crate::{ - orion::{simd_field_agg_impl::*, *}, + orion::{simd_field_agg_impl::*, utils::*, *}, traits::TensorCodeIOPPCS, ExpanderGKRChallenge, }; @@ -29,6 +29,47 @@ where _phantom: PhantomData<EvalF>, } +fn orion_proof_aggregate<C, T>( + openings: &[OrionProof<C::ChallengeField>], + x_mpi: &[C::ChallengeField], +) -> OrionProof<C::ChallengeField> +where + C: GKRFieldConfig, + T: Transcript<C::ChallengeField>, +{ + let paths = openings + .iter() + .flat_map(|o| o.query_openings.clone()) + .collect(); + let num_parties = 1 << x_mpi.len(); + + let proximity_reps = openings[0].proximity_rows.len(); + let mut scratch = vec![C::ChallengeField::ZERO; num_parties * openings[0].eval_row.len()]; + + let aggregated_proximity_rows = (0..proximity_reps) + .map(|i| { + let mut rows: Vec<_> = openings + .iter() + .flat_map(|o| o.proximity_rows[i].clone()) + .collect(); + transpose_in_place(&mut rows, &mut scratch, num_parties); + rows.chunks(num_parties).map(|c| c.iter().sum()).collect() + }) + .collect(); + + let aggregated_eval_row: Vec<_> = { + let mut rows: Vec<_> = openings.iter().flat_map(|o| o.eval_row.clone()).collect(); + transpose_in_place(&mut rows, &mut scratch, num_parties); + rows.chunks(num_parties).map(|c| c.iter().sum()).collect() + }; + + OrionProof { + eval_row: aggregated_eval_row, + proximity_rows: aggregated_proximity_rows, + query_openings: paths, + } +} + fn test_orion_simd_aggregate_verify_helper<C, ComPackF, OpenPackF, T>( num_parties: usize, num_vars: usize, @@ -97,7 +138,7 @@ fn test_orion_simd_aggregate_verify_helper<C, ComPackF, OpenPackF, T>( .collect(); let final_tree_height = 1 + roots.len().ilog2(); - let (internals, _) = tree::Tree::new_with_leaf_nodes(roots, final_tree_height); + let (internals, _) = tree::Tree::new_with_leaf_nodes(roots.clone(), final_tree_height); internals[0] }; @@ -105,28 +146,22 @@ fn test_orion_simd_aggregate_verify_helper<C, ComPackF, OpenPackF, T>( &mut committee, global_poly.coeffs.chunks(1 << local_real_num_vars) ) - .map(|(committer, eval_slice)| { + .enumerate() + .map(|(i, (committer, eval_slice))| { let cloned_poly = MultiLinearPoly::new(eval_slice.to_vec()); - orion_open_simd_field::< - C::CircuitField, - C::SimdCircuitField, - C::ChallengeField, - ComPackF, - OpenPackF, - T, - >( + orion_open_simd_field_mpi::<C, ComPackF, OpenPackF, T>( + num_parties, + i, &srs, &cloned_poly, - &gkr_challenge.local_xs(), + &gkr_challenge, &mut committer.transcript, &committer.scratch_pad, ) }) .collect(); - let mut aggregator_transcript = committee[0].transcript.clone(); - let aggregated_proof = - orion_proof_aggregate::<C, T>(&openings, &gkr_challenge.x_mpi, &mut aggregator_transcript); + let aggregated_proof = orion_proof_aggregate::<C, T>(&openings, &gkr_challenge.x_mpi); let mut scratch = vec![C::ChallengeField::ZERO; 1 << num_vars_in_unpacked_msg]; let final_expected_eval = MultiLinearPoly::evaluate_with_buffer( diff --git a/poly_commit/src/orion/simd_field_impl.rs b/poly_commit/src/orion/simd_field_impl.rs index c1951575..a0b28634 100644 --- a/poly_commit/src/orion/simd_field_impl.rs +++ b/poly_commit/src/orion/simd_field_impl.rs @@ -6,10 +6,7 @@ use polynomials::{EqPolynomial, MultiLinearPoly}; use transcript::Transcript; use crate::{ - orion::{ - utils::{commit_encoded, orion_mt_openings, orion_mt_verify, transpose_in_place}, - OrionCommitment, OrionProof, OrionResult, OrionSRS, OrionScratchPad, - }, + orion::{utils::*, OrionCommitment, OrionProof, OrionResult, OrionSRS, OrionScratchPad}, traits::TensorCodeIOPPCS, SubsetSumLUTs, PCS_SOUNDNESS_BITS, }; @@ -71,33 +68,6 @@ where commit_encoded(pk, &packed_evals, scratch_pad, packed_rows, msg_size) } -#[inline(always)] -fn transpose_and_shuffle_simd<F, SimdF, PackF>( - evaluations: &mut [SimdF], - row_num: usize, -) -> Vec<PackF> -where - F: Field, - SimdF: SimdField<Scalar = F>, - PackF: SimdField<Scalar = F>, -{ - // NOTE: pre transpose evaluations - let mut scratch = vec![SimdF::ZERO; evaluations.len()]; - transpose_in_place(evaluations, &mut scratch, row_num); - drop(scratch); - - // NOTE: reshuffle the transposed matrix, from SIMD over row to SIMD over col - let mut scratch = vec![F::ZERO; SimdF::PACK_SIZE * row_num]; - evaluations - .chunks(row_num) - .flat_map(|row_simds| -> Vec<_> { - let mut elts: Vec<_> = row_simds.iter().flat_map(|f| f.unpack()).collect(); - transpose_in_place(&mut elts, &mut scratch, row_num); - elts.chunks(PackF::PACK_SIZE).map(PackF::pack).collect() - }) - .collect() -} - // NOTE: this implementation doesn't quite align with opening for // multilinear polynomials over base field, // as this directly plug into GKR argument system. @@ -133,8 +103,6 @@ where // NOTE: transpose and shuffle evaluations (repack evaluations in another direction) // for linear combinations in evaulation/proximity tests let mut evals = poly.coeffs.clone(); - assert_eq!(evals.len() * SimdF::PACK_SIZE % OpenPackF::PACK_SIZE, 0); - let packed_shuffled_evals: Vec<OpenPackF> = transpose_and_shuffle_simd(&mut evals, row_num); drop(evals); @@ -154,9 +122,8 @@ where // NOTE: draw random linear combination out // and compose proximity response(s) of tensor code IOP based PCS - let proximity_test_num = pk.proximity_repetitions::<EvalF>(PCS_SOUNDNESS_BITS); - let mut proximity_rows = - vec![vec![EvalF::ZERO; msg_size * SimdF::PACK_SIZE]; proximity_test_num]; + let proximity_reps = pk.proximity_repetitions::<EvalF>(PCS_SOUNDNESS_BITS); + let mut proximity_rows = vec![vec![EvalF::ZERO; msg_size * SimdF::PACK_SIZE]; proximity_reps]; proximity_rows.iter_mut().for_each(|row_buffer| { let random_coeffs = transcript.generate_challenge_field_elements(row_num); diff --git a/poly_commit/src/orion/utils.rs b/poly_commit/src/orion/utils.rs index 17757bcc..479e9601 100644 --- a/poly_commit/src/orion/utils.rs +++ b/poly_commit/src/orion/utils.rs @@ -209,6 +209,35 @@ pub(crate) fn transpose_in_place<F: Field>(mat: &mut [F], scratch: &mut [F], row mat.copy_from_slice(scratch); } +#[inline(always)] +pub(crate) fn transpose_and_shuffle_simd<F, SimdF, PackF>( + evaluations: &mut [SimdF], + row_num: usize, +) -> Vec<PackF> +where + F: Field, + SimdF: SimdField<Scalar = F>, + PackF: SimdField<Scalar = F>, +{ + assert_eq!(evaluations.len() * SimdF::PACK_SIZE % PackF::PACK_SIZE, 0); + + // NOTE: pre transpose evaluations + let mut scratch = vec![SimdF::ZERO; evaluations.len()]; + transpose_in_place(evaluations, &mut scratch, row_num); + drop(scratch); + + // NOTE: reshuffle the transposed matrix, from SIMD over row to SIMD over col + let mut scratch = vec![F::ZERO; SimdF::PACK_SIZE * row_num]; + evaluations + .chunks(row_num) + .flat_map(|row_simds| -> Vec<_> { + let mut elts: Vec<_> = row_simds.iter().flat_map(|f| f.unpack()).collect(); + transpose_in_place(&mut elts, &mut scratch, row_num); + elts.chunks(PackF::PACK_SIZE).map(PackF::pack).collect() + }) + .collect() +} + /* * LINEAR OPERATIONS */ From 213ca4aac9cd96ad74c89ce823daae76ad7527ea Mon Sep 17 00:00:00 2001 From: Hang Su <tonyfloater@gmail.com> Date: Wed, 4 Dec 2024 19:14:47 -0500 Subject: [PATCH 38/65] Revert "experimental move, mpi opening each party precalculate weighted version" Performance in MPI does not improve with the hack I thought about This reverts commit 9ab727a9be14ef4fac0e2aebb5118e7d641c9d14. --- poly_commit/src/orion/pcs_for_expander_gkr.rs | 76 ++---- poly_commit/src/orion/simd_field_agg_impl.rs | 248 +----------------- poly_commit/src/orion/simd_field_agg_tests.rs | 35 ++- poly_commit/src/orion/simd_field_impl.rs | 39 ++- poly_commit/src/orion/utils.rs | 29 -- 5 files changed, 96 insertions(+), 331 deletions(-) diff --git a/poly_commit/src/orion/pcs_for_expander_gkr.rs b/poly_commit/src/orion/pcs_for_expander_gkr.rs index cba6e1f6..3d94fd4c 100644 --- a/poly_commit/src/orion/pcs_for_expander_gkr.rs +++ b/poly_commit/src/orion/pcs_for_expander_gkr.rs @@ -3,7 +3,7 @@ use std::io::Cursor; use arith::{FieldSerde, SimdField}; use gkr_field_config::GKRFieldConfig; use mpi_config::MPIConfig; -use polynomials::MultiLinearPoly; +use polynomials::{EqPolynomial, MultiLinearPoly}; use transcript::Transcript; use crate::{ @@ -100,63 +100,52 @@ where assert_eq!(num_vars_each_core, proving_key.num_vars); let local_xs = eval_point.local_xs(); + let local_opening = orion_open_simd_field::< + C::CircuitField, + C::SimdCircuitField, + C::ChallengeField, + ComPackF, + OpenPackF, + T, + >(proving_key, poly, &local_xs, transcript, scratch_pad); if mpi_config.world_size() == 1 { - return orion_open_simd_field::< - C::CircuitField, - C::SimdCircuitField, - C::ChallengeField, - ComPackF, - OpenPackF, - T, - >(proving_key, poly, &local_xs, transcript, scratch_pad); + return local_opening; } - let local_opening = orion_open_simd_field_mpi::<C, ComPackF, OpenPackF, T>( - mpi_config.world_size(), - mpi_config.world_rank(), - proving_key, - poly, - eval_point, - transcript, - scratch_pad, - ); - // NOTE: eval row combine from MPI - let eval_row = mpi_config.sum_vec(&local_opening.eval_row); + let mpi_eq_coeffs = EqPolynomial::build_eq_x_r(&eval_point.x_mpi); + let eval_row = mpi_config.coef_combine_vec(&local_opening.eval_row, &mpi_eq_coeffs); // NOTE: sample MPI linear combination coeffs for proximity rows, // and proximity rows combine with MPI let proximity_rows = local_opening .proximity_rows .iter() - .map(|v| mpi_config.sum_vec(v)) + .map(|row| { + let weights = transcript.generate_challenge_field_elements(mpi_config.world_size()); + mpi_config.coef_combine_vec(row, &weights) + }) .collect(); // NOTE: local query openings serialized to bytes - let mut local_query_openings_serialized = Vec::new(); + let mut local_mt_paths_serialized = Vec::new(); local_opening .query_openings - .serialize_into(&mut local_query_openings_serialized) + .serialize_into(&mut local_mt_paths_serialized) .unwrap(); // NOTE: Hang does not think this is a good move, but this is mostly // working with MPI behavior, so we align local MT openings serialization // against power-of-2 bytes length. - local_query_openings_serialized.resize( - local_query_openings_serialized.len().next_power_of_two(), - 0u8, - ); + local_mt_paths_serialized.resize(local_mt_paths_serialized.len().next_power_of_two(), 0u8); // NOTE: gather all merkle paths - let mut query_openings_serialized = - vec![0u8; mpi_config.world_size() * local_query_openings_serialized.len()]; - mpi_config.gather_vec( - &local_query_openings_serialized, - &mut query_openings_serialized, - ); - - let query_openings: Vec<tree::RangePath> = query_openings_serialized - .chunks(local_query_openings_serialized.len()) + let mut mt_paths_serialized = + vec![0u8; mpi_config.world_size() * local_mt_paths_serialized.len()]; + mpi_config.gather_vec(&local_mt_paths_serialized, &mut mt_paths_serialized); + + let query_openings: Vec<tree::RangePath> = mt_paths_serialized + .chunks(local_mt_paths_serialized.len()) .flat_map(|bs| { let mut read_cursor = Cursor::new(bs); Vec::deserialize_from(&mut read_cursor).unwrap() @@ -187,7 +176,7 @@ where ) -> bool { assert_eq!(*params, eval_point.num_vars()); - if mpi_config.world_size() == 1 { + if mpi_config.world_size() == 1 || !mpi_config.is_root() { return orion_verify_simd_field::< C::CircuitField, C::SimdCircuitField, @@ -205,19 +194,6 @@ where ); } - if !mpi_config.is_root() { - return orion_verify_simd_field_mpi::<C, ComPackF, OpenPackF, T>( - mpi_config.world_size(), - mpi_config.world_rank(), - verifying_key, - commitment, - eval_point, - eval, - transcript, - opening, - ); - } - // NOTE: we now assume that the input opening is from the root machine, // as proofs from other machines are typically undefined orion_verify_simd_field_aggregated::<C, ComPackF, OpenPackF, T>( diff --git a/poly_commit/src/orion/simd_field_agg_impl.rs b/poly_commit/src/orion/simd_field_agg_impl.rs index e0b77058..632c3e74 100644 --- a/poly_commit/src/orion/simd_field_agg_impl.rs +++ b/poly_commit/src/orion/simd_field_agg_impl.rs @@ -11,226 +11,6 @@ use crate::{ OrionSRS, PCS_SOUNDNESS_BITS, }; -// NOTE: We assume this API is only used under PCS integration for expander GKR, -// and this method represents the local work for PCS opening in a party in MPI. -pub(crate) fn orion_open_simd_field_mpi<C, ComPackF, OpenPackF, T>( - mpi_world_size: usize, - mpi_rank: usize, - pk: &OrionSRS, - poly: &MultiLinearPoly<C::SimdCircuitField>, - eval_point: &ExpanderGKRChallenge<C>, - transcript: &mut T, - scratch_pad: &OrionScratchPad<C::CircuitField, ComPackF>, -) -> OrionProof<C::ChallengeField> -where - C: GKRFieldConfig, - ComPackF: SimdField<Scalar = C::CircuitField>, - OpenPackF: SimdField<Scalar = C::CircuitField>, - T: Transcript<C::ChallengeField>, -{ - let local_point = eval_point.local_xs(); - assert_eq!(eval_point.x_mpi.len(), mpi_world_size.ilog2() as usize); - - let (row_num, msg_size) = { - let num_vars = poly.get_num_vars() + C::SimdCircuitField::PACK_SIZE.ilog2() as usize; - assert_eq!(num_vars, local_point.len()); - - let (row_field_elems, msg_size) = OrionSRS::evals_shape::<C::CircuitField>(num_vars); - let row_num = row_field_elems / C::SimdCircuitField::PACK_SIZE; - (row_num, msg_size) - }; - - let num_vars_in_row = row_num.ilog2() as usize; - let num_vars_in_unpacked_msg = local_point.len() - num_vars_in_row; - - // NOTE: transpose and shuffle evaluations (repack evaluations in another direction) - // for linear combinations in evaulation/proximity tests - let mut evals = poly.coeffs.clone(); - let packed_shuffled_evals: Vec<OpenPackF> = transpose_and_shuffle_simd(&mut evals, row_num); - drop(evals); - - // NOTE: declare the look up tables for column sums - let tables_num = row_num / OpenPackF::PACK_SIZE; - let mut luts = SubsetSumLUTs::<C::ChallengeField>::new(OpenPackF::PACK_SIZE, tables_num); - assert_eq!(row_num % OpenPackF::PACK_SIZE, 0); - - // NOTE: working on evaluation response of tensor code IOP based PCS - // The difference is that, the LUTs multiply with MPI eq coeff weight - let mut eval_row = vec![C::ChallengeField::ZERO; msg_size * C::SimdCircuitField::PACK_SIZE]; - - let eq_coeffs: Vec<_> = { - let mpi_eq_coeffs = EqPolynomial::build_eq_x_r(&eval_point.x_mpi); - let mpi_eq_weight = mpi_eq_coeffs[mpi_rank]; - let eqs = EqPolynomial::build_eq_x_r(&local_point[num_vars_in_unpacked_msg..]); - eqs.iter().map(|t| *t * mpi_eq_weight).collect() - }; - luts.build(&eq_coeffs); - - izip!(packed_shuffled_evals.chunks(tables_num), &mut eval_row) - .for_each(|(p_col, res)| *res = luts.lookup_and_sum(p_col)); - - // NOTE: draw random linear combination out - // and compose proximity response(s) of tensor code IOP based PCS - let proximity_reps = pk.proximity_repetitions::<C::ChallengeField>(PCS_SOUNDNESS_BITS); - let mut proximity_rows = - vec![ - vec![C::ChallengeField::ZERO; msg_size * C::SimdCircuitField::PACK_SIZE]; - proximity_reps - ]; - - proximity_rows.iter_mut().for_each(|row_buffer| { - let random_coeffs: Vec<_> = { - let mpi_weights = transcript.generate_challenge_field_elements(mpi_world_size); - let mpi_weight = mpi_weights[mpi_rank]; - let random_coeffs = transcript.generate_challenge_field_elements(row_num); - random_coeffs.iter().map(|t| *t * mpi_weight).collect() - }; - luts.build(&random_coeffs); - - izip!(packed_shuffled_evals.chunks(tables_num), row_buffer) - .for_each(|(p_col, res)| *res = luts.lookup_and_sum(p_col)); - }); - drop(luts); - - // NOTE: MT opening for point queries - let query_openings = orion_mt_openings(pk, transcript, scratch_pad); - - OrionProof { - eval_row, - proximity_rows, - query_openings, - } -} - -#[allow(clippy::too_many_arguments)] -pub(crate) fn orion_verify_simd_field_mpi<C, ComPackF, OpenPackF, T>( - mpi_world_size: usize, - mpi_rank: usize, - vk: &OrionSRS, - commitment: &OrionCommitment, - point: &ExpanderGKRChallenge<C>, - evaluation: C::ChallengeField, - transcript: &mut T, - proof: &OrionProof<C::ChallengeField>, -) -> bool -where - C: GKRFieldConfig, - ComPackF: SimdField<Scalar = C::CircuitField>, - OpenPackF: SimdField<Scalar = C::CircuitField>, - T: Transcript<C::ChallengeField>, -{ - let local_xs = point.local_xs(); - - let (row_num, msg_size) = { - let (row_field_elems, msg_size) = OrionSRS::evals_shape::<C::CircuitField>(local_xs.len()); - let row_num = row_field_elems / C::SimdCircuitField::PACK_SIZE; - (row_num, msg_size) - }; - - let num_vars_in_row = row_num.ilog2() as usize; - let num_vars_in_unpacked_msg = local_xs.len() - num_vars_in_row; - - // NOTE: working on evaluation response, evaluate the rest of the response - let mut scratch = vec![C::ChallengeField::ZERO; msg_size * C::SimdCircuitField::PACK_SIZE]; - let final_eval = MultiLinearPoly::evaluate_with_buffer( - &proof.eval_row, - &local_xs[..num_vars_in_unpacked_msg], - &mut scratch, - ); - - let mpi_eq_coeffs = EqPolynomial::build_eq_x_r(&point.x_mpi); - let mpi_eq_weight = mpi_eq_coeffs[mpi_rank]; - if final_eval != evaluation * mpi_eq_weight { - return false; - } - - // NOTE: working on proximity responses, draw random linear combinations - // then draw query points from fiat shamir transcripts - let proximity_reps = vk.proximity_repetitions::<C::ChallengeField>(PCS_SOUNDNESS_BITS); - let random_linear_combinations: Vec<Vec<C::ChallengeField>> = (0..proximity_reps) - .map(|_| { - let mpi_weights = transcript.generate_challenge_field_elements(mpi_world_size); - let mpi_weight = mpi_weights[mpi_rank]; - let random_coeffs = transcript.generate_challenge_field_elements(row_num); - random_coeffs.iter().map(|t| *t * mpi_weight).collect() - }) - .collect(); - let query_num = vk.query_complexity(PCS_SOUNDNESS_BITS); - let query_indices = transcript.generate_challenge_index_vector(query_num); - - // NOTE: check consistency in MT in the opening trees and against the commitment tree - if !orion_mt_verify(vk, &query_indices, &proof.query_openings, commitment) { - return false; - } - - // NOTE: prepare the interleaved alphabets from the MT paths, - // but reshuffle the packed elements into another direction - let mut scratch = vec![C::CircuitField::ZERO; C::SimdCircuitField::PACK_SIZE * row_num]; - let shuffled_interleaved_alphabet: Vec<Vec<OpenPackF>> = proof - .query_openings - .iter() - .map(|c| -> Vec<_> { - let mut elts = c.unpack_field_elems::<C::CircuitField, ComPackF>(); - transpose_in_place(&mut elts, &mut scratch, row_num); - elts.chunks(OpenPackF::PACK_SIZE) - .map(OpenPackF::pack) - .collect() - }) - .collect(); - - // NOTE: declare the look up tables for column sums - let tables_num = row_num / OpenPackF::PACK_SIZE; - let mut luts = SubsetSumLUTs::<C::ChallengeField>::new(OpenPackF::PACK_SIZE, tables_num); - assert_eq!(row_num % OpenPackF::PACK_SIZE, 0); - - let eq_linear_combination = { - let mpi_eq_coeffs = EqPolynomial::build_eq_x_r(&point.x_mpi); - let mpi_eq_weight = mpi_eq_coeffs[mpi_rank]; - let eqs = EqPolynomial::build_eq_x_r(&local_xs[num_vars_in_unpacked_msg..]); - eqs.iter().map(|t| *t * mpi_eq_weight).collect() - }; - - let mut scratch_msg = vec![C::ChallengeField::ZERO; C::SimdCircuitField::PACK_SIZE * msg_size]; - let mut scratch_codeword = - vec![C::ChallengeField::ZERO; C::SimdCircuitField::PACK_SIZE * vk.codeword_len()]; - - izip!(&random_linear_combinations, &proof.proximity_rows) - .chain(iter::once((&eq_linear_combination, &proof.eval_row))) - .all(|(rl, msg)| { - let mut msg_cloned = msg.clone(); - transpose_in_place(&mut msg_cloned, &mut scratch_msg, msg_size); - let mut codeword: Vec<_> = msg_cloned - .chunks(msg_size) - .flat_map(|m| vk.code_instance.encode(m).unwrap()) - .collect(); - transpose_in_place( - &mut codeword, - &mut scratch_codeword, - C::SimdCircuitField::PACK_SIZE, - ); - - luts.build(rl); - - izip!(&query_indices, &shuffled_interleaved_alphabet).all( - |(&qi, interleaved_alphabet)| { - let index = qi % vk.codeword_len(); - - let simd_starts = index * C::SimdCircuitField::PACK_SIZE; - let simd_ends = (index + 1) * C::SimdCircuitField::PACK_SIZE; - - izip!( - &codeword[simd_starts..simd_ends], - interleaved_alphabet.chunks(tables_num) - ) - .all(|(expected_alphabet, packed_index)| { - let alphabet = luts.lookup_and_sum(packed_index); - alphabet == *expected_alphabet - }) - }, - ) - }) -} - pub(crate) fn orion_verify_simd_field_aggregated<C, ComPackF, OpenPackF, T>( mpi_world_size: usize, vk: &OrionSRS, @@ -277,31 +57,23 @@ where // NOTE: working on proximity responses, draw random linear combinations // then draw query points from fiat shamir transcripts let proximity_reps = vk.proximity_repetitions::<C::ChallengeField>(PCS_SOUNDNESS_BITS); - let (proximity_worlds_coeffs, proximity_local_coeffs): (Vec<_>, Vec<_>) = (0..proximity_reps) - .map(|_| { - ( - transcript.generate_challenge_field_elements(mpi_world_size), - transcript.generate_challenge_field_elements(row_num), - ) - }) - .unzip(); + let proximity_local_coeffs: Vec<Vec<C::ChallengeField>> = (0..proximity_reps) + .map(|_| transcript.generate_challenge_field_elements(row_num)) + .collect(); let query_num = vk.query_complexity(PCS_SOUNDNESS_BITS); let query_indices = transcript.generate_challenge_index_vector(query_num); + let proximity_worlds_coeffs: Vec<Vec<C::ChallengeField>> = (0..proximity_reps) + .map(|_| transcript.generate_challenge_field_elements(mpi_world_size)) + .collect(); + // NOTE: check all merkle paths, and check merkle roots against commitment - let (mt_verifications, roots): (Vec<_>, Vec<_>) = proof + let roots: Vec<_> = proof .query_openings .chunks(query_num) - .map(|queries| { - let root = queries[0].root(); - (orion_mt_verify(vk, &query_indices, queries, &root), root) - }) - .unzip(); - - if !itertools::all(&mt_verifications, |v| *v) { - return false; - } + .map(|qs| qs[0].root()) + .collect(); let final_tree_height = 1 + roots.len().ilog2(); let (internals, _) = tree::Tree::new_with_leaf_nodes(roots, final_tree_height); diff --git a/poly_commit/src/orion/simd_field_agg_tests.rs b/poly_commit/src/orion/simd_field_agg_tests.rs index 78d9160a..db335063 100644 --- a/poly_commit/src/orion/simd_field_agg_tests.rs +++ b/poly_commit/src/orion/simd_field_agg_tests.rs @@ -6,7 +6,7 @@ use gf2::{GF2x128, GF2x8}; use gf2_128::GF2_128; use gkr_field_config::{GF2ExtConfig, GKRFieldConfig}; use itertools::izip; -use polynomials::MultiLinearPoly; +use polynomials::{EqPolynomial, MultiLinearPoly}; use transcript::{BytesHashTranscript, Keccak256hasher, Transcript}; use crate::{ @@ -32,6 +32,7 @@ where fn orion_proof_aggregate<C, T>( openings: &[OrionProof<C::ChallengeField>], x_mpi: &[C::ChallengeField], + transcript: &mut T, ) -> OrionProof<C::ChallengeField> where C: GKRFieldConfig, @@ -48,19 +49,25 @@ where let aggregated_proximity_rows = (0..proximity_reps) .map(|i| { + let weights = transcript.generate_challenge_field_elements(num_parties); let mut rows: Vec<_> = openings .iter() .flat_map(|o| o.proximity_rows[i].clone()) .collect(); transpose_in_place(&mut rows, &mut scratch, num_parties); - rows.chunks(num_parties).map(|c| c.iter().sum()).collect() + rows.chunks(num_parties) + .map(|c| izip!(c, &weights).map(|(&l, &r)| l * r).sum()) + .collect() }) .collect(); let aggregated_eval_row: Vec<_> = { + let eq_worlds_coeffs = EqPolynomial::build_eq_x_r(x_mpi); let mut rows: Vec<_> = openings.iter().flat_map(|o| o.eval_row.clone()).collect(); transpose_in_place(&mut rows, &mut scratch, num_parties); - rows.chunks(num_parties).map(|c| c.iter().sum()).collect() + rows.chunks(num_parties) + .map(|c| izip!(c, &eq_worlds_coeffs).map(|(&l, &r)| l * r).sum()) + .collect() }; OrionProof { @@ -138,7 +145,7 @@ fn test_orion_simd_aggregate_verify_helper<C, ComPackF, OpenPackF, T>( .collect(); let final_tree_height = 1 + roots.len().ilog2(); - let (internals, _) = tree::Tree::new_with_leaf_nodes(roots.clone(), final_tree_height); + let (internals, _) = tree::Tree::new_with_leaf_nodes(roots, final_tree_height); internals[0] }; @@ -146,22 +153,28 @@ fn test_orion_simd_aggregate_verify_helper<C, ComPackF, OpenPackF, T>( &mut committee, global_poly.coeffs.chunks(1 << local_real_num_vars) ) - .enumerate() - .map(|(i, (committer, eval_slice))| { + .map(|(committer, eval_slice)| { let cloned_poly = MultiLinearPoly::new(eval_slice.to_vec()); - orion_open_simd_field_mpi::<C, ComPackF, OpenPackF, T>( - num_parties, - i, + orion_open_simd_field::< + C::CircuitField, + C::SimdCircuitField, + C::ChallengeField, + ComPackF, + OpenPackF, + T, + >( &srs, &cloned_poly, - &gkr_challenge, + &gkr_challenge.local_xs(), &mut committer.transcript, &committer.scratch_pad, ) }) .collect(); - let aggregated_proof = orion_proof_aggregate::<C, T>(&openings, &gkr_challenge.x_mpi); + let mut aggregator_transcript = committee[0].transcript.clone(); + let aggregated_proof = + orion_proof_aggregate::<C, T>(&openings, &gkr_challenge.x_mpi, &mut aggregator_transcript); let mut scratch = vec![C::ChallengeField::ZERO; 1 << num_vars_in_unpacked_msg]; let final_expected_eval = MultiLinearPoly::evaluate_with_buffer( diff --git a/poly_commit/src/orion/simd_field_impl.rs b/poly_commit/src/orion/simd_field_impl.rs index a0b28634..c1951575 100644 --- a/poly_commit/src/orion/simd_field_impl.rs +++ b/poly_commit/src/orion/simd_field_impl.rs @@ -6,7 +6,10 @@ use polynomials::{EqPolynomial, MultiLinearPoly}; use transcript::Transcript; use crate::{ - orion::{utils::*, OrionCommitment, OrionProof, OrionResult, OrionSRS, OrionScratchPad}, + orion::{ + utils::{commit_encoded, orion_mt_openings, orion_mt_verify, transpose_in_place}, + OrionCommitment, OrionProof, OrionResult, OrionSRS, OrionScratchPad, + }, traits::TensorCodeIOPPCS, SubsetSumLUTs, PCS_SOUNDNESS_BITS, }; @@ -68,6 +71,33 @@ where commit_encoded(pk, &packed_evals, scratch_pad, packed_rows, msg_size) } +#[inline(always)] +fn transpose_and_shuffle_simd<F, SimdF, PackF>( + evaluations: &mut [SimdF], + row_num: usize, +) -> Vec<PackF> +where + F: Field, + SimdF: SimdField<Scalar = F>, + PackF: SimdField<Scalar = F>, +{ + // NOTE: pre transpose evaluations + let mut scratch = vec![SimdF::ZERO; evaluations.len()]; + transpose_in_place(evaluations, &mut scratch, row_num); + drop(scratch); + + // NOTE: reshuffle the transposed matrix, from SIMD over row to SIMD over col + let mut scratch = vec![F::ZERO; SimdF::PACK_SIZE * row_num]; + evaluations + .chunks(row_num) + .flat_map(|row_simds| -> Vec<_> { + let mut elts: Vec<_> = row_simds.iter().flat_map(|f| f.unpack()).collect(); + transpose_in_place(&mut elts, &mut scratch, row_num); + elts.chunks(PackF::PACK_SIZE).map(PackF::pack).collect() + }) + .collect() +} + // NOTE: this implementation doesn't quite align with opening for // multilinear polynomials over base field, // as this directly plug into GKR argument system. @@ -103,6 +133,8 @@ where // NOTE: transpose and shuffle evaluations (repack evaluations in another direction) // for linear combinations in evaulation/proximity tests let mut evals = poly.coeffs.clone(); + assert_eq!(evals.len() * SimdF::PACK_SIZE % OpenPackF::PACK_SIZE, 0); + let packed_shuffled_evals: Vec<OpenPackF> = transpose_and_shuffle_simd(&mut evals, row_num); drop(evals); @@ -122,8 +154,9 @@ where // NOTE: draw random linear combination out // and compose proximity response(s) of tensor code IOP based PCS - let proximity_reps = pk.proximity_repetitions::<EvalF>(PCS_SOUNDNESS_BITS); - let mut proximity_rows = vec![vec![EvalF::ZERO; msg_size * SimdF::PACK_SIZE]; proximity_reps]; + let proximity_test_num = pk.proximity_repetitions::<EvalF>(PCS_SOUNDNESS_BITS); + let mut proximity_rows = + vec![vec![EvalF::ZERO; msg_size * SimdF::PACK_SIZE]; proximity_test_num]; proximity_rows.iter_mut().for_each(|row_buffer| { let random_coeffs = transcript.generate_challenge_field_elements(row_num); diff --git a/poly_commit/src/orion/utils.rs b/poly_commit/src/orion/utils.rs index 479e9601..17757bcc 100644 --- a/poly_commit/src/orion/utils.rs +++ b/poly_commit/src/orion/utils.rs @@ -209,35 +209,6 @@ pub(crate) fn transpose_in_place<F: Field>(mat: &mut [F], scratch: &mut [F], row mat.copy_from_slice(scratch); } -#[inline(always)] -pub(crate) fn transpose_and_shuffle_simd<F, SimdF, PackF>( - evaluations: &mut [SimdF], - row_num: usize, -) -> Vec<PackF> -where - F: Field, - SimdF: SimdField<Scalar = F>, - PackF: SimdField<Scalar = F>, -{ - assert_eq!(evaluations.len() * SimdF::PACK_SIZE % PackF::PACK_SIZE, 0); - - // NOTE: pre transpose evaluations - let mut scratch = vec![SimdF::ZERO; evaluations.len()]; - transpose_in_place(evaluations, &mut scratch, row_num); - drop(scratch); - - // NOTE: reshuffle the transposed matrix, from SIMD over row to SIMD over col - let mut scratch = vec![F::ZERO; SimdF::PACK_SIZE * row_num]; - evaluations - .chunks(row_num) - .flat_map(|row_simds| -> Vec<_> { - let mut elts: Vec<_> = row_simds.iter().flat_map(|f| f.unpack()).collect(); - transpose_in_place(&mut elts, &mut scratch, row_num); - elts.chunks(PackF::PACK_SIZE).map(PackF::pack).collect() - }) - .collect() -} - /* * LINEAR OPERATIONS */ From f1e298cccfa3d5a067c56739d7674725539d9230 Mon Sep 17 00:00:00 2001 From: Hang Su <tonyfloater@gmail.com> Date: Wed, 4 Dec 2024 20:26:31 -0500 Subject: [PATCH 39/65] use common toolset in pcs testing --- poly_commit/src/traits.rs | 1 + poly_commit/tests/common.rs | 1 - poly_commit/tests/test_orion.rs | 108 +++++--------------------------- 3 files changed, 15 insertions(+), 95 deletions(-) diff --git a/poly_commit/src/traits.rs b/poly_commit/src/traits.rs index 6ce0616f..b0e40255 100644 --- a/poly_commit/src/traits.rs +++ b/poly_commit/src/traits.rs @@ -65,6 +65,7 @@ pub trait PolynomialCommitmentScheme<F: Field, T: Transcript<F>> { ) -> bool; } +#[derive(Debug, Clone)] pub struct ExpanderGKRChallenge<C: GKRFieldConfig> { pub x: Vec<C::ChallengeField>, pub x_simd: Vec<C::ChallengeField>, diff --git a/poly_commit/tests/common.rs b/poly_commit/tests/common.rs index 6175842e..55b2c633 100644 --- a/poly_commit/tests/common.rs +++ b/poly_commit/tests/common.rs @@ -45,7 +45,6 @@ pub fn test_pcs<F: Field, T: Transcript<F>, P: PolynomialCommitmentScheme<F, T>> } } -#[allow(unused)] pub fn test_pcs_for_expander_gkr< C: GKRFieldConfig, T: Transcript<C::ChallengeField>, diff --git a/poly_commit/tests/test_orion.rs b/poly_commit/tests/test_orion.rs index 6dcec575..8a79bedb 100644 --- a/poly_commit/tests/test_orion.rs +++ b/poly_commit/tests/test_orion.rs @@ -8,7 +8,6 @@ use gkr_field_config::{GF2ExtConfig, GKRFieldConfig}; use mpi_config::MPIConfig; use poly_commit::*; use polynomials::MultiLinearPoly; -use raw::RawExpanderGKR; use transcript::{BytesHashTranscript, Keccak256hasher, Transcript}; const TEST_REPETITION: usize = 3; @@ -111,17 +110,6 @@ where MultiLinearPoly::<C::SimdCircuitField>::random(num_vars - num_vars_in_simd, &mut rng); // NOTE generate srs for each party, and shared challenge point in each party - let srs = - <OrionSIMDFieldPCS< - C::CircuitField, - C::SimdCircuitField, - C::ChallengeField, - ComPackF, - OpenPackF, - T, - > as PCSForExpanderGKR<C, T>>::gen_srs_for_testing(&num_vars, &mpi_config, &mut rng); - let (pk, vk) = srs.into_keys(); - let challenge_point = ExpanderGKRChallenge::<C> { x_mpi: (0..num_vars_in_mpi) .map(|_| C::ChallengeField::random_unsafe(&mut rng)) @@ -134,31 +122,11 @@ where .collect(), }; - let mut scratch_pad = - <OrionSIMDFieldPCS< - C::CircuitField, - C::SimdCircuitField, - C::ChallengeField, - ComPackF, - OpenPackF, - T, - > as PCSForExpanderGKR<C, T>>::init_scratch_pad(&num_vars, &mpi_config); - - let mut local_prover_transcript = T::new(); - let mut local_verifier_transcript = T::new(); - - let expected_global_eval = RawExpanderGKR::<C, T>::eval( - &poly.coeffs, - &challenge_point.x, - &challenge_point.x_simd, - &challenge_point.x_mpi, - ); + let mut transcript = T::new(); dbg!(poly.get_num_vars(), poly.coeffs[0]); - dbg!(pk.num_vars); dbg!(&challenge_point.x_mpi); dbg!(mpi_config.world_size(), mpi_config.world_rank()); - dbg!(expected_global_eval); // NOTE separate polynomial into different pieces by mpi rank let poly_vars_stride = (1 << poly.get_num_vars()) / mpi_config.world_size(); @@ -166,75 +134,27 @@ where let poly_coeff_ends = poly_coeff_starts + poly_vars_stride; let local_poly = MultiLinearPoly::new(poly.coeffs[poly_coeff_starts..poly_coeff_ends].to_vec()); - let expected_local_eval = RawExpanderGKR::<C, T>::eval_local( - &local_poly.coeffs, - &challenge_point.x, - &challenge_point.x_simd, - ); - dbg!(local_poly.get_num_vars(), local_poly.coeffs[0]); - // NOTE commit polynomial in different parts - let commitment = <OrionSIMDFieldPCS< - C::CircuitField, - C::SimdCircuitField, - C::ChallengeField, - ComPackF, - OpenPackF, + common::test_pcs_for_expander_gkr::< + C, T, - > as PCSForExpanderGKR<C, T>>::commit( - &num_vars, - &mpi_config, - &pk, - &local_poly, - &mut scratch_pad, - ); - dbg!(commitment); - - // NOTE: open polynomial in different parts - let opening = <OrionSIMDFieldPCS< - C::CircuitField, - C::SimdCircuitField, - C::ChallengeField, - ComPackF, - OpenPackF, - T, - > as PCSForExpanderGKR<C, T>>::open( + OrionSIMDFieldPCS< + C::CircuitField, + C::SimdCircuitField, + C::ChallengeField, + ComPackF, + OpenPackF, + T, + >, + >( &num_vars, &mpi_config, - &pk, + &mut transcript, &local_poly, - &challenge_point, - &mut local_prover_transcript, - &mut scratch_pad, - ); - dbg!(opening.query_openings.len()); - - // NOTE verify polynomial in different parts - let pass = <OrionSIMDFieldPCS< - C::CircuitField, - C::SimdCircuitField, - C::ChallengeField, - ComPackF, - OpenPackF, - T, - > as PCSForExpanderGKR<C, T>>::verify( - &num_vars, - &mpi_config, - &vk, - &commitment, - &challenge_point, - if mpi_config.is_root() { - expected_global_eval - } else { - expected_local_eval - }, - &mut local_verifier_transcript, - &opening, + &vec![challenge_point], ); - assert!(pass); - MPIConfig::finalize() } From df91b0a741ba5ac5d5687e2c0be62325f08c26c1 Mon Sep 17 00:00:00 2001 From: Hang Su <tonyfloater@gmail.com> Date: Wed, 4 Dec 2024 23:35:16 -0500 Subject: [PATCH 40/65] pcs init for testing, take rng from the context --- gkr/benches/gkr_hashes.rs | 3 +++ gkr/src/exec.rs | 8 ++++++++ gkr/src/main.rs | 3 +++ gkr/src/main_mpi.rs | 4 ++++ gkr/src/tests/gkr_correctness.rs | 4 +++- poly_commit/src/lib.rs | 3 +-- poly_commit/src/orion/pcs_for_expander_gkr.rs | 2 +- 7 files changed, 23 insertions(+), 4 deletions(-) diff --git a/gkr/benches/gkr_hashes.rs b/gkr/benches/gkr_hashes.rs index 1256fa3b..07d1b3aa 100644 --- a/gkr/benches/gkr_hashes.rs +++ b/gkr/benches/gkr_hashes.rs @@ -15,6 +15,7 @@ use poly_commit::{ expander_pcs_init_testing_only, raw::RawExpanderGKR, PCSForExpanderGKR, StructuredReferenceString, }; +use rand::thread_rng; use std::hint::black_box; use transcript::{BytesHashTranscript, SHA256hasher}; @@ -55,10 +56,12 @@ fn benchmark_setup<Cfg: GKRConfig>( circuit.set_random_input_for_test(); } + let mut rng = thread_rng(); let (pcs_params, pcs_proving_key, _pcs_verification_key, pcs_scratch) = expander_pcs_init_testing_only::<Cfg::FieldConfig, Cfg::Transcript, Cfg::PCS>( circuit.log_input_size(), &config.mpi_config, + &mut rng, ); (config, circuit, pcs_params, pcs_proving_key, pcs_scratch) diff --git a/gkr/src/exec.rs b/gkr/src/exec.rs index 65b96a8b..5c9acab3 100644 --- a/gkr/src/exec.rs +++ b/gkr/src/exec.rs @@ -16,6 +16,7 @@ use gkr_field_config::{BN254Config, GF2ExtConfig, GKRFieldConfig, M31ExtConfig}; use mpi_config::MPIConfig; use poly_commit::{expander_pcs_init_testing_only, raw::RawExpanderGKR}; +use rand::thread_rng; use transcript::{BytesHashTranscript, FieldHashTranscript, MIMCHasher, SHA256hasher}; use log::{debug, info}; @@ -78,10 +79,13 @@ async fn run_command<'a, Cfg: GKRConfig>( let mut prover = gkr::Prover::new(&config); prover.prepare_mem(&circuit); // TODO: Read PCS setup from files + + let mut rng = thread_rng(); let (pcs_params, pcs_proving_key, _pcs_verification_key, mut pcs_scratch) = expander_pcs_init_testing_only::<Cfg::FieldConfig, Cfg::Transcript, Cfg::PCS>( circuit.log_input_size(), &config.mpi_config, + &mut rng, ); let (claimed_v, proof) = prover.prove( @@ -119,10 +123,12 @@ async fn run_command<'a, Cfg: GKRConfig>( load_proof_and_claimed_v(&bytes).expect("Unable to deserialize proof."); // TODO: Read PCS setup from files + let mut rng = thread_rng(); let (pcs_params, _pcs_proving_key, pcs_verification_key, mut _pcs_scratch) = expander_pcs_init_testing_only::<Cfg::FieldConfig, Cfg::Transcript, Cfg::PCS>( circuit.log_input_size(), &config.mpi_config, + &mut rng, ); let verifier = gkr::Verifier::new(&config); let public_input = circuit.public_input.clone(); @@ -150,10 +156,12 @@ async fn run_command<'a, Cfg: GKRConfig>( let verifier = gkr::Verifier::new(&config); // TODO: Read PCS setup from files + let mut rng = thread_rng(); let (pcs_params, pcs_proving_key, pcs_verification_key, pcs_scratch) = expander_pcs_init_testing_only::<Cfg::FieldConfig, Cfg::Transcript, Cfg::PCS>( circuit.log_input_size(), &config.mpi_config, + &mut rng, ); let circuit = Arc::new(Mutex::new(circuit)); diff --git a/gkr/src/main.rs b/gkr/src/main.rs index 52b204aa..a0b12460 100644 --- a/gkr/src/main.rs +++ b/gkr/src/main.rs @@ -11,6 +11,7 @@ use gkr_field_config::{BN254Config, GF2ExtConfig, GKRFieldConfig, M31ExtConfig}; use mpi_config::MPIConfig; use poly_commit::{expander_pcs_init_testing_only, raw::RawExpanderGKR}; +use rand::thread_rng; use transcript::{BytesHashTranscript, SHA256hasher}; use gkr::{ @@ -175,10 +176,12 @@ fn run_benchmark<Cfg: GKRConfig>(args: &Args, config: Config<Cfg>) { println!("Circuit loaded!"); + let mut rng = thread_rng(); let (pcs_params, pcs_proving_key, _pcs_verification_key, pcs_scratch) = expander_pcs_init_testing_only::<Cfg::FieldConfig, Cfg::Transcript, Cfg::PCS>( circuit_template.log_input_size(), &config.mpi_config, + &mut rng, ); let start_time = std::time::Instant::now(); diff --git a/gkr/src/main_mpi.rs b/gkr/src/main_mpi.rs index 72f8c472..2cb5a895 100644 --- a/gkr/src/main_mpi.rs +++ b/gkr/src/main_mpi.rs @@ -6,6 +6,7 @@ use mpi_config::MPIConfig; use gkr_field_config::{BN254Config, GF2ExtConfig, GKRFieldConfig, M31ExtConfig}; use poly_commit::{expander_pcs_init_testing_only, raw::RawExpanderGKR}; +use rand::thread_rng; use transcript::{BytesHashTranscript, SHA256hasher}; use gkr::{ @@ -154,10 +155,13 @@ fn run_benchmark<Cfg: GKRConfig>(args: &Args, config: Config<Cfg>) { let mut prover = Prover::new(&config); prover.prepare_mem(&circuit); + + let mut rng = thread_rng(); let (pcs_params, pcs_proving_key, _pcs_verification_key, mut pcs_scratch) = expander_pcs_init_testing_only::<Cfg::FieldConfig, Cfg::Transcript, Cfg::PCS>( circuit.log_input_size(), &config.mpi_config, + &mut rng, ); const N_PROOF: usize = 1000; diff --git a/gkr/src/tests/gkr_correctness.rs b/gkr/src/tests/gkr_correctness.rs index 939ee7c7..c020903c 100644 --- a/gkr/src/tests/gkr_correctness.rs +++ b/gkr/src/tests/gkr_correctness.rs @@ -11,7 +11,7 @@ use gkr_field_config::{BN254Config, FieldType, GF2ExtConfig, GKRFieldConfig, M31 use mpi_config::{root_println, MPIConfig}; use poly_commit::expander_pcs_init_testing_only; use poly_commit::raw::RawExpanderGKR; -use rand::Rng; +use rand::{thread_rng, Rng}; use sha2::Digest; use transcript::{ BytesHashTranscript, FieldHashTranscript, Keccak256hasher, MIMCHasher, SHA256hasher, @@ -145,10 +145,12 @@ fn test_gkr_correctness_helper<Cfg: GKRConfig>(config: &Config<Cfg>, write_proof let mut prover = Prover::new(config); prover.prepare_mem(&circuit); + let mut rng = thread_rng(); let (pcs_params, pcs_proving_key, pcs_verification_key, mut pcs_scratch) = expander_pcs_init_testing_only::<Cfg::FieldConfig, Cfg::Transcript, Cfg::PCS>( circuit.log_input_size(), &config.mpi_config, + &mut rng, ); let proving_start = Instant::now(); diff --git a/poly_commit/src/lib.rs b/poly_commit/src/lib.rs index 21efebb9..86460583 100644 --- a/poly_commit/src/lib.rs +++ b/poly_commit/src/lib.rs @@ -1,7 +1,6 @@ mod traits; use gkr_field_config::GKRFieldConfig; use mpi_config::MPIConfig; -use rand::thread_rng; pub use traits::{ ExpanderGKRChallenge, PCSForExpanderGKR, PolynomialCommitmentScheme, StructuredReferenceString, }; @@ -14,13 +13,13 @@ pub fn expander_pcs_init_testing_only< >( n_input_vars: usize, mpi_config: &MPIConfig, + mut rng: impl rand::RngCore, ) -> ( PCS::Params, <PCS::SRS as StructuredReferenceString>::PKey, <PCS::SRS as StructuredReferenceString>::VKey, PCS::ScratchPad, ) { - let mut rng = thread_rng(); let pcs_params = <PCS as PCSForExpanderGKR<FieldConfig, T>>::gen_params(n_input_vars); let pcs_setup = <PCS as PCSForExpanderGKR<FieldConfig, T>>::gen_srs_for_testing( &pcs_params, diff --git a/poly_commit/src/orion/pcs_for_expander_gkr.rs b/poly_commit/src/orion/pcs_for_expander_gkr.rs index 3d94fd4c..a9cd818a 100644 --- a/poly_commit/src/orion/pcs_for_expander_gkr.rs +++ b/poly_commit/src/orion/pcs_for_expander_gkr.rs @@ -26,7 +26,7 @@ where OpenPackF: SimdField<Scalar = C::CircuitField>, T: Transcript<C::ChallengeField>, { - const NAME: &'static str = "OrionSIMDPCSForExpanderGKR"; + const NAME: &'static str = "OrionPCSForExpanderGKR"; type Params = usize; type ScratchPad = OrionScratchPad<C::CircuitField, ComPackF>; From 1bd79e450cd32fd976f785b7297cb9e95b737ba4 Mon Sep 17 00:00:00 2001 From: Hang Su <tonyfloater@gmail.com> Date: Wed, 4 Dec 2024 23:45:38 -0500 Subject: [PATCH 41/65] minor - refactoring in favor of poly_commit lib export --- poly_commit/src/lib.rs | 38 +------------------------------------- poly_commit/src/utils.rs | 38 +++++++++++++++++++++++++++++++++++++- 2 files changed, 38 insertions(+), 38 deletions(-) diff --git a/poly_commit/src/lib.rs b/poly_commit/src/lib.rs index 86460583..a0a348ca 100644 --- a/poly_commit/src/lib.rs +++ b/poly_commit/src/lib.rs @@ -1,48 +1,12 @@ mod traits; -use gkr_field_config::GKRFieldConfig; -use mpi_config::MPIConfig; pub use traits::{ ExpanderGKRChallenge, PCSForExpanderGKR, PolynomialCommitmentScheme, StructuredReferenceString, }; -#[allow(clippy::type_complexity)] -pub fn expander_pcs_init_testing_only< - FieldConfig: GKRFieldConfig, - T: Transcript<FieldConfig::ChallengeField>, - PCS: PCSForExpanderGKR<FieldConfig, T>, ->( - n_input_vars: usize, - mpi_config: &MPIConfig, - mut rng: impl rand::RngCore, -) -> ( - PCS::Params, - <PCS::SRS as StructuredReferenceString>::PKey, - <PCS::SRS as StructuredReferenceString>::VKey, - PCS::ScratchPad, -) { - let pcs_params = <PCS as PCSForExpanderGKR<FieldConfig, T>>::gen_params(n_input_vars); - let pcs_setup = <PCS as PCSForExpanderGKR<FieldConfig, T>>::gen_srs_for_testing( - &pcs_params, - mpi_config, - &mut rng, - ); - let (pcs_proving_key, pcs_verification_key) = pcs_setup.into_keys(); - let pcs_scratch = - <PCS as PCSForExpanderGKR<FieldConfig, T>>::init_scratch_pad(&pcs_params, mpi_config); - - ( - pcs_params, - pcs_proving_key, - pcs_verification_key, - pcs_scratch, - ) -} - pub const PCS_SOUNDNESS_BITS: usize = 128; mod utils; -use transcript::Transcript; -use utils::PCSEmptyType; +pub use utils::{expander_pcs_init_testing_only, PCSEmptyType}; pub mod raw; diff --git a/poly_commit/src/utils.rs b/poly_commit/src/utils.rs index eb50f192..290c0c62 100644 --- a/poly_commit/src/utils.rs +++ b/poly_commit/src/utils.rs @@ -1,5 +1,8 @@ -use crate::StructuredReferenceString; +use crate::{PCSForExpanderGKR, StructuredReferenceString}; use arith::FieldSerde; +use gkr_field_config::GKRFieldConfig; +use mpi_config::MPIConfig; +use transcript::Transcript; #[derive(Clone, Debug, Default)] pub struct PCSEmptyType {} @@ -24,3 +27,36 @@ impl StructuredReferenceString for PCSEmptyType { (Self {}, Self {}) } } + +#[allow(clippy::type_complexity)] +pub fn expander_pcs_init_testing_only< + FieldConfig: GKRFieldConfig, + T: Transcript<FieldConfig::ChallengeField>, + PCS: PCSForExpanderGKR<FieldConfig, T>, +>( + n_input_vars: usize, + mpi_config: &MPIConfig, + mut rng: impl rand::RngCore, +) -> ( + PCS::Params, + <PCS::SRS as StructuredReferenceString>::PKey, + <PCS::SRS as StructuredReferenceString>::VKey, + PCS::ScratchPad, +) { + let pcs_params = <PCS as PCSForExpanderGKR<FieldConfig, T>>::gen_params(n_input_vars); + let pcs_setup = <PCS as PCSForExpanderGKR<FieldConfig, T>>::gen_srs_for_testing( + &pcs_params, + mpi_config, + &mut rng, + ); + let (pcs_proving_key, pcs_verification_key) = pcs_setup.into_keys(); + let pcs_scratch = + <PCS as PCSForExpanderGKR<FieldConfig, T>>::init_scratch_pad(&pcs_params, mpi_config); + + ( + pcs_params, + pcs_proving_key, + pcs_verification_key, + pcs_scratch, + ) +} From 446b838be31211657113570a0fade7d0dedcf0dc Mon Sep 17 00:00:00 2001 From: Hang Su <tonyfloater@gmail.com> Date: Thu, 5 Dec 2024 01:29:23 -0500 Subject: [PATCH 42/65] minor - reformatting for code structure --- poly_commit/src/orion/simd_field_agg_impl.rs | 21 +++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/poly_commit/src/orion/simd_field_agg_impl.rs b/poly_commit/src/orion/simd_field_agg_impl.rs index 632c3e74..5958a295 100644 --- a/poly_commit/src/orion/simd_field_agg_impl.rs +++ b/poly_commit/src/orion/simd_field_agg_impl.rs @@ -68,16 +68,19 @@ where .map(|_| transcript.generate_challenge_field_elements(mpi_world_size)) .collect(); - // NOTE: check all merkle paths, and check merkle roots against commitment - let roots: Vec<_> = proof - .query_openings - .chunks(query_num) - .map(|qs| qs[0].root()) - .collect(); + let final_root = { + // NOTE: check all merkle paths, and check merkle roots against commitment + let roots: Vec<_> = proof + .query_openings + .chunks(query_num) + .map(|qs| qs[0].root()) + .collect(); - let final_tree_height = 1 + roots.len().ilog2(); - let (internals, _) = tree::Tree::new_with_leaf_nodes(roots, final_tree_height); - if internals[0] != *commitment { + let final_tree_height = 1 + roots.len().ilog2(); + let (internals, _) = tree::Tree::new_with_leaf_nodes(roots, final_tree_height); + internals[0] + }; + if final_root != *commitment { return false; } From ca880043061c1ca3150bdd1a19a16fd8de434af0 Mon Sep 17 00:00:00 2001 From: Hang Su <tonyfloater@gmail.com> Date: Thu, 5 Dec 2024 08:15:53 -0500 Subject: [PATCH 43/65] minor, circle back to the improved query complexity --- poly_commit/src/traits.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/poly_commit/src/traits.rs b/poly_commit/src/traits.rs index b0e40255..902c4969 100644 --- a/poly_commit/src/traits.rs +++ b/poly_commit/src/traits.rs @@ -177,9 +177,8 @@ pub(crate) trait TensorCodeIOPPCS { } fn query_complexity(&self, soundness_bits: usize) -> usize { - // NOTE: use Ligero (AHIV22) or Avg-case dist to a code (BKS18) - // version of avg case dist in unique decoding technique. - let avg_case_dist = self.hamming_weight() / 3f64; + // NOTE: use Ligero (AHIV22) appendix C argument. + let avg_case_dist = self.hamming_weight() / 2f64; let sec_bits = -(1f64 - avg_case_dist).log2(); (soundness_bits as f64 / sec_bits).ceil() as usize From 7d6436ba2cb4d39041afee55549f9c45e313e86d Mon Sep 17 00:00:00 2001 From: Hang Su <tonyfloater@gmail.com> Date: Tue, 17 Dec 2024 14:33:16 -0500 Subject: [PATCH 44/65] minor - use multilinear extension trait for orion base field impl --- poly_commit/src/orion/base_field_impl.rs | 29 ++++++++------------ poly_commit/src/orion/simd_field_agg_impl.rs | 5 ++-- poly_commit/src/orion/simd_field_impl.rs | 9 ++---- 3 files changed, 17 insertions(+), 26 deletions(-) diff --git a/poly_commit/src/orion/base_field_impl.rs b/poly_commit/src/orion/base_field_impl.rs index c336784e..908c0999 100644 --- a/poly_commit/src/orion/base_field_impl.rs +++ b/poly_commit/src/orion/base_field_impl.rs @@ -2,7 +2,7 @@ use std::iter; use arith::{ExtensionField, Field, SimdField}; use itertools::izip; -use polynomials::{EqPolynomial, MultiLinearPoly}; +use polynomials::{EqPolynomial, MultilinearExtension, RefMultiLinearPoly}; use transcript::Transcript; use crate::{ @@ -34,18 +34,18 @@ where pub fn orion_commit_base_field<F, ComPackF>( pk: &OrionSRS, - poly: &MultiLinearPoly<F>, + poly: &impl MultilinearExtension<F>, scratch_pad: &mut OrionScratchPad<F, ComPackF>, ) -> OrionResult<OrionCommitment> where F: Field, ComPackF: SimdField<Scalar = F>, { - let (row_num, msg_size) = OrionSRS::evals_shape::<F>(poly.get_num_vars()); + let (row_num, msg_size) = OrionSRS::evals_shape::<F>(poly.num_vars()); let packed_rows = row_num / ComPackF::PACK_SIZE; assert_eq!(row_num % ComPackF::PACK_SIZE, 0); - let mut evals = poly.coeffs.clone(); + let mut evals = poly.hypercube_basis(); assert_eq!(evals.len() % ComPackF::PACK_SIZE, 0); let mut packed_evals: Vec<ComPackF> = transpose_and_pack(&mut evals, row_num); @@ -61,7 +61,7 @@ where pub fn orion_open_base_field<F, EvalF, ComPackF, OpenPackF, T>( pk: &OrionSRS, - poly: &MultiLinearPoly<F>, + poly: &impl MultilinearExtension<F>, point: &[EvalF], transcript: &mut T, scratch_pad: &OrionScratchPad<F, ComPackF>, @@ -73,11 +73,11 @@ where OpenPackF: SimdField<Scalar = F>, T: Transcript<EvalF>, { - let (row_num, msg_size) = OrionSRS::evals_shape::<F>(poly.get_num_vars()); + let (row_num, msg_size) = OrionSRS::evals_shape::<F>(poly.num_vars()); let num_vars_in_row = row_num.ilog2() as usize; // NOTE: transpose evaluations for linear combinations in evaulation/proximity tests - let mut evals = poly.coeffs.clone(); + let mut evals = poly.hypercube_basis(); assert_eq!(evals.len() % OpenPackF::PACK_SIZE, 0); let packed_evals: Vec<OpenPackF> = transpose_and_pack(&mut evals, row_num); @@ -113,11 +113,8 @@ where // NOTE: working on evaluation on top of evaluation response let mut scratch = vec![EvalF::ZERO; msg_size]; - let eval = MultiLinearPoly::evaluate_with_buffer( - &eval_row, - &point[..point.len() - num_vars_in_row], - &mut scratch, - ); + let eval = RefMultiLinearPoly::from_ref(&eval_row) + .evaluate_with_buffer(&point[..point.len() - num_vars_in_row], &mut scratch); drop(scratch); // NOTE: MT opening for point queries @@ -153,11 +150,9 @@ where // NOTE: working on evaluation response, evaluate the rest of the response let mut scratch = vec![EvalF::ZERO; msg_size]; - let final_eval = MultiLinearPoly::evaluate_with_buffer( - &proof.eval_row, - &point[..num_vars_in_msg], - &mut scratch, - ); + let final_eval = RefMultiLinearPoly::from_ref(&proof.eval_row) + .evaluate_with_buffer(&point[..num_vars_in_msg], &mut scratch); + if final_eval != evaluation { return false; } diff --git a/poly_commit/src/orion/simd_field_agg_impl.rs b/poly_commit/src/orion/simd_field_agg_impl.rs index 5958a295..73acf0de 100644 --- a/poly_commit/src/orion/simd_field_agg_impl.rs +++ b/poly_commit/src/orion/simd_field_agg_impl.rs @@ -3,7 +3,7 @@ use std::iter; use arith::{Field, SimdField}; use gkr_field_config::GKRFieldConfig; use itertools::izip; -use polynomials::{EqPolynomial, MultiLinearPoly}; +use polynomials::{EqPolynomial, MultilinearExtension, RefMultiLinearPoly}; use transcript::Transcript; use crate::{ @@ -45,8 +45,7 @@ where // NOTE: working on evaluation response let mut scratch = vec![C::ChallengeField::ZERO; mpi_world_size * C::SimdCircuitField::PACK_SIZE * msg_size]; - let final_eval = MultiLinearPoly::evaluate_with_buffer( - &proof.eval_row, + let final_eval = RefMultiLinearPoly::from_ref(&proof.eval_row).evaluate_with_buffer( &local_xs[..num_vars_in_unpacked_msg], &mut scratch[..C::SimdCircuitField::PACK_SIZE * msg_size], ); diff --git a/poly_commit/src/orion/simd_field_impl.rs b/poly_commit/src/orion/simd_field_impl.rs index 953fd3cd..db293936 100644 --- a/poly_commit/src/orion/simd_field_impl.rs +++ b/poly_commit/src/orion/simd_field_impl.rs @@ -2,7 +2,7 @@ use std::iter; use arith::{ExtensionField, Field, SimdField}; use itertools::izip; -use polynomials::{EqPolynomial, MultiLinearPoly, MultilinearExtension}; +use polynomials::{EqPolynomial, MultilinearExtension, RefMultiLinearPoly}; use transcript::Transcript; use crate::{ @@ -204,11 +204,8 @@ where // NOTE: working on evaluation response, evaluate the rest of the response let mut scratch = vec![EvalF::ZERO; msg_size * SimdF::PACK_SIZE]; - let final_eval = MultiLinearPoly::evaluate_with_buffer( - &proof.eval_row, - &point[..num_vars_in_unpacked_msg], - &mut scratch, - ); + let final_eval = RefMultiLinearPoly::from_ref(&proof.eval_row) + .evaluate_with_buffer(&point[..num_vars_in_unpacked_msg], &mut scratch); if final_eval != evaluation { return false; From a609b6c24c3954b58e795441c0bbfa3b92ee45ec Mon Sep 17 00:00:00 2001 From: Hang Su <tonyfloater@gmail.com> Date: Wed, 18 Dec 2024 00:40:33 -0500 Subject: [PATCH 45/65] allow for parsing gf2 orion gkr config --- config/config_macros/Cargo.toml | 3 +++ config/config_macros/src/lib.rs | 25 ++++++++++++++++--- config/config_macros/tests/macro_expansion.rs | 10 +++++++- 3 files changed, 33 insertions(+), 5 deletions(-) diff --git a/config/config_macros/Cargo.toml b/config/config_macros/Cargo.toml index 732e6490..25e35604 100644 --- a/config/config_macros/Cargo.toml +++ b/config/config_macros/Cargo.toml @@ -13,5 +13,8 @@ syn = "2.0" # For parsing Rust code quote = "1.0" # For generating code proc-macro2 = "1.0" # For working with tokens +[dev-dependencies] +gf2 = { path = "../../arith/gf2" } + [lib] proc-macro=true diff --git a/config/config_macros/src/lib.rs b/config/config_macros/src/lib.rs index cc4f0193..15fc6b16 100644 --- a/config/config_macros/src/lib.rs +++ b/config/config_macros/src/lib.rs @@ -78,6 +78,7 @@ fn parse_fiat_shamir_hash_type( } fn parse_polynomial_commitment_type( + field_type: &str, field_config: &str, transcript_type: &str, polynomial_commitment_type: ExprPath, @@ -89,11 +90,26 @@ fn parse_polynomial_commitment_type( .expect("Empty path for polynomial commitment type"); let pcs_type_str = binding.ident.to_string(); - match pcs_type_str.as_str() { - "Raw" => ( + match (pcs_type_str.as_str(), field_type) { + ("Raw", _) => ( "Raw".to_owned(), format!("RawExpanderGKR::<{field_config}, {transcript_type}>").to_owned(), ), + ("Orion", "GF2") => ( + "Orion".to_owned(), + format!( + " +OrionSIMDFieldPCS::< + <{field_config} as GKRFieldConfig>::CircuitField, + <{field_config} as GKRFieldConfig>::SimdCircuitField, + <{field_config} as GKRFieldConfig>::ChallengeField, + GF2x128, + <{field_config} as GKRFieldConfig>::SimdCircuitField, + {transcript_type} +>" + ) + .to_owned(), + ), _ => panic!("Unknown polynomial commitment type in config macro expansion"), } } @@ -119,11 +135,12 @@ fn declare_gkr_config_impl(input: proc_macro::TokenStream) -> proc_macro::TokenS polynomial_commitment_type, } = parse_macro_input!(input as ConfigLit); - let (_field_type, field_config) = parse_field_type(field_expr); + let (field_type, field_config) = parse_field_type(field_expr); let (fiat_shamir_hash_type, transcript_type) = parse_fiat_shamir_hash_type(field_config.as_str(), fiat_shamir_hash_type_expr); let (polynomial_commitment_enum, polynomial_commitment_type) = parse_polynomial_commitment_type( - field_config.as_str(), + &field_type, + &field_config, &transcript_type, polynomial_commitment_type, ); diff --git a/config/config_macros/tests/macro_expansion.rs b/config/config_macros/tests/macro_expansion.rs index c594369b..dae6d315 100644 --- a/config/config_macros/tests/macro_expansion.rs +++ b/config/config_macros/tests/macro_expansion.rs @@ -7,8 +7,9 @@ use gkr_field_config::FieldType; use config::GKRConfig; use config_macros::declare_gkr_config; +use gf2::GF2x128; use gkr_field_config::{BN254Config, GF2ExtConfig, GKRFieldConfig, M31ExtConfig}; -use poly_commit::raw::RawExpanderGKR; +use poly_commit::{orion::OrionSIMDFieldPCS, raw::RawExpanderGKR}; use transcript::{ BytesHashTranscript, FieldHashTranscript, Keccak256hasher, MIMCHasher, SHA256hasher, }; @@ -37,8 +38,15 @@ fn main() { FiatShamirHashType::Keccak256, PolynomialCommitmentType::Raw ); + declare_gkr_config!( + GF2Keccak256OrionConfig, + FieldType::GF2, + FiatShamirHashType::Keccak256, + PolynomialCommitmentType::Orion + ); print_type_name::<M31Sha256Config>(); print_type_name::<BN254MIMCConfig>(); print_type_name::<GF2Keccak256Config>(); + print_type_name::<GF2Keccak256OrionConfig>(); } From 28dc09b0b2acd9d1862e61572aecbad58c1f8343 Mon Sep 17 00:00:00 2001 From: Hang Su <tonyfloater@gmail.com> Date: Wed, 18 Dec 2024 16:59:07 -0500 Subject: [PATCH 46/65] use fixed rng seeds to generate deterministic pcs crs in mpi cases --- Cargo.lock | 2 ++ Cargo.toml | 1 + gkr/Cargo.toml | 1 + gkr/src/exec.rs | 13 +++++++++---- gkr/src/main.rs | 7 +++++-- gkr/src/main_mpi.rs | 7 +++++-- gkr/src/tests/gkr_correctness.rs | 7 +++++-- 7 files changed, 28 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8d849ab0..5a026165 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -563,6 +563,7 @@ name = "config_macros" version = "0.1.0" dependencies = [ "config", + "gf2", "gkr_field_config", "poly_commit", "proc-macro2", @@ -940,6 +941,7 @@ dependencies = [ "poly_commit", "polynomials", "rand", + "rand_chacha", "sha2", "sumcheck", "thiserror", diff --git a/Cargo.toml b/Cargo.toml index cfa7b6dd..9151a57b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,3 +45,4 @@ tynm = { version = "0.1.6", default-features = false } warp = "0.3.7" thiserror = "1.0.63" ethnum = "1.5.0" +rand_chacha = "0.3.1" diff --git a/gkr/Cargo.toml b/gkr/Cargo.toml index c88a127a..92a65e47 100644 --- a/gkr/Cargo.toml +++ b/gkr/Cargo.toml @@ -28,6 +28,7 @@ sha2.workspace = true halo2curves.workspace = true thiserror.workspace = true ethnum.workspace = true +rand_chacha.workspace = true # for the server bytes.workspace = true diff --git a/gkr/src/exec.rs b/gkr/src/exec.rs index 5c9acab3..c78bb9ed 100644 --- a/gkr/src/exec.rs +++ b/gkr/src/exec.rs @@ -16,7 +16,8 @@ use gkr_field_config::{BN254Config, GF2ExtConfig, GKRFieldConfig, M31ExtConfig}; use mpi_config::MPIConfig; use poly_commit::{expander_pcs_init_testing_only, raw::RawExpanderGKR}; -use rand::thread_rng; +use rand::SeedableRng; +use rand_chacha::ChaCha12Rng; use transcript::{BytesHashTranscript, FieldHashTranscript, MIMCHasher, SHA256hasher}; use log::{debug, info}; @@ -64,6 +65,8 @@ fn detect_field_type_from_circuit_file(circuit_file: &str) -> FieldType { } } +const PCS_TESTING_SEED_U64: u64 = 114514; + async fn run_command<'a, Cfg: GKRConfig>( command: &str, circuit_file: &str, @@ -80,7 +83,8 @@ async fn run_command<'a, Cfg: GKRConfig>( prover.prepare_mem(&circuit); // TODO: Read PCS setup from files - let mut rng = thread_rng(); + let mut rng = ChaCha12Rng::seed_from_u64(PCS_TESTING_SEED_U64); + let (pcs_params, pcs_proving_key, _pcs_verification_key, mut pcs_scratch) = expander_pcs_init_testing_only::<Cfg::FieldConfig, Cfg::Transcript, Cfg::PCS>( circuit.log_input_size(), @@ -123,7 +127,8 @@ async fn run_command<'a, Cfg: GKRConfig>( load_proof_and_claimed_v(&bytes).expect("Unable to deserialize proof."); // TODO: Read PCS setup from files - let mut rng = thread_rng(); + let mut rng = ChaCha12Rng::seed_from_u64(PCS_TESTING_SEED_U64); + let (pcs_params, _pcs_proving_key, pcs_verification_key, mut _pcs_scratch) = expander_pcs_init_testing_only::<Cfg::FieldConfig, Cfg::Transcript, Cfg::PCS>( circuit.log_input_size(), @@ -156,7 +161,7 @@ async fn run_command<'a, Cfg: GKRConfig>( let verifier = gkr::Verifier::new(&config); // TODO: Read PCS setup from files - let mut rng = thread_rng(); + let mut rng = ChaCha12Rng::seed_from_u64(PCS_TESTING_SEED_U64); let (pcs_params, pcs_proving_key, pcs_verification_key, pcs_scratch) = expander_pcs_init_testing_only::<Cfg::FieldConfig, Cfg::Transcript, Cfg::PCS>( circuit.log_input_size(), diff --git a/gkr/src/main.rs b/gkr/src/main.rs index a0b12460..2c11ef5b 100644 --- a/gkr/src/main.rs +++ b/gkr/src/main.rs @@ -11,7 +11,8 @@ use gkr_field_config::{BN254Config, GF2ExtConfig, GKRFieldConfig, M31ExtConfig}; use mpi_config::MPIConfig; use poly_commit::{expander_pcs_init_testing_only, raw::RawExpanderGKR}; -use rand::thread_rng; +use rand::SeedableRng; +use rand_chacha::ChaCha12Rng; use transcript::{BytesHashTranscript, SHA256hasher}; use gkr::{ @@ -114,6 +115,8 @@ fn main() { MPIConfig::finalize(); } +const PCS_TESTING_SEED_U64: u64 = 114514; + fn run_benchmark<Cfg: GKRConfig>(args: &Args, config: Config<Cfg>) { let partial_proof_cnts = (0..args.threads) .map(|_| Arc::new(Mutex::new(0))) @@ -176,7 +179,7 @@ fn run_benchmark<Cfg: GKRConfig>(args: &Args, config: Config<Cfg>) { println!("Circuit loaded!"); - let mut rng = thread_rng(); + let mut rng = ChaCha12Rng::seed_from_u64(PCS_TESTING_SEED_U64); let (pcs_params, pcs_proving_key, _pcs_verification_key, pcs_scratch) = expander_pcs_init_testing_only::<Cfg::FieldConfig, Cfg::Transcript, Cfg::PCS>( circuit_template.log_input_size(), diff --git a/gkr/src/main_mpi.rs b/gkr/src/main_mpi.rs index 2cb5a895..2a03a453 100644 --- a/gkr/src/main_mpi.rs +++ b/gkr/src/main_mpi.rs @@ -6,7 +6,8 @@ use mpi_config::MPIConfig; use gkr_field_config::{BN254Config, GF2ExtConfig, GKRFieldConfig, M31ExtConfig}; use poly_commit::{expander_pcs_init_testing_only, raw::RawExpanderGKR}; -use rand::thread_rng; +use rand::SeedableRng; +use rand_chacha::ChaCha12Rng; use transcript::{BytesHashTranscript, SHA256hasher}; use gkr::{ @@ -105,6 +106,8 @@ fn main() { MPIConfig::finalize(); } +const PCS_TESTING_SEED_U64: u64 = 114514; + fn run_benchmark<Cfg: GKRConfig>(args: &Args, config: Config<Cfg>) { let pack_size = Cfg::FieldConfig::get_field_pack_size(); @@ -156,7 +159,7 @@ fn run_benchmark<Cfg: GKRConfig>(args: &Args, config: Config<Cfg>) { let mut prover = Prover::new(&config); prover.prepare_mem(&circuit); - let mut rng = thread_rng(); + let mut rng = ChaCha12Rng::seed_from_u64(PCS_TESTING_SEED_U64); let (pcs_params, pcs_proving_key, _pcs_verification_key, mut pcs_scratch) = expander_pcs_init_testing_only::<Cfg::FieldConfig, Cfg::Transcript, Cfg::PCS>( circuit.log_input_size(), diff --git a/gkr/src/tests/gkr_correctness.rs b/gkr/src/tests/gkr_correctness.rs index c020903c..5dd11479 100644 --- a/gkr/src/tests/gkr_correctness.rs +++ b/gkr/src/tests/gkr_correctness.rs @@ -11,7 +11,8 @@ use gkr_field_config::{BN254Config, FieldType, GF2ExtConfig, GKRFieldConfig, M31 use mpi_config::{root_println, MPIConfig}; use poly_commit::expander_pcs_init_testing_only; use poly_commit::raw::RawExpanderGKR; -use rand::{thread_rng, Rng}; +use rand::{Rng, SeedableRng}; +use rand_chacha::ChaCha12Rng; use sha2::Digest; use transcript::{ BytesHashTranscript, FieldHashTranscript, Keccak256hasher, MIMCHasher, SHA256hasher, @@ -19,6 +20,8 @@ use transcript::{ use crate::{utils::*, Prover, Verifier}; +const PCS_TESTING_SEED_U64: u64 = 114514; + #[test] fn test_gkr_correctness() { let mpi_config = MPIConfig::new(); @@ -145,7 +148,7 @@ fn test_gkr_correctness_helper<Cfg: GKRConfig>(config: &Config<Cfg>, write_proof let mut prover = Prover::new(config); prover.prepare_mem(&circuit); - let mut rng = thread_rng(); + let mut rng = ChaCha12Rng::seed_from_u64(PCS_TESTING_SEED_U64); let (pcs_params, pcs_proving_key, pcs_verification_key, mut pcs_scratch) = expander_pcs_init_testing_only::<Cfg::FieldConfig, Cfg::Transcript, Cfg::PCS>( circuit.log_input_size(), From 8bcd415e5d36b0ed18d43ef3d6f7d70e3663c33a Mon Sep 17 00:00:00 2001 From: Hang Su <tonyfloater@gmail.com> Date: Wed, 18 Dec 2024 20:43:05 -0500 Subject: [PATCH 47/65] adapting to gkr codebase - setup with number of variables in each poly, ignore simd and mpi vars --- poly_commit/src/orion/pcs_for_expander_gkr.rs | 15 +++++++++----- poly_commit/src/traits.rs | 2 ++ poly_commit/tests/test_orion.rs | 20 +++++++++++-------- 3 files changed, 24 insertions(+), 13 deletions(-) diff --git a/poly_commit/src/orion/pcs_for_expander_gkr.rs b/poly_commit/src/orion/pcs_for_expander_gkr.rs index 5eaff362..c6b53f75 100644 --- a/poly_commit/src/orion/pcs_for_expander_gkr.rs +++ b/poly_commit/src/orion/pcs_for_expander_gkr.rs @@ -35,16 +35,18 @@ where type Opening = OrionProof<C::ChallengeField>; type SRS = OrionSRS; + /// NOTE(HS): this is actually number of variables in polynomial, + /// ignoring the variables for MPI parties and SIMD field element fn gen_params(n_input_vars: usize) -> Self::Params { n_input_vars } fn gen_srs_for_testing( params: &Self::Params, - mpi_config: &MPIConfig, + #[allow(unused)] mpi_config: &MPIConfig, rng: impl rand::RngCore, ) -> Self::SRS { - let num_vars_each_core = *params - mpi_config.world_size().ilog2() as usize; + let num_vars_each_core = *params + C::SimdCircuitField::PACK_SIZE.ilog2() as usize; OrionSRS::from_random::<C::CircuitField>( num_vars_each_core, ORION_CODE_PARAMETER_INSTANCE, @@ -63,7 +65,7 @@ where poly: &impl MultilinearExtension<C::SimdCircuitField>, scratch_pad: &mut Self::ScratchPad, ) -> Self::Commitment { - let num_vars_each_core = *params - mpi_config.world_size().ilog2() as usize; + let num_vars_each_core = *params + C::SimdCircuitField::PACK_SIZE.ilog2() as usize; assert_eq!(num_vars_each_core, proving_key.num_vars); let commitment = orion_commit_simd_field(proving_key, poly, scratch_pad).unwrap(); @@ -96,7 +98,7 @@ where transcript: &mut T, // add transcript here to allow interactive arguments scratch_pad: &mut Self::ScratchPad, ) -> Self::Opening { - let num_vars_each_core = *params - mpi_config.world_size().ilog2() as usize; + let num_vars_each_core = *params + C::SimdCircuitField::PACK_SIZE.ilog2() as usize; assert_eq!(num_vars_each_core, proving_key.num_vars); let local_xs = eval_point.local_xs(); @@ -174,7 +176,10 @@ where transcript: &mut T, // add transcript here to allow interactive arguments opening: &Self::Opening, ) -> bool { - assert_eq!(*params, eval_point.num_vars()); + let global_poly_num_vars = *params + + mpi_config.world_size().ilog2() as usize + + C::SimdCircuitField::PACK_SIZE.ilog2() as usize; + assert_eq!(global_poly_num_vars, eval_point.num_vars()); if mpi_config.world_size() == 1 || !mpi_config.is_root() { return orion_verify_simd_field::< diff --git a/poly_commit/src/traits.rs b/poly_commit/src/traits.rs index 0715de82..07f82399 100644 --- a/poly_commit/src/traits.rs +++ b/poly_commit/src/traits.rs @@ -103,6 +103,8 @@ pub trait PCSForExpanderGKR<C: GKRFieldConfig, T: Transcript<C::ChallengeField>> rng: impl RngCore, ) -> Self::SRS; + /// n_input_vars is with respect to the multilinear poly on each machine in MPI, + /// also ignore the number of variables stacked in the SIMD field. fn gen_params(n_input_vars: usize) -> Self::Params; /// Initialize the scratch pad. diff --git a/poly_commit/tests/test_orion.rs b/poly_commit/tests/test_orion.rs index 8a79bedb..e21886c3 100644 --- a/poly_commit/tests/test_orion.rs +++ b/poly_commit/tests/test_orion.rs @@ -93,7 +93,7 @@ fn test_orion_simd_field_pcs_full_e2e() { test_orion_simd_field_pcs_generics::<GF2, GF2x8, GF2_128, GF2x128, GF2x8>(); } -fn test_orion_for_expander_gkr_generics<C, ComPackF, OpenPackF, T>(num_vars: usize) +fn test_orion_for_expander_gkr_generics<C, ComPackF, OpenPackF, T>(total_num_vars: usize) where C: GKRFieldConfig, ComPackF: SimdField<Scalar = C::CircuitField>, @@ -106,8 +106,11 @@ where // NOTE: generate global random polynomial let num_vars_in_simd = C::SimdCircuitField::PACK_SIZE.ilog2() as usize; let num_vars_in_mpi = mpi_config.world_size().ilog2() as usize; - let poly = - MultiLinearPoly::<C::SimdCircuitField>::random(num_vars - num_vars_in_simd, &mut rng); + let num_vars_in_each_poly = total_num_vars - num_vars_in_mpi - num_vars_in_simd; + let num_vars_in_global_poly = total_num_vars - num_vars_in_simd; + + let global_poly = + MultiLinearPoly::<C::SimdCircuitField>::random(num_vars_in_global_poly, &mut rng); // NOTE generate srs for each party, and shared challenge point in each party let challenge_point = ExpanderGKRChallenge::<C> { @@ -117,22 +120,23 @@ where x_simd: (0..num_vars_in_simd) .map(|_| C::ChallengeField::random_unsafe(&mut rng)) .collect(), - x: (0..(num_vars - num_vars_in_mpi - num_vars_in_simd)) + x: (0..num_vars_in_each_poly) .map(|_| C::ChallengeField::random_unsafe(&mut rng)) .collect(), }; let mut transcript = T::new(); - dbg!(poly.get_num_vars(), poly.coeffs[0]); + dbg!(global_poly.get_num_vars(), global_poly.coeffs[0]); dbg!(&challenge_point.x_mpi); dbg!(mpi_config.world_size(), mpi_config.world_rank()); // NOTE separate polynomial into different pieces by mpi rank - let poly_vars_stride = (1 << poly.get_num_vars()) / mpi_config.world_size(); + let poly_vars_stride = (1 << global_poly.get_num_vars()) / mpi_config.world_size(); let poly_coeff_starts = mpi_config.world_rank() * poly_vars_stride; let poly_coeff_ends = poly_coeff_starts + poly_vars_stride; - let local_poly = MultiLinearPoly::new(poly.coeffs[poly_coeff_starts..poly_coeff_ends].to_vec()); + let local_poly = + MultiLinearPoly::new(global_poly.coeffs[poly_coeff_starts..poly_coeff_ends].to_vec()); dbg!(local_poly.get_num_vars(), local_poly.coeffs[0]); @@ -148,7 +152,7 @@ where T, >, >( - &num_vars, + &num_vars_in_each_poly, &mpi_config, &mut transcript, &local_poly, From db771687a2782f610523c2b48e59424fe9d62c76 Mon Sep 17 00:00:00 2001 From: Hang Su <tonyfloater@gmail.com> Date: Wed, 18 Dec 2024 20:52:11 -0500 Subject: [PATCH 48/65] integrated orion gf2 config into gkr correctness test - single process works, need to read test closely --- config/config_macros/tests/macro_expansion.rs | 2 +- gkr/src/tests/gkr_correctness.rs | 14 ++++++++++++-- poly_commit/src/lib.rs | 3 ++- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/config/config_macros/tests/macro_expansion.rs b/config/config_macros/tests/macro_expansion.rs index dae6d315..940ed7aa 100644 --- a/config/config_macros/tests/macro_expansion.rs +++ b/config/config_macros/tests/macro_expansion.rs @@ -9,7 +9,7 @@ use config::GKRConfig; use config_macros::declare_gkr_config; use gf2::GF2x128; use gkr_field_config::{BN254Config, GF2ExtConfig, GKRFieldConfig, M31ExtConfig}; -use poly_commit::{orion::OrionSIMDFieldPCS, raw::RawExpanderGKR}; +use poly_commit::{OrionSIMDFieldPCS, RawExpanderGKR}; use transcript::{ BytesHashTranscript, FieldHashTranscript, Keccak256hasher, MIMCHasher, SHA256hasher, }; diff --git a/gkr/src/tests/gkr_correctness.rs b/gkr/src/tests/gkr_correctness.rs index 5dd11479..1c21645a 100644 --- a/gkr/src/tests/gkr_correctness.rs +++ b/gkr/src/tests/gkr_correctness.rs @@ -7,10 +7,10 @@ use arith::{Field, FieldSerde}; use circuit::Circuit; use config::{Config, FiatShamirHashType, GKRConfig, GKRScheme, PolynomialCommitmentType}; use config_macros::declare_gkr_config; +use gf2::GF2x128; use gkr_field_config::{BN254Config, FieldType, GF2ExtConfig, GKRFieldConfig, M31ExtConfig}; use mpi_config::{root_println, MPIConfig}; -use poly_commit::expander_pcs_init_testing_only; -use poly_commit::raw::RawExpanderGKR; +use poly_commit::{expander_pcs_init_testing_only, OrionSIMDFieldPCS, RawExpanderGKR}; use rand::{Rng, SeedableRng}; use rand_chacha::ChaCha12Rng; use sha2::Digest; @@ -67,6 +67,12 @@ fn test_gkr_correctness() { FiatShamirHashType::MIMC5, PolynomialCommitmentType::Raw ); + declare_gkr_config!( + C7, + FieldType::GF2, + FiatShamirHashType::Keccak256, + PolynomialCommitmentType::Orion, + ); test_gkr_correctness_helper( &Config::<C0>::new(GKRScheme::Vanilla, mpi_config.clone()), @@ -96,6 +102,10 @@ fn test_gkr_correctness() { &Config::<C6>::new(GKRScheme::Vanilla, mpi_config.clone()), Some("../data/gkr_proof.txt"), ); + test_gkr_correctness_helper( + &Config::<C7>::new(GKRScheme::Vanilla, mpi_config.clone()), + None, + ); MPIConfig::finalize(); } diff --git a/poly_commit/src/lib.rs b/poly_commit/src/lib.rs index a0a348ca..5184ea08 100644 --- a/poly_commit/src/lib.rs +++ b/poly_commit/src/lib.rs @@ -9,6 +9,7 @@ mod utils; pub use utils::{expander_pcs_init_testing_only, PCSEmptyType}; pub mod raw; +pub use raw::RawExpanderGKR; pub mod orion; -pub use self::orion::*; +pub use orion::*; From 403f23e752518af03da7d7908b012fc4890f78be Mon Sep 17 00:00:00 2001 From: Hang Su <tonyfloater@gmail.com> Date: Sat, 21 Dec 2024 20:39:41 -0500 Subject: [PATCH 49/65] align fiat shamir transcript among all mpi parties --- gkr/src/prover/linear_gkr.rs | 2 ++ gkr/src/verifier.rs | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/gkr/src/prover/linear_gkr.rs b/gkr/src/prover/linear_gkr.rs index 907ae30e..95e0e262 100644 --- a/gkr/src/prover/linear_gkr.rs +++ b/gkr/src/prover/linear_gkr.rs @@ -122,6 +122,8 @@ impl<Cfg: GKRConfig> Prover<Cfg> { gkr_prove(c, &mut self.sp, &mut transcript, &self.config.mpi_config); } + transcript_root_broadcast(&mut transcript, &self.config.mpi_config); + // open let mle_ref = RefMultiLinearPoly::from_ref(&c.layers[0].input_vals); self.prove_input_layer_claim( diff --git a/gkr/src/verifier.rs b/gkr/src/verifier.rs index 89349359..809f1d37 100644 --- a/gkr/src/verifier.rs +++ b/gkr/src/verifier.rs @@ -307,6 +307,10 @@ impl<Cfg: GKRConfig> Verifier<Cfg> { log::info!("GKR verification: {}", verified); + if self.config.mpi_config.world_size() > 1 { + let _ = transcript.hash_and_return_state(); // Trigger an additional hash + } + verified &= self.get_pcs_opening_from_proof_and_verify( pcs_params, pcs_verification_key, From 894cf890cf47ebef086ae79d4d36702d6cd30f68 Mon Sep 17 00:00:00 2001 From: Hang Su <tonyfloater@gmail.com> Date: Sat, 21 Dec 2024 21:01:11 -0500 Subject: [PATCH 50/65] integrated into the benchmarking --- gkr/src/exec.rs | 5 +++-- gkr/src/main.rs | 5 +++-- gkr/src/main_mpi.rs | 5 +++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/gkr/src/exec.rs b/gkr/src/exec.rs index c78bb9ed..aa6bd351 100644 --- a/gkr/src/exec.rs +++ b/gkr/src/exec.rs @@ -12,10 +12,11 @@ use config::{ SENTINEL_M31, }; use config_macros::declare_gkr_config; +use gf2::GF2x128; use gkr_field_config::{BN254Config, GF2ExtConfig, GKRFieldConfig, M31ExtConfig}; use mpi_config::MPIConfig; -use poly_commit::{expander_pcs_init_testing_only, raw::RawExpanderGKR}; +use poly_commit::{expander_pcs_init_testing_only, raw::RawExpanderGKR, OrionSIMDFieldPCS}; use rand::SeedableRng; use rand_chacha::ChaCha12Rng; use transcript::{BytesHashTranscript, FieldHashTranscript, MIMCHasher, SHA256hasher}; @@ -304,7 +305,7 @@ async fn main() { GF2ExtConfigSha2, FieldType::GF2, FiatShamirHashType::SHA256, - PolynomialCommitmentType::Raw + PolynomialCommitmentType::Orion ); let circuit_file = &args[2]; diff --git a/gkr/src/main.rs b/gkr/src/main.rs index 2c11ef5b..e5476e97 100644 --- a/gkr/src/main.rs +++ b/gkr/src/main.rs @@ -7,10 +7,11 @@ use circuit::Circuit; use clap::Parser; use config::{Config, GKRConfig, GKRScheme}; use config_macros::declare_gkr_config; +use gf2::GF2x128; use gkr_field_config::{BN254Config, GF2ExtConfig, GKRFieldConfig, M31ExtConfig}; use mpi_config::MPIConfig; -use poly_commit::{expander_pcs_init_testing_only, raw::RawExpanderGKR}; +use poly_commit::{expander_pcs_init_testing_only, raw::RawExpanderGKR, OrionSIMDFieldPCS}; use rand::SeedableRng; use rand_chacha::ChaCha12Rng; use transcript::{BytesHashTranscript, SHA256hasher}; @@ -72,7 +73,7 @@ fn main() { GF2ExtConfigSha2, FieldType::GF2, FiatShamirHashType::SHA256, - PolynomialCommitmentType::Raw + PolynomialCommitmentType::Orion ); match args.field.as_str() { diff --git a/gkr/src/main_mpi.rs b/gkr/src/main_mpi.rs index 2a03a453..162c0833 100644 --- a/gkr/src/main_mpi.rs +++ b/gkr/src/main_mpi.rs @@ -4,8 +4,9 @@ use config::{Config, GKRConfig, GKRScheme}; use config_macros::declare_gkr_config; use mpi_config::MPIConfig; +use gf2::GF2x128; use gkr_field_config::{BN254Config, GF2ExtConfig, GKRFieldConfig, M31ExtConfig}; -use poly_commit::{expander_pcs_init_testing_only, raw::RawExpanderGKR}; +use poly_commit::{expander_pcs_init_testing_only, raw::RawExpanderGKR, OrionSIMDFieldPCS}; use rand::SeedableRng; use rand_chacha::ChaCha12Rng; use transcript::{BytesHashTranscript, SHA256hasher}; @@ -63,7 +64,7 @@ fn main() { GF2ExtConfigSha2, FieldType::GF2, FiatShamirHashType::SHA256, - PolynomialCommitmentType::Raw + PolynomialCommitmentType::Orion ); match args.field.as_str() { From ff730e1002d298f99c1d05ba9987290ba8266dfa Mon Sep 17 00:00:00 2001 From: Hang Su <tonyfloater@gmail.com> Date: Sat, 21 Dec 2024 22:17:27 -0500 Subject: [PATCH 51/65] inline things from multilinear extension --- arith/polynomials/src/ref_mle.rs | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/arith/polynomials/src/ref_mle.rs b/arith/polynomials/src/ref_mle.rs index fab69eab..5cd7f9de 100644 --- a/arith/polynomials/src/ref_mle.rs +++ b/arith/polynomials/src/ref_mle.rs @@ -9,6 +9,7 @@ pub trait MultilinearExtension<F: Field>: Index<usize, Output = F> { fn num_vars(&self) -> usize; + #[inline(always)] fn hypercube_size(&self) -> usize { 1 << self.num_vars() } @@ -26,7 +27,7 @@ pub struct RefMultiLinearPoly<'ref_life, F: Field> { } impl<'ref_life, 'outer: 'ref_life, F: Field> RefMultiLinearPoly<'ref_life, F> { - #[inline] + #[inline(always)] pub fn from_ref(evals: &'outer Vec<F>) -> Self { Self { coeffs: evals } } @@ -35,6 +36,7 @@ impl<'ref_life, 'outer: 'ref_life, F: Field> RefMultiLinearPoly<'ref_life, F> { impl<'a, F: Field> Index<usize> for RefMultiLinearPoly<'a, F> { type Output = F; + #[inline(always)] fn index(&self, index: usize) -> &Self::Output { assert!(index < self.hypercube_size()); &self.coeffs[index] @@ -42,23 +44,28 @@ impl<'a, F: Field> Index<usize> for RefMultiLinearPoly<'a, F> { } impl<'a, F: Field> MultilinearExtension<F> for RefMultiLinearPoly<'a, F> { + #[inline(always)] fn num_vars(&self) -> usize { assert!(self.coeffs.len().is_power_of_two()); self.coeffs.len().ilog2() as usize } + #[inline(always)] fn hypercube_basis(&self) -> Vec<F> { self.coeffs.clone() } + #[inline(always)] fn hypercube_basis_ref(&self) -> &Vec<F> { self.coeffs } + #[inline(always)] fn interpolate_over_hypercube(&self) -> Vec<F> { MultiLinearPoly::interpolate_over_hypercube_impl(self.coeffs) } + #[inline(always)] fn evaluate_with_buffer(&self, point: &[F], scratch: &mut [F]) -> F { MultiLinearPoly::evaluate_with_buffer(self.coeffs, point, scratch) } @@ -80,7 +87,7 @@ pub struct MutRefMultiLinearPoly<'ref_life, F: Field> { } impl<'ref_life, 'outer_mut: 'ref_life, F: Field> MutRefMultiLinearPoly<'ref_life, F> { - #[inline] + #[inline(always)] pub fn from_ref(evals: &'outer_mut mut Vec<F>) -> Self { Self { coeffs: evals } } @@ -89,6 +96,7 @@ impl<'ref_life, 'outer_mut: 'ref_life, F: Field> MutRefMultiLinearPoly<'ref_life impl<'a, F: Field> Index<usize> for MutRefMultiLinearPoly<'a, F> { type Output = F; + #[inline(always)] fn index(&self, index: usize) -> &Self::Output { assert!(index < self.hypercube_size()); &self.coeffs[index] @@ -96,6 +104,7 @@ impl<'a, F: Field> Index<usize> for MutRefMultiLinearPoly<'a, F> { } impl<'a, F: Field> IndexMut<usize> for MutRefMultiLinearPoly<'a, F> { + #[inline(always)] fn index_mut(&mut self, index: usize) -> &mut Self::Output { assert!(index < self.hypercube_size()); &mut self.coeffs[index] @@ -103,29 +112,35 @@ impl<'a, F: Field> IndexMut<usize> for MutRefMultiLinearPoly<'a, F> { } impl<'a, F: Field> MultilinearExtension<F> for MutRefMultiLinearPoly<'a, F> { + #[inline(always)] fn num_vars(&self) -> usize { assert!(self.coeffs.len().is_power_of_two()); self.coeffs.len().ilog2() as usize } + #[inline(always)] fn hypercube_basis(&self) -> Vec<F> { self.coeffs.clone() } + #[inline(always)] fn hypercube_basis_ref(&self) -> &Vec<F> { self.coeffs } + #[inline(always)] fn interpolate_over_hypercube(&self) -> Vec<F> { MultiLinearPoly::interpolate_over_hypercube_impl(self.coeffs) } + #[inline(always)] fn evaluate_with_buffer(&self, point: &[F], scratch: &mut [F]) -> F { MultiLinearPoly::evaluate_with_buffer(self.coeffs, point, scratch) } } impl<'a, F: Field> MutableMultilinearExtension<F> for MutRefMultiLinearPoly<'a, F> { + #[inline(always)] fn fix_top_variable<AF: Field + Mul<F, Output = F>>(&mut self, r: AF) { let n = self.hypercube_size() / 2; let (left, right) = self.coeffs.split_at_mut(n); @@ -136,12 +151,14 @@ impl<'a, F: Field> MutableMultilinearExtension<F> for MutRefMultiLinearPoly<'a, self.coeffs.truncate(n) } + #[inline(always)] fn fix_variables<AF: Field + Mul<F, Output = F>>(&mut self, vars: &[AF]) { // evaluate single variable of partial point from left to right // need to reverse the order of the point vars.iter().rev().for_each(|p| self.fix_top_variable(*p)) } + #[inline(always)] fn interpolate_over_hypercube_in_place(&mut self) { let num_vars = self.num_vars(); for i in 1..=num_vars { From 04d7f344b395fe8ac5be56a523c5f179ddd61386 Mon Sep 17 00:00:00 2001 From: Hang Su <tonyfloater@gmail.com> Date: Thu, 9 Jan 2025 22:52:50 -0500 Subject: [PATCH 52/65] base field orion pcs support for M31 prime --- poly_commit/Cargo.toml | 2 +- poly_commit/src/orion/base_field_impl.rs | 203 ++++++++++++++++++---- poly_commit/src/orion/base_field_tests.rs | 5 + poly_commit/src/orion/utils.rs | 33 ++++ poly_commit/tests/test_orion.rs | 14 +- 5 files changed, 213 insertions(+), 44 deletions(-) diff --git a/poly_commit/Cargo.toml b/poly_commit/Cargo.toml index 7c28a787..5638a839 100644 --- a/poly_commit/Cargo.toml +++ b/poly_commit/Cargo.toml @@ -10,6 +10,7 @@ mpi_config = { path = "../config/mpi_config" } polynomials = { path = "../arith/polynomials"} transcript = { path = "../transcript" } tree = { path = "../tree" } +gf2 = { path = "../arith/gf2" } rand.workspace = true ethnum.workspace = true @@ -18,7 +19,6 @@ thiserror.workspace = true itertools.workspace = true [dev-dependencies] -gf2 = { path = "../arith/gf2" } gf2_128 = { path = "../arith/gf2_128" } mersenne31 = { path = "../arith/mersenne31" } diff --git a/poly_commit/src/orion/base_field_impl.rs b/poly_commit/src/orion/base_field_impl.rs index 908c0999..ad69754c 100644 --- a/poly_commit/src/orion/base_field_impl.rs +++ b/poly_commit/src/orion/base_field_impl.rs @@ -1,13 +1,17 @@ use std::iter; use arith::{ExtensionField, Field, SimdField}; +use gf2::GF2; use itertools::izip; use polynomials::{EqPolynomial, MultilinearExtension, RefMultiLinearPoly}; use transcript::Transcript; use crate::{ orion::{ - utils::{commit_encoded, orion_mt_openings, orion_mt_verify, transpose_in_place}, + utils::{ + commit_encoded, orion_mt_openings, orion_mt_verify, simd_ext_base_inner_prod, + transpose_in_place, + }, OrionCommitment, OrionProof, OrionResult, OrionSRS, OrionScratchPad, }, traits::TensorCodeIOPPCS, @@ -59,6 +63,80 @@ where commit_encoded(pk, &packed_evals, scratch_pad, packed_rows, msg_size) } +#[inline(always)] +fn lut_open_linear_combine<F, EvalF, OpenPackF, T>( + row_num: usize, + packed_evals: &[OpenPackF], + eq_col_coeffs: &[EvalF], + eval_row: &mut [EvalF], + proximity_rows: &mut [Vec<EvalF>], + transcript: &mut T, +) where + F: Field, + EvalF: ExtensionField<BaseField = F>, + OpenPackF: SimdField<Scalar = F>, + T: Transcript<EvalF>, +{ + // NOTE: declare the look up tables for column sums + let table_num = row_num / OpenPackF::PACK_SIZE; + let mut luts = SubsetSumLUTs::<EvalF>::new(OpenPackF::PACK_SIZE, table_num); + assert_eq!(row_num % OpenPackF::PACK_SIZE, 0); + + // NOTE: working on evaluation response of tensor code IOP based PCS + luts.build(eq_col_coeffs); + + izip!(packed_evals.chunks(table_num), eval_row) + .for_each(|(p_col, res)| *res = luts.lookup_and_sum(p_col)); + + // NOTE: draw random linear combination out + // and compose proximity response(s) of tensor code IOP based PCS + proximity_rows.iter_mut().for_each(|row_buffer| { + let random_coeffs = transcript.generate_challenge_field_elements(row_num); + luts.build(&random_coeffs); + + izip!(packed_evals.chunks(table_num), row_buffer) + .for_each(|(p_col, res)| *res = luts.lookup_and_sum(p_col)); + }); + drop(luts); +} + +#[inline(always)] +fn simd_open_linear_combine<F, EvalF, OpenPackF, T>( + row_num: usize, + packed_evals: &[OpenPackF], + eq_col_coeffs: &[EvalF], + eval_row: &mut [EvalF], + proximity_rows: &mut [Vec<EvalF>], + transcript: &mut T, +) where + F: Field, + EvalF: ExtensionField<BaseField = F>, + OpenPackF: SimdField<Scalar = F>, + T: Transcript<EvalF>, +{ + // NOTE: check SIMD inner product numbers for column sums + let simd_inner_prods = row_num / OpenPackF::PACK_SIZE; + assert_eq!(row_num % OpenPackF::PACK_SIZE, 0); + + // NOTE: working on evaluation response of tensor code IOP based PCS + let mut eq_col_coeffs_limbs: Vec<_> = eq_col_coeffs.iter().flat_map(|e| e.to_limbs()).collect(); + let eq_col_simd_limbs: Vec<_> = transpose_and_pack(&mut eq_col_coeffs_limbs, row_num); + + izip!(packed_evals.chunks(simd_inner_prods), eval_row) + .for_each(|(p_col, res)| *res = simd_ext_base_inner_prod(&eq_col_simd_limbs, p_col)); + + // NOTE: draw random linear combination out + // and compose proximity response(s) of tensor code IOP based PCS + proximity_rows.iter_mut().for_each(|row_buffer| { + let random_coeffs = transcript.generate_challenge_field_elements(row_num); + let mut proximity_limbs: Vec<_> = random_coeffs.iter().flat_map(|e| e.to_limbs()).collect(); + let proximity_simd_limbs: Vec<_> = transpose_and_pack(&mut proximity_limbs, row_num); + + izip!(packed_evals.chunks(simd_inner_prods), row_buffer) + .for_each(|(p_col, res)| *res = simd_ext_base_inner_prod(&proximity_simd_limbs, p_col)); + }); +} + pub fn orion_open_base_field<F, EvalF, ComPackF, OpenPackF, T>( pk: &OrionSRS, poly: &impl MultilinearExtension<F>, @@ -83,33 +161,33 @@ where let packed_evals: Vec<OpenPackF> = transpose_and_pack(&mut evals, row_num); drop(evals); - // NOTE: declare the look up tables for column sums - let table_num = row_num / OpenPackF::PACK_SIZE; - let mut luts = SubsetSumLUTs::<EvalF>::new(OpenPackF::PACK_SIZE, table_num); - assert_eq!(row_num % OpenPackF::PACK_SIZE, 0); - - // NOTE: working on evaluation response of tensor code IOP based PCS - let mut eval_row = vec![EvalF::ZERO; msg_size]; - + // NOTE: pre-compute the eq linear combine coeffs for linear combination let eq_col_coeffs = EqPolynomial::build_eq_x_r(&point[point.len() - num_vars_in_row..]); - luts.build(&eq_col_coeffs); - izip!(packed_evals.chunks(table_num), &mut eval_row) - .for_each(|(p_col, res)| *res = luts.lookup_and_sum(p_col)); + // NOTE: pre-declare the spaces for returning evaluation and proximity queries + let mut eval_row = vec![EvalF::ZERO; msg_size]; - // NOTE: draw random linear combination out - // and compose proximity response(s) of tensor code IOP based PCS let proximity_test_num = pk.proximity_repetitions::<EvalF>(PCS_SOUNDNESS_BITS); let mut proximity_rows = vec![vec![EvalF::ZERO; msg_size]; proximity_test_num]; - proximity_rows.iter_mut().for_each(|row_buffer| { - let random_coeffs = transcript.generate_challenge_field_elements(row_num); - luts.build(&random_coeffs); - - izip!(packed_evals.chunks(table_num), row_buffer) - .for_each(|(p_col, res)| *res = luts.lookup_and_sum(p_col)); - }); - drop(luts); + match F::NAME { + GF2::NAME => lut_open_linear_combine( + row_num, + &packed_evals, + &eq_col_coeffs, + &mut eval_row, + &mut proximity_rows, + transcript, + ), + _ => simd_open_linear_combine( + row_num, + &packed_evals, + &eq_col_coeffs, + &mut eval_row, + &mut proximity_rows, + transcript, + ), + } // NOTE: working on evaluation on top of evaluation response let mut scratch = vec![EvalF::ZERO; msg_size]; @@ -130,6 +208,57 @@ where ) } +#[inline(always)] +fn lut_verify_alphabet_check<F, SimdF, ExtF>( + codeword: &[ExtF], + fixed_rl: &[ExtF], + query_indices: &[usize], + packed_interleaved_alphabets: &[Vec<SimdF>], +) -> bool +where + F: Field, + SimdF: SimdField<Scalar = F>, + ExtF: ExtensionField<BaseField = F>, +{ + // NOTE: build up lookup table + let tables_num = fixed_rl.len() / SimdF::PACK_SIZE; + assert_eq!(fixed_rl.len() % SimdF::PACK_SIZE, 0); + let mut luts = SubsetSumLUTs::<ExtF>::new(SimdF::PACK_SIZE, tables_num); + + luts.build(fixed_rl); + + izip!(query_indices, packed_interleaved_alphabets).all(|(qi, interleaved_alphabet)| { + let index = qi % codeword.len(); + let alphabet = luts.lookup_and_sum(interleaved_alphabet); + alphabet == codeword[index] + }) +} + +#[inline(always)] +fn simd_verify_alphabet_check<F, SimdF, ExtF>( + codeword: &[ExtF], + fixed_rl: &[ExtF], + query_indices: &[usize], + packed_interleaved_alphabets: &[Vec<SimdF>], +) -> bool +where + F: Field, + SimdF: SimdField<Scalar = F>, + ExtF: ExtensionField<BaseField = F>, +{ + // NOTE: check SIMD inner product numbers for column sums + assert_eq!(fixed_rl.len() % SimdF::PACK_SIZE, 0); + + let mut rl_limbs: Vec<_> = fixed_rl.iter().flat_map(|e| e.to_limbs()).collect(); + let rl_simd_limbs: Vec<SimdF> = transpose_and_pack(&mut rl_limbs, fixed_rl.len()); + + izip!(query_indices, packed_interleaved_alphabets).all(|(qi, interleaved_alphabet)| { + let index = qi % codeword.len(); + let alphabet: ExtF = simd_ext_base_inner_prod(&rl_simd_limbs, interleaved_alphabet); + alphabet == codeword[index] + }) +} + pub fn orion_verify_base_field<F, EvalF, ComPackF, OpenPackF, T>( vk: &OrionSRS, commitment: &OrionCommitment, @@ -184,13 +313,6 @@ where }) .collect(); - // NOTE: encode the proximity/evaluation responses, - // check againts all challenged indices by check alphabets against - // linear combined interleaved alphabet - let tables_num = row_num / OpenPackF::PACK_SIZE; - let mut luts = SubsetSumLUTs::<EvalF>::new(OpenPackF::PACK_SIZE, tables_num); - assert_eq!(row_num % OpenPackF::PACK_SIZE, 0); - let eq_linear_combination = EqPolynomial::build_eq_x_r(&point[num_vars_in_msg..]); izip!(&random_linear_combinations, &proof.proximity_rows) @@ -201,14 +323,19 @@ where _ => return false, }; - luts.build(rl); - - izip!(&query_indices, &packed_interleaved_alphabets).all( - |(qi, interleaved_alphabet)| { - let index = qi % vk.codeword_len(); - let alphabet = luts.lookup_and_sum(interleaved_alphabet); - alphabet == codeword[index] - }, - ) + match F::NAME { + GF2::NAME => lut_verify_alphabet_check( + &codeword, + rl, + &query_indices, + &packed_interleaved_alphabets, + ), + _ => simd_verify_alphabet_check( + &codeword, + rl, + &query_indices, + &packed_interleaved_alphabets, + ), + } }) } diff --git a/poly_commit/src/orion/base_field_tests.rs b/poly_commit/src/orion/base_field_tests.rs index 5147f492..47007497 100644 --- a/poly_commit/src/orion/base_field_tests.rs +++ b/poly_commit/src/orion/base_field_tests.rs @@ -1,6 +1,7 @@ use arith::{Field, SimdField}; use ark_std::test_rng; use gf2::{GF2x128, GF2x64, GF2}; +use mersenne31::{M31x16, M31}; use polynomials::MultiLinearPoly; use crate::{ @@ -63,4 +64,8 @@ fn test_orion_commit_base_field_consistency() { test_orion_commit_base_field_consistency_generic::<GF2, GF2x64>(num_vars); test_orion_commit_base_field_consistency_generic::<GF2, GF2x128>(num_vars); }); + + (16..=22).for_each(|num_vars| { + test_orion_commit_base_field_consistency_generic::<M31, M31x16>(num_vars) + }); } diff --git a/poly_commit/src/orion/utils.rs b/poly_commit/src/orion/utils.rs index 17757bcc..df88ba88 100644 --- a/poly_commit/src/orion/utils.rs +++ b/poly_commit/src/orion/utils.rs @@ -269,3 +269,36 @@ impl<F: Field> SubsetSumLUTs<F> { .sum() } } + +#[inline(always)] +pub(crate) fn simd_inner_product<F, SimdF>(lhs: &[SimdF], rhs: &[SimdF]) -> F +where + F: Field, + SimdF: SimdField<Scalar = F>, +{ + assert_eq!(lhs.len(), rhs.len()); + + let simd_sum: SimdF = izip!(lhs, rhs).map(|(a, b)| *a * b).sum(); + + simd_sum.unpack().iter().sum() +} + +#[inline(always)] +pub(crate) fn simd_ext_base_inner_prod<F, ExtF, SimdF>( + simd_ext_limbs: &[SimdF], + simd_base_elems: &[SimdF], +) -> ExtF +where + F: Field, + SimdF: SimdField<Scalar = F>, + ExtF: ExtensionField<BaseField = F>, +{ + assert_eq!(simd_ext_limbs.len(), simd_base_elems.len() * ExtF::DEGREE); + + let mut ext_limbs = vec![F::ZERO; ExtF::DEGREE]; + + izip!(&mut ext_limbs, simd_ext_limbs.chunks(simd_base_elems.len())) + .for_each(|(e, simd_ext_limb)| *e = simd_inner_product(simd_ext_limb, simd_base_elems)); + + ExtF::from_limbs(&ext_limbs) +} diff --git a/poly_commit/tests/test_orion.rs b/poly_commit/tests/test_orion.rs index e21886c3..aa8759cf 100644 --- a/poly_commit/tests/test_orion.rs +++ b/poly_commit/tests/test_orion.rs @@ -5,6 +5,7 @@ use ark_std::test_rng; use gf2::{GF2x128, GF2x64, GF2x8, GF2}; use gf2_128::GF2_128; use gkr_field_config::{GF2ExtConfig, GKRFieldConfig}; +use mersenne31::{M31Ext3, M31x16, M31}; use mpi_config::MPIConfig; use poly_commit::*; use polynomials::MultiLinearPoly; @@ -12,8 +13,10 @@ use transcript::{BytesHashTranscript, Keccak256hasher, Transcript}; const TEST_REPETITION: usize = 3; -fn test_orion_base_field_pcs_generics<F, EvalF, ComPackF, OpenPackF>() -where +fn test_orion_base_field_pcs_generics<F, EvalF, ComPackF, OpenPackF>( + num_vars_start: usize, + num_vars_end: usize, +) where F: Field, EvalF: ExtensionField<BaseField = F>, ComPackF: SimdField<Scalar = F>, @@ -21,7 +24,7 @@ where { let mut rng = test_rng(); - (19..=25).for_each(|num_vars| { + (num_vars_start..=num_vars_end).for_each(|num_vars| { let xs: Vec<_> = (0..TEST_REPETITION) .map(|_| -> Vec<EvalF> { (0..num_vars) @@ -47,8 +50,9 @@ where #[test] fn test_orion_base_field_pcs_full_e2e() { - test_orion_base_field_pcs_generics::<GF2, GF2_128, GF2x64, GF2x8>(); - test_orion_base_field_pcs_generics::<GF2, GF2_128, GF2x128, GF2x8>() + test_orion_base_field_pcs_generics::<GF2, GF2_128, GF2x64, GF2x8>(19, 25); + test_orion_base_field_pcs_generics::<GF2, GF2_128, GF2x128, GF2x8>(19, 25); + test_orion_base_field_pcs_generics::<M31, M31Ext3, M31x16, M31x16>(16, 22) } fn test_orion_simd_field_pcs_generics<F, SimdF, EvalF, ComPackF, OpenPackF>() From 4fce2202784059538f4030c9cbb86539f81a7887 Mon Sep 17 00:00:00 2001 From: Hang Su <tonyfloater@gmail.com> Date: Thu, 9 Jan 2025 23:19:23 -0500 Subject: [PATCH 53/65] update on verifier side - sync up the randomness on verifier side --- gkr/src/verifier.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/gkr/src/verifier.rs b/gkr/src/verifier.rs index 8d37ebf4..359f1cb1 100644 --- a/gkr/src/verifier.rs +++ b/gkr/src/verifier.rs @@ -286,7 +286,7 @@ impl<Cfg: GKRConfig> Verifier<Cfg> { // transcript.append_u8_slice(&proof.bytes[..commitment.size()]); if self.config.mpi_config.world_size() > 1 { - let state = transcript.hash_and_return_state(); // Trigger an additional hash + let state = transcript.hash_and_return_state(); // Sync up the Fiat-Shamir randomness transcript.set_state(&state); } @@ -309,7 +309,8 @@ impl<Cfg: GKRConfig> Verifier<Cfg> { log::info!("GKR verification: {}", verified); if self.config.mpi_config.world_size() > 1 { - let _ = transcript.hash_and_return_state(); // Trigger an additional hash + let state = transcript.hash_and_return_state(); // Sync up the Fiat-Shamir randomness + transcript.set_state(&state); } verified &= self.get_pcs_opening_from_proof_and_verify( From fcce135d61170bac65ebbac235fcd6dd2729d9ce Mon Sep 17 00:00:00 2001 From: Hang Su <tonyfloater@gmail.com> Date: Fri, 10 Jan 2025 00:22:22 -0500 Subject: [PATCH 54/65] orion m31 base pcs commit and open benchmarking --- poly_commit/benches/orion.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/poly_commit/benches/orion.rs b/poly_commit/benches/orion.rs index de3269f4..30f2c72a 100644 --- a/poly_commit/benches/orion.rs +++ b/poly_commit/benches/orion.rs @@ -5,6 +5,7 @@ use ark_std::test_rng; use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion}; use gf2::{GF2x128, GF2x8, GF2}; use gf2_128::GF2_128; +use mersenne31::{M31Ext3, M31x16, M31}; use poly_commit::*; use polynomials::MultiLinearPoly; use transcript::{BytesHashTranscript, Keccak256hasher, Transcript}; @@ -49,6 +50,7 @@ fn base_field_committing_benchmark_helper<F, ComPackF>( fn orion_base_field_committing_benchmark(c: &mut Criterion) { base_field_committing_benchmark_helper::<GF2, GF2x128>(c, 19, 30); + base_field_committing_benchmark_helper::<M31, M31x16>(c, 19, 26); } fn simd_field_committing_benchmark_helper<F, SimdF, ComPackF>( @@ -160,6 +162,13 @@ fn orion_base_field_opening_benchmark(c: &mut Criterion) { GF2x8, BytesHashTranscript<_, Keccak256hasher>, >(c, 19, 30); + base_field_opening_benchmark_helper::< + M31, + M31Ext3, + M31x16, + M31x16, + BytesHashTranscript<_, Keccak256hasher>, + >(c, 19, 26); } fn simd_field_opening_benchmark_helper<F, SimdF, EvalF, ComPackF, OpenPackF, T>( From cea7ebd70c0f567c447a21947fb0efb54c1c05c3 Mon Sep 17 00:00:00 2001 From: Hang Su <tonyfloater@gmail.com> Date: Fri, 10 Jan 2025 00:47:57 -0500 Subject: [PATCH 55/65] M31 simd commit testing --- poly_commit/src/orion/simd_field_tests.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/poly_commit/src/orion/simd_field_tests.rs b/poly_commit/src/orion/simd_field_tests.rs index c7c02a65..978110ee 100644 --- a/poly_commit/src/orion/simd_field_tests.rs +++ b/poly_commit/src/orion/simd_field_tests.rs @@ -1,6 +1,7 @@ use arith::{Field, SimdField}; use ark_std::test_rng; use gf2::{GF2x128, GF2x64, GF2x8, GF2}; +use mersenne31::{M31x16, M31}; use polynomials::MultiLinearPoly; use crate::{ @@ -79,4 +80,8 @@ fn test_orion_commit_simd_field_consistency() { test_orion_commit_simd_field_consistency_generic::<GF2, GF2x8, GF2x64>(num_vars); test_orion_commit_simd_field_consistency_generic::<GF2, GF2x8, GF2x128>(num_vars); }); + + (12..=18).for_each(|num_vars| { + test_orion_commit_simd_field_consistency_generic::<M31, M31x16, M31x16>(num_vars) + }) } From 0274b94ddda49b0ef33ec135ee6f9a3521d900c8 Mon Sep 17 00:00:00 2001 From: Hang Su <tonyfloater@gmail.com> Date: Sun, 12 Jan 2025 02:32:49 -0500 Subject: [PATCH 56/65] minor - updated orion implementation with m31 support prototype --- config/config_macros/src/lib.rs | 17 +- config/config_macros/tests/macro_expansion.rs | 12 +- gkr/src/executor.rs | 2 +- gkr/src/main.rs | 2 +- gkr/src/main_mpi.rs | 2 +- gkr/src/tests/gkr_correctness.rs | 2 +- poly_commit/benches/orion.rs | 23 +- poly_commit/src/orion.rs | 2 + poly_commit/src/orion/base_field_impl.rs | 153 +------------ poly_commit/src/orion/pcs_for_expander_gkr.rs | 24 +- poly_commit/src/orion/pcs_trait_impl.rs | 23 +- poly_commit/src/orion/simd_field_agg_impl.rs | 176 +++++++-------- poly_commit/src/orion/simd_field_agg_tests.rs | 91 ++++---- poly_commit/src/orion/simd_field_impl.rs | 208 ++++++------------ poly_commit/src/orion/utils.rs | 172 ++++++++++++++- poly_commit/src/traits.rs | 7 + poly_commit/tests/test_orion.rs | 53 +++-- 17 files changed, 454 insertions(+), 515 deletions(-) diff --git a/config/config_macros/src/lib.rs b/config/config_macros/src/lib.rs index 65d569c3..9cc7430e 100644 --- a/config/config_macros/src/lib.rs +++ b/config/config_macros/src/lib.rs @@ -107,18 +107,11 @@ fn parse_polynomial_commitment_type( ), ("Orion", "GF2") => ( "Orion".to_owned(), - format!( - " -OrionSIMDFieldPCS::< - <{field_config} as GKRFieldConfig>::CircuitField, - <{field_config} as GKRFieldConfig>::SimdCircuitField, - <{field_config} as GKRFieldConfig>::ChallengeField, - GF2x128, - <{field_config} as GKRFieldConfig>::SimdCircuitField, - {transcript_type} ->" - ) - .to_owned(), + format!("OrionPCSForGKR::<{field_config}, GF2x128, {transcript_type}>").to_owned(), + ), + ("Orion", "M31") => ( + "Orion".to_owned(), + format!("OrionPCSForGKR::<{field_config}, M31x16, {transcript_type}>").to_owned(), ), _ => panic!("Unknown polynomial commitment type in config macro expansion"), } diff --git a/config/config_macros/tests/macro_expansion.rs b/config/config_macros/tests/macro_expansion.rs index a2c7a1d2..af8dba85 100644 --- a/config/config_macros/tests/macro_expansion.rs +++ b/config/config_macros/tests/macro_expansion.rs @@ -11,7 +11,7 @@ use field_hashers::{MiMC5FiatShamirHasher, PoseidonFiatShamirHasher}; use gf2::GF2x128; use gkr_field_config::{BN254Config, GF2ExtConfig, GKRFieldConfig, M31ExtConfig}; use mersenne31::M31x16; -use poly_commit::{OrionSIMDFieldPCS, RawExpanderGKR}; +use poly_commit::{OrionPCSForGKR, RawExpanderGKR}; use transcript::{BytesHashTranscript, FieldHashTranscript, Keccak256hasher, SHA256hasher}; fn print_type_name<Cfg: GKRConfig>() { @@ -27,11 +27,17 @@ fn main() { PolynomialCommitmentType::Raw ); declare_gkr_config!( - M31PoseidonConfig, + M31PoseidonRawConfig, FieldType::M31, FiatShamirHashType::Poseidon, PolynomialCommitmentType::Raw ); + declare_gkr_config!( + M31PoseidonOrionConfig, + FieldType::M31, + FiatShamirHashType::Poseidon, + PolynomialCommitmentType::Orion + ); declare_gkr_config!( BN254MIMCConfig, FieldType::BN254, @@ -52,6 +58,8 @@ fn main() { ); print_type_name::<M31Sha256Config>(); + print_type_name::<M31PoseidonRawConfig>(); + print_type_name::<M31PoseidonOrionConfig>(); print_type_name::<BN254MIMCConfig>(); print_type_name::<GF2Keccak256Config>(); print_type_name::<GF2Keccak256OrionConfig>(); diff --git a/gkr/src/executor.rs b/gkr/src/executor.rs index 2aca17ee..859afe9c 100644 --- a/gkr/src/executor.rs +++ b/gkr/src/executor.rs @@ -15,7 +15,7 @@ use field_hashers::{MiMC5FiatShamirHasher, PoseidonFiatShamirHasher}; use gf2::GF2x128; use gkr_field_config::{BN254Config, GF2ExtConfig, GKRFieldConfig, M31ExtConfig}; use mersenne31::M31x16; -use poly_commit::{expander_pcs_init_testing_only, raw::RawExpanderGKR, OrionSIMDFieldPCS}; +use poly_commit::{expander_pcs_init_testing_only, raw::RawExpanderGKR, OrionPCSForGKR}; use rand::SeedableRng; use rand_chacha::ChaCha12Rng; use transcript::{BytesHashTranscript, FieldHashTranscript, SHA256hasher}; diff --git a/gkr/src/main.rs b/gkr/src/main.rs index e5476e97..48dc770e 100644 --- a/gkr/src/main.rs +++ b/gkr/src/main.rs @@ -11,7 +11,7 @@ use gf2::GF2x128; use gkr_field_config::{BN254Config, GF2ExtConfig, GKRFieldConfig, M31ExtConfig}; use mpi_config::MPIConfig; -use poly_commit::{expander_pcs_init_testing_only, raw::RawExpanderGKR, OrionSIMDFieldPCS}; +use poly_commit::{expander_pcs_init_testing_only, raw::RawExpanderGKR, OrionPCSForGKR}; use rand::SeedableRng; use rand_chacha::ChaCha12Rng; use transcript::{BytesHashTranscript, SHA256hasher}; diff --git a/gkr/src/main_mpi.rs b/gkr/src/main_mpi.rs index 162c0833..afc198e5 100644 --- a/gkr/src/main_mpi.rs +++ b/gkr/src/main_mpi.rs @@ -6,7 +6,7 @@ use mpi_config::MPIConfig; use gf2::GF2x128; use gkr_field_config::{BN254Config, GF2ExtConfig, GKRFieldConfig, M31ExtConfig}; -use poly_commit::{expander_pcs_init_testing_only, raw::RawExpanderGKR, OrionSIMDFieldPCS}; +use poly_commit::{expander_pcs_init_testing_only, raw::RawExpanderGKR, OrionPCSForGKR}; use rand::SeedableRng; use rand_chacha::ChaCha12Rng; use transcript::{BytesHashTranscript, SHA256hasher}; diff --git a/gkr/src/tests/gkr_correctness.rs b/gkr/src/tests/gkr_correctness.rs index 18c7ec1f..37433944 100644 --- a/gkr/src/tests/gkr_correctness.rs +++ b/gkr/src/tests/gkr_correctness.rs @@ -12,7 +12,7 @@ use gf2::GF2x128; use gkr_field_config::{BN254Config, FieldType, GF2ExtConfig, GKRFieldConfig, M31ExtConfig}; use mersenne31::M31x16; use mpi_config::{root_println, MPIConfig}; -use poly_commit::{expander_pcs_init_testing_only, OrionSIMDFieldPCS, RawExpanderGKR}; +use poly_commit::{expander_pcs_init_testing_only, OrionPCSForGKR, RawExpanderGKR}; use rand::{Rng, SeedableRng}; use rand_chacha::ChaCha12Rng; use sha2::Digest; diff --git a/poly_commit/benches/orion.rs b/poly_commit/benches/orion.rs index 30f2c72a..5c5833e9 100644 --- a/poly_commit/benches/orion.rs +++ b/poly_commit/benches/orion.rs @@ -171,7 +171,7 @@ fn orion_base_field_opening_benchmark(c: &mut Criterion) { >(c, 19, 26); } -fn simd_field_opening_benchmark_helper<F, SimdF, EvalF, ComPackF, OpenPackF, T>( +fn simd_field_opening_benchmark_helper<F, SimdF, EvalF, ComPackF, T>( c: &mut Criterion, lowest_num_vars: usize, highest_num_vars: usize, @@ -180,15 +180,13 @@ fn simd_field_opening_benchmark_helper<F, SimdF, EvalF, ComPackF, OpenPackF, T>( SimdF: SimdField<Scalar = F>, EvalF: ExtensionField<BaseField = F>, ComPackF: SimdField<Scalar = F>, - OpenPackF: SimdField<Scalar = F>, T: Transcript<EvalF>, { let mut group = c.benchmark_group(format!( - "Orion PCS SIMD field opening: SIMD-F = {}, EvalF = {}, ComPackF = {}, OpenPackF = {}", + "Orion PCS SIMD field opening: SIMD-F = {}, EvalF = {}, ComPackF = {}", type_name::<SimdF>(), type_name::<EvalF>(), type_name::<ComPackF>(), - type_name::<OpenPackF>() )); let mut rng = test_rng(); @@ -216,15 +214,13 @@ fn simd_field_opening_benchmark_helper<F, SimdF, EvalF, ComPackF, OpenPackF, T>( ), |b| { b.iter(|| { - _ = black_box( - orion_open_simd_field::<F, SimdF, _, ComPackF, OpenPackF, T>( - &srs, - &poly, - &eval_point, - &mut transcript, - &scratch_pad, - ), - ) + _ = black_box(orion_open_simd_field::<F, SimdF, _, ComPackF, T>( + &srs, + &poly, + &eval_point, + &mut transcript, + &scratch_pad, + )) }) }, ) @@ -238,7 +234,6 @@ fn orion_simd_field_opening_benchmark(c: &mut Criterion) { GF2x8, GF2_128, GF2x128, - GF2x8, BytesHashTranscript<_, Keccak256hasher>, >(c, 19, 30); } diff --git a/poly_commit/src/orion.rs b/poly_commit/src/orion.rs index 32a500a1..375b0f30 100644 --- a/poly_commit/src/orion.rs +++ b/poly_commit/src/orion.rs @@ -32,6 +32,8 @@ mod simd_field_agg_impl; mod simd_field_agg_tests; mod pcs_for_expander_gkr; +pub use pcs_for_expander_gkr::OrionPCSForGKR; + mod pcs_trait_impl; pub use pcs_trait_impl::{OrionBaseFieldPCS, OrionSIMDFieldPCS}; diff --git a/poly_commit/src/orion/base_field_impl.rs b/poly_commit/src/orion/base_field_impl.rs index ad69754c..91fd4502 100644 --- a/poly_commit/src/orion/base_field_impl.rs +++ b/poly_commit/src/orion/base_field_impl.rs @@ -7,35 +7,11 @@ use polynomials::{EqPolynomial, MultilinearExtension, RefMultiLinearPoly}; use transcript::Transcript; use crate::{ - orion::{ - utils::{ - commit_encoded, orion_mt_openings, orion_mt_verify, simd_ext_base_inner_prod, - transpose_in_place, - }, - OrionCommitment, OrionProof, OrionResult, OrionSRS, OrionScratchPad, - }, + orion::{utils::*, OrionCommitment, OrionProof, OrionResult, OrionSRS, OrionScratchPad}, traits::TensorCodeIOPPCS, - SubsetSumLUTs, PCS_SOUNDNESS_BITS, + PCS_SOUNDNESS_BITS, }; -#[inline(always)] -fn transpose_and_pack<F, PackF>(evaluations: &mut [F], row_num: usize) -> Vec<PackF> -where - F: Field, - PackF: SimdField<Scalar = F>, -{ - // NOTE: pre transpose evaluations - let mut scratch = vec![F::ZERO; evaluations.len()]; - transpose_in_place(evaluations, &mut scratch, row_num); - drop(scratch); - - // NOTE: SIMD pack each row of transposed matrix - evaluations - .chunks(PackF::PACK_SIZE) - .map(SimdField::pack) - .collect() -} - pub fn orion_commit_base_field<F, ComPackF>( pk: &OrionSRS, poly: &impl MultilinearExtension<F>, @@ -63,80 +39,6 @@ where commit_encoded(pk, &packed_evals, scratch_pad, packed_rows, msg_size) } -#[inline(always)] -fn lut_open_linear_combine<F, EvalF, OpenPackF, T>( - row_num: usize, - packed_evals: &[OpenPackF], - eq_col_coeffs: &[EvalF], - eval_row: &mut [EvalF], - proximity_rows: &mut [Vec<EvalF>], - transcript: &mut T, -) where - F: Field, - EvalF: ExtensionField<BaseField = F>, - OpenPackF: SimdField<Scalar = F>, - T: Transcript<EvalF>, -{ - // NOTE: declare the look up tables for column sums - let table_num = row_num / OpenPackF::PACK_SIZE; - let mut luts = SubsetSumLUTs::<EvalF>::new(OpenPackF::PACK_SIZE, table_num); - assert_eq!(row_num % OpenPackF::PACK_SIZE, 0); - - // NOTE: working on evaluation response of tensor code IOP based PCS - luts.build(eq_col_coeffs); - - izip!(packed_evals.chunks(table_num), eval_row) - .for_each(|(p_col, res)| *res = luts.lookup_and_sum(p_col)); - - // NOTE: draw random linear combination out - // and compose proximity response(s) of tensor code IOP based PCS - proximity_rows.iter_mut().for_each(|row_buffer| { - let random_coeffs = transcript.generate_challenge_field_elements(row_num); - luts.build(&random_coeffs); - - izip!(packed_evals.chunks(table_num), row_buffer) - .for_each(|(p_col, res)| *res = luts.lookup_and_sum(p_col)); - }); - drop(luts); -} - -#[inline(always)] -fn simd_open_linear_combine<F, EvalF, OpenPackF, T>( - row_num: usize, - packed_evals: &[OpenPackF], - eq_col_coeffs: &[EvalF], - eval_row: &mut [EvalF], - proximity_rows: &mut [Vec<EvalF>], - transcript: &mut T, -) where - F: Field, - EvalF: ExtensionField<BaseField = F>, - OpenPackF: SimdField<Scalar = F>, - T: Transcript<EvalF>, -{ - // NOTE: check SIMD inner product numbers for column sums - let simd_inner_prods = row_num / OpenPackF::PACK_SIZE; - assert_eq!(row_num % OpenPackF::PACK_SIZE, 0); - - // NOTE: working on evaluation response of tensor code IOP based PCS - let mut eq_col_coeffs_limbs: Vec<_> = eq_col_coeffs.iter().flat_map(|e| e.to_limbs()).collect(); - let eq_col_simd_limbs: Vec<_> = transpose_and_pack(&mut eq_col_coeffs_limbs, row_num); - - izip!(packed_evals.chunks(simd_inner_prods), eval_row) - .for_each(|(p_col, res)| *res = simd_ext_base_inner_prod(&eq_col_simd_limbs, p_col)); - - // NOTE: draw random linear combination out - // and compose proximity response(s) of tensor code IOP based PCS - proximity_rows.iter_mut().for_each(|row_buffer| { - let random_coeffs = transcript.generate_challenge_field_elements(row_num); - let mut proximity_limbs: Vec<_> = random_coeffs.iter().flat_map(|e| e.to_limbs()).collect(); - let proximity_simd_limbs: Vec<_> = transpose_and_pack(&mut proximity_limbs, row_num); - - izip!(packed_evals.chunks(simd_inner_prods), row_buffer) - .for_each(|(p_col, res)| *res = simd_ext_base_inner_prod(&proximity_simd_limbs, p_col)); - }); -} - pub fn orion_open_base_field<F, EvalF, ComPackF, OpenPackF, T>( pk: &OrionSRS, poly: &impl MultilinearExtension<F>, @@ -208,57 +110,6 @@ where ) } -#[inline(always)] -fn lut_verify_alphabet_check<F, SimdF, ExtF>( - codeword: &[ExtF], - fixed_rl: &[ExtF], - query_indices: &[usize], - packed_interleaved_alphabets: &[Vec<SimdF>], -) -> bool -where - F: Field, - SimdF: SimdField<Scalar = F>, - ExtF: ExtensionField<BaseField = F>, -{ - // NOTE: build up lookup table - let tables_num = fixed_rl.len() / SimdF::PACK_SIZE; - assert_eq!(fixed_rl.len() % SimdF::PACK_SIZE, 0); - let mut luts = SubsetSumLUTs::<ExtF>::new(SimdF::PACK_SIZE, tables_num); - - luts.build(fixed_rl); - - izip!(query_indices, packed_interleaved_alphabets).all(|(qi, interleaved_alphabet)| { - let index = qi % codeword.len(); - let alphabet = luts.lookup_and_sum(interleaved_alphabet); - alphabet == codeword[index] - }) -} - -#[inline(always)] -fn simd_verify_alphabet_check<F, SimdF, ExtF>( - codeword: &[ExtF], - fixed_rl: &[ExtF], - query_indices: &[usize], - packed_interleaved_alphabets: &[Vec<SimdF>], -) -> bool -where - F: Field, - SimdF: SimdField<Scalar = F>, - ExtF: ExtensionField<BaseField = F>, -{ - // NOTE: check SIMD inner product numbers for column sums - assert_eq!(fixed_rl.len() % SimdF::PACK_SIZE, 0); - - let mut rl_limbs: Vec<_> = fixed_rl.iter().flat_map(|e| e.to_limbs()).collect(); - let rl_simd_limbs: Vec<SimdF> = transpose_and_pack(&mut rl_limbs, fixed_rl.len()); - - izip!(query_indices, packed_interleaved_alphabets).all(|(qi, interleaved_alphabet)| { - let index = qi % codeword.len(); - let alphabet: ExtF = simd_ext_base_inner_prod(&rl_simd_limbs, interleaved_alphabet); - alphabet == codeword[index] - }) -} - pub fn orion_verify_base_field<F, EvalF, ComPackF, OpenPackF, T>( vk: &OrionSRS, commitment: &OrionCommitment, diff --git a/poly_commit/src/orion/pcs_for_expander_gkr.rs b/poly_commit/src/orion/pcs_for_expander_gkr.rs index c6b53f75..1157a6d5 100644 --- a/poly_commit/src/orion/pcs_for_expander_gkr.rs +++ b/poly_commit/src/orion/pcs_for_expander_gkr.rs @@ -11,19 +11,11 @@ use crate::{ ExpanderGKRChallenge, PCSForExpanderGKR, StructuredReferenceString, }; -impl<C, ComPackF, OpenPackF, T> PCSForExpanderGKR<C, T> - for OrionSIMDFieldPCS< - C::CircuitField, - C::SimdCircuitField, - C::ChallengeField, - ComPackF, - OpenPackF, - T, - > +impl<C, ComPackF, T> PCSForExpanderGKR<C, T> + for OrionSIMDFieldPCS<C::CircuitField, C::SimdCircuitField, C::ChallengeField, ComPackF, T> where C: GKRFieldConfig, ComPackF: SimdField<Scalar = C::CircuitField>, - OpenPackF: SimdField<Scalar = C::CircuitField>, T: Transcript<C::ChallengeField>, { const NAME: &'static str = "OrionPCSForExpanderGKR"; @@ -107,7 +99,6 @@ where C::SimdCircuitField, C::ChallengeField, ComPackF, - OpenPackF, T, >(proving_key, poly, &local_xs, transcript, scratch_pad); if mpi_config.world_size() == 1 { @@ -187,7 +178,6 @@ where C::SimdCircuitField, C::ChallengeField, ComPackF, - OpenPackF, T, >( verifying_key, @@ -201,7 +191,7 @@ where // NOTE: we now assume that the input opening is from the root machine, // as proofs from other machines are typically undefined - orion_verify_simd_field_aggregated::<C, ComPackF, OpenPackF, T>( + orion_verify_simd_field_aggregated::<C, ComPackF, T>( mpi_config.world_size(), verifying_key, commitment, @@ -212,3 +202,11 @@ where ) } } + +pub type OrionPCSForGKR<C, ComPack, T> = OrionSIMDFieldPCS< + <C as GKRFieldConfig>::CircuitField, + <C as GKRFieldConfig>::SimdCircuitField, + <C as GKRFieldConfig>::ChallengeField, + ComPack, + T, +>; diff --git a/poly_commit/src/orion/pcs_trait_impl.rs b/poly_commit/src/orion/pcs_trait_impl.rs index 8ad1a212..e00cfe2e 100644 --- a/poly_commit/src/orion/pcs_trait_impl.rs +++ b/poly_commit/src/orion/pcs_trait_impl.rs @@ -109,31 +109,28 @@ where } } -pub struct OrionSIMDFieldPCS<F, SimdF, EvalF, ComPackF, OpenPackF, T> +pub struct OrionSIMDFieldPCS<F, SimdF, EvalF, ComPackF, T> where F: Field, SimdF: SimdField<Scalar = F>, EvalF: ExtensionField<BaseField = F>, ComPackF: SimdField<Scalar = F>, - OpenPackF: SimdField<Scalar = F>, T: Transcript<EvalF>, { _marker_f: PhantomData<F>, _marker_simd_f: PhantomData<SimdF>, _marker_eval_f: PhantomData<EvalF>, _marker_commit_f: PhantomData<ComPackF>, - _marker_open_f: PhantomData<OpenPackF>, _marker_t: PhantomData<T>, } -impl<F, SimdF, EvalF, ComPackF, OpenPackF, T> PolynomialCommitmentScheme<EvalF, T> - for OrionSIMDFieldPCS<F, SimdF, EvalF, ComPackF, OpenPackF, T> +impl<F, SimdF, EvalF, ComPackF, T> PolynomialCommitmentScheme<EvalF, T> + for OrionSIMDFieldPCS<F, SimdF, EvalF, ComPackF, T> where F: Field, SimdF: SimdField<Scalar = F>, EvalF: ExtensionField<BaseField = F>, ComPackF: SimdField<Scalar = F>, - OpenPackF: SimdField<Scalar = F>, T: Transcript<EvalF>, { const NAME: &'static str = "OrionSIMDFieldPCS"; @@ -185,7 +182,7 @@ where poly.get_num_vars(), proving_key.num_vars - SimdF::PACK_SIZE.ilog2() as usize ); - let opening = orion_open_simd_field::<F, SimdF, EvalF, ComPackF, OpenPackF, T>( + let opening = orion_open_simd_field::<F, SimdF, EvalF, ComPackF, T>( proving_key, poly, x, @@ -193,16 +190,18 @@ where scratch_pad, ); - let real_num_vars = poly.get_num_vars() + SimdF::PACK_SIZE.ilog2() as usize; let num_vars_in_msg = { + let real_num_vars = poly.get_num_vars() + SimdF::PACK_SIZE.ilog2() as usize; let (_, m) = <Self::SRS as TensorCodeIOPPCS>::evals_shape::<F>(real_num_vars); - (m.ilog2() + SimdF::PACK_SIZE.ilog2()) as usize + m.ilog2() as usize }; + let num_vars_in_simd = SimdF::PACK_SIZE.ilog2() as usize; - let mut scratch = vec![EvalF::ZERO; 1 << num_vars_in_msg]; + // NOTE: working on evaluation response, evaluate the rest of the response + let mut scratch = vec![EvalF::ZERO; opening.eval_row.len()]; let eval = MultiLinearPoly::evaluate_with_buffer( &opening.eval_row, - &x[..num_vars_in_msg], + &x[num_vars_in_simd..num_vars_in_simd + num_vars_in_msg], &mut scratch, ); drop(scratch); @@ -221,7 +220,7 @@ where ) -> bool { assert_eq!(*params, verifying_key.num_vars); assert_eq!(x.len(), verifying_key.num_vars); - orion_verify_simd_field::<F, SimdF, EvalF, ComPackF, OpenPackF, T>( + orion_verify_simd_field::<F, SimdF, EvalF, ComPackF, T>( verifying_key, commitment, x, diff --git a/poly_commit/src/orion/simd_field_agg_impl.rs b/poly_commit/src/orion/simd_field_agg_impl.rs index 73acf0de..3625bb18 100644 --- a/poly_commit/src/orion/simd_field_agg_impl.rs +++ b/poly_commit/src/orion/simd_field_agg_impl.rs @@ -1,6 +1,7 @@ use std::iter; use arith::{Field, SimdField}; +use gf2::GF2; use gkr_field_config::GKRFieldConfig; use itertools::izip; use polynomials::{EqPolynomial, MultilinearExtension, RefMultiLinearPoly}; @@ -11,7 +12,7 @@ use crate::{ OrionSRS, PCS_SOUNDNESS_BITS, }; -pub(crate) fn orion_verify_simd_field_aggregated<C, ComPackF, OpenPackF, T>( +pub(crate) fn orion_verify_simd_field_aggregated<C, ComPackF, T>( mpi_world_size: usize, vk: &OrionSRS, commitment: &OrionCommitment, @@ -23,7 +24,6 @@ pub(crate) fn orion_verify_simd_field_aggregated<C, ComPackF, OpenPackF, T>( where C: GKRFieldConfig, ComPackF: SimdField<Scalar = C::CircuitField>, - OpenPackF: SimdField<Scalar = C::CircuitField>, T: Transcript<C::ChallengeField>, { let local_num_vars = eval_point.num_vars() - mpi_world_size.ilog2() as usize; @@ -35,19 +35,16 @@ where (row_num, msg_size) }; - let num_vars_in_local_rows = row_num.ilog2() as usize; - let num_vars_in_unpacked_msg = local_num_vars - num_vars_in_local_rows; - let local_xs = eval_point.local_xs(); + let num_vars_in_simd = C::SimdCircuitField::PACK_SIZE.ilog2() as usize; + let num_vars_in_msg = msg_size.ilog2() as usize; - let eq_local_coeffs = EqPolynomial::build_eq_x_r(&local_xs[num_vars_in_unpacked_msg..]); - let eq_worlds_coeffs = EqPolynomial::build_eq_x_r(&eval_point.x_mpi); + let global_xs = eval_point.global_xs(); // NOTE: working on evaluation response - let mut scratch = - vec![C::ChallengeField::ZERO; mpi_world_size * C::SimdCircuitField::PACK_SIZE * msg_size]; + let mut scratch = vec![C::ChallengeField::ZERO; msg_size]; let final_eval = RefMultiLinearPoly::from_ref(&proof.eval_row).evaluate_with_buffer( - &local_xs[..num_vars_in_unpacked_msg], - &mut scratch[..C::SimdCircuitField::PACK_SIZE * msg_size], + &global_xs[num_vars_in_simd..num_vars_in_simd + num_vars_in_msg], + &mut scratch, ); if final_eval != eval { return false; @@ -57,7 +54,9 @@ where // then draw query points from fiat shamir transcripts let proximity_reps = vk.proximity_repetitions::<C::ChallengeField>(PCS_SOUNDNESS_BITS); let proximity_local_coeffs: Vec<Vec<C::ChallengeField>> = (0..proximity_reps) - .map(|_| transcript.generate_challenge_field_elements(row_num)) + .map(|_| { + transcript.generate_challenge_field_elements(row_num * C::SimdCircuitField::PACK_SIZE) + }) .collect(); let query_num = vk.query_complexity(PCS_SOUNDNESS_BITS); @@ -67,104 +66,93 @@ where .map(|_| transcript.generate_challenge_field_elements(mpi_world_size)) .collect(); + // NOTE: work on the Merkle tree path validity + let roots: Vec<_> = proof + .query_openings + .chunks(query_num) + .map(|qs| qs[0].root()) + .collect(); + let final_root = { // NOTE: check all merkle paths, and check merkle roots against commitment - let roots: Vec<_> = proof - .query_openings - .chunks(query_num) - .map(|qs| qs[0].root()) - .collect(); - let final_tree_height = 1 + roots.len().ilog2(); - let (internals, _) = tree::Tree::new_with_leaf_nodes(roots, final_tree_height); + let (internals, _) = tree::Tree::new_with_leaf_nodes(roots.clone(), final_tree_height); internals[0] }; if final_root != *commitment { return false; } - // NOTE: prepare the interleaved alphabets from the MT paths, - // but reshuffle the packed elements into another direction - let mut scratch_f = vec![C::CircuitField::ZERO; C::SimdCircuitField::PACK_SIZE * row_num]; - let shuffled_interleaved_alphabet: Vec<_> = proof + if izip!(proof.query_openings.chunks(query_num), &roots) + .any(|(range_openings, root)| !orion_mt_verify(vk, &query_indices, range_openings, root)) + { + return false; + } + + // NOTE: prepare the interleaved alphabets from the MT paths + let mut packed_interleaved_alphabets: Vec<Vec<C::SimdCircuitField>> = + vec![Vec::new(); query_num]; + + let concatenated_packed_interleaved_alphabets: Vec<_> = proof .query_openings .iter() .map(|c| -> Vec<_> { - let mut elts = c.unpack_field_elems::<C::CircuitField, ComPackF>(); - transpose_in_place(&mut elts, &mut scratch_f, row_num); - elts.chunks(OpenPackF::PACK_SIZE) - .map(OpenPackF::pack) + let elts = c.unpack_field_elems::<C::CircuitField, ComPackF>(); + elts.chunks(C::SimdCircuitField::PACK_SIZE) + .map(C::SimdCircuitField::pack) + .collect() + }) + .collect(); + + concatenated_packed_interleaved_alphabets + .chunks(query_num) + .for_each(|alphabets| { + izip!(&mut packed_interleaved_alphabets, alphabets) + .for_each(|(packed, alphabet)| packed.extend_from_slice(alphabet)) + }); + + let mut eq_vars = vec![C::ChallengeField::ZERO; eval_point.num_vars() - num_vars_in_msg]; + eq_vars[..num_vars_in_simd].copy_from_slice(&global_xs[..num_vars_in_simd]); + eq_vars[num_vars_in_simd..].copy_from_slice(&global_xs[num_vars_in_simd + num_vars_in_msg..]); + + let eq_col_coeffs = EqPolynomial::build_eq_x_r(&eq_vars); + + let proximity_coeffs: Vec<Vec<C::ChallengeField>> = (0..proximity_reps) + .map(|i| { + proximity_worlds_coeffs[i] + .iter() + .flat_map(|w| { + proximity_local_coeffs[i] + .iter() + .map(|l| *l * *w) + .collect::<Vec<_>>() + }) .collect() }) .collect(); // NOTE: decide if expected alphabet matches actual responses - let table_num = row_num / OpenPackF::PACK_SIZE; - let mut luts = SubsetSumLUTs::<C::ChallengeField>::new(OpenPackF::PACK_SIZE, table_num); - assert_eq!(row_num % OpenPackF::PACK_SIZE, 0); - - let mut scratch_q = - vec![C::ChallengeField::ZERO; mpi_world_size * C::SimdCircuitField::PACK_SIZE * query_num]; - let mut codeword = - vec![C::ChallengeField::ZERO; C::SimdCircuitField::PACK_SIZE * vk.codeword_len()]; - - izip!( - &proximity_local_coeffs, - &proximity_worlds_coeffs, - &proof.proximity_rows - ) - .chain(iter::once(( - &eq_local_coeffs, - &eq_worlds_coeffs, - &proof.eval_row, - ))) - .all(|(local_coeffs, worlds_coeffs, msg)| { - // NOTE: compute final actual alphabets cross worlds - luts.build(local_coeffs); - let mut each_world_alphabets: Vec<_> = shuffled_interleaved_alphabet - .iter() - .flat_map(|c| -> Vec<_> { - c.chunks(table_num) - .map(|ts| luts.lookup_and_sum(ts)) - .collect() - }) - .collect(); - transpose_in_place(&mut each_world_alphabets, &mut scratch_q, mpi_world_size); - let actual_alphabets: Vec<_> = each_world_alphabets - .chunks(mpi_world_size) - .map(|rs| izip!(rs, worlds_coeffs).map(|(&l, &r)| l * r).sum()) - .collect(); - - // NOTE: compute SIMD codewords from the message - let mut msg_cloned = msg.clone(); - transpose_in_place( - &mut msg_cloned, - &mut scratch[..C::SimdCircuitField::PACK_SIZE * msg_size], - msg_size, - ); - izip!( - msg_cloned.chunks(msg_size), - codeword.chunks_mut(vk.codeword_len()) - ) - .for_each(|(msg, c)| vk.code_instance.encode_in_place(msg, c).unwrap()); - transpose_in_place( - &mut codeword, - &mut scratch[..C::SimdCircuitField::PACK_SIZE * vk.codeword_len()], - C::SimdCircuitField::PACK_SIZE, - ); - - // NOTE: check actual SIMD alphabets against expected SIMD alphabets - izip!( - &query_indices, - actual_alphabets.chunks(C::SimdCircuitField::PACK_SIZE) - ) - .all(|(qi, actual_alphabets)| { - let index = qi % vk.codeword_len(); - - let simd_starts = index * C::SimdCircuitField::PACK_SIZE; - let simd_ends = (index + 1) * C::SimdCircuitField::PACK_SIZE; - - izip!(&codeword[simd_starts..simd_ends], actual_alphabets).all(|(ec, ac)| ec == ac) + izip!(&proximity_coeffs, &proof.proximity_rows) + .chain(iter::once((&eq_col_coeffs, &proof.eval_row))) + .all(|(rl, msg)| { + let codeword = match vk.code_instance.encode(msg) { + Ok(c) => c, + _ => return false, + }; + + match C::CircuitField::NAME { + GF2::NAME => lut_verify_alphabet_check( + &codeword, + rl, + &query_indices, + &packed_interleaved_alphabets, + ), + _ => simd_verify_alphabet_check( + &codeword, + rl, + &query_indices, + &packed_interleaved_alphabets, + ), + } }) - }) } diff --git a/poly_commit/src/orion/simd_field_agg_tests.rs b/poly_commit/src/orion/simd_field_agg_tests.rs index db335063..63d14f3f 100644 --- a/poly_commit/src/orion/simd_field_agg_tests.rs +++ b/poly_commit/src/orion/simd_field_agg_tests.rs @@ -2,17 +2,17 @@ use std::marker::PhantomData; use arith::{ExtensionField, Field, SimdField}; use ark_std::test_rng; -use gf2::{GF2x128, GF2x8}; +use gf2::GF2x128; use gf2_128::GF2_128; -use gkr_field_config::{GF2ExtConfig, GKRFieldConfig}; +use gkr_field_config::{GF2ExtConfig, GKRFieldConfig, M31ExtConfig}; use itertools::izip; +use mersenne31::{M31Ext3, M31x16}; use polynomials::{EqPolynomial, MultiLinearPoly}; use transcript::{BytesHashTranscript, Keccak256hasher, Transcript}; use crate::{ orion::{simd_field_agg_impl::*, utils::*, *}, - traits::TensorCodeIOPPCS, - ExpanderGKRChallenge, + ExpanderGKRChallenge, RawExpanderGKR, }; #[derive(Clone)] @@ -77,13 +77,10 @@ where } } -fn test_orion_simd_aggregate_verify_helper<C, ComPackF, OpenPackF, T>( - num_parties: usize, - num_vars: usize, -) where +fn test_orion_simd_aggregate_verify_helper<C, ComPackF, T>(num_parties: usize, num_vars: usize) +where C: GKRFieldConfig, ComPackF: SimdField<Scalar = C::CircuitField>, - OpenPackF: SimdField<Scalar = C::CircuitField>, T: Transcript<C::ChallengeField>, { assert!(num_parties.is_power_of_two()); @@ -93,14 +90,6 @@ fn test_orion_simd_aggregate_verify_helper<C, ComPackF, OpenPackF, T>( let simd_num_vars = C::SimdCircuitField::PACK_SIZE.ilog2() as usize; let world_num_vars = num_parties.ilog2() as usize; - let num_vars_in_unpacked_msg = { - let (row_field_elems, _) = - OrionSRS::evals_shape::<C::CircuitField>(num_vars - world_num_vars); - let row_num = row_field_elems / C::SimdCircuitField::PACK_SIZE; - let num_vars_in_row = row_num.ilog2() as usize; - num_vars - world_num_vars - num_vars_in_row - }; - let global_poly = MultiLinearPoly::<C::SimdCircuitField>::random(num_vars - simd_num_vars, &mut rng); @@ -149,46 +138,41 @@ fn test_orion_simd_aggregate_verify_helper<C, ComPackF, OpenPackF, T>( internals[0] }; - let openings: Vec<_> = izip!( - &mut committee, - global_poly.coeffs.chunks(1 << local_real_num_vars) - ) - .map(|(committer, eval_slice)| { - let cloned_poly = MultiLinearPoly::new(eval_slice.to_vec()); - orion_open_simd_field::< - C::CircuitField, - C::SimdCircuitField, - C::ChallengeField, - ComPackF, - OpenPackF, - T, - >( - &srs, - &cloned_poly, - &gkr_challenge.local_xs(), - &mut committer.transcript, - &committer.scratch_pad, + let openings: Vec<_> = + izip!( + &mut committee, + global_poly.coeffs.chunks(1 << local_real_num_vars) ) - }) - .collect(); + .map(|(committer, eval_slice)| { + let cloned_poly = MultiLinearPoly::new(eval_slice.to_vec()); + orion_open_simd_field::< + C::CircuitField, + C::SimdCircuitField, + C::ChallengeField, + ComPackF, + T, + >( + &srs, + &cloned_poly, + &gkr_challenge.local_xs(), + &mut committer.transcript, + &committer.scratch_pad, + ) + }) + .collect(); let mut aggregator_transcript = committee[0].transcript.clone(); let aggregated_proof = orion_proof_aggregate::<C, T>(&openings, &gkr_challenge.x_mpi, &mut aggregator_transcript); - let mut scratch = vec![C::ChallengeField::ZERO; 1 << num_vars_in_unpacked_msg]; - let final_expected_eval = MultiLinearPoly::evaluate_with_buffer( - &aggregated_proof.eval_row, - &gkr_challenge.local_xs()[..num_vars_in_unpacked_msg], - &mut scratch, + let final_expected_eval = RawExpanderGKR::<C, T>::eval( + &global_poly.coeffs, + &gkr_challenge.x, + &gkr_challenge.x_simd, + &gkr_challenge.x_mpi, ); - assert!(orion_verify_simd_field_aggregated::< - C, - ComPackF, - OpenPackF, - T, - >( + assert!(orion_verify_simd_field_aggregated::<C, ComPackF, T>( num_parties, &srs, &final_commitment, @@ -207,8 +191,15 @@ fn test_orion_simd_aggregate_verify() { test_orion_simd_aggregate_verify_helper::< GF2ExtConfig, GF2x128, - GF2x8, BytesHashTranscript<GF2_128, Keccak256hasher>, >(parties, num_var) + }); + + (18..25).for_each(|num_var| { + test_orion_simd_aggregate_verify_helper::< + M31ExtConfig, + M31x16, + BytesHashTranscript<M31Ext3, Keccak256hasher>, + >(parties, num_var) }) } diff --git a/poly_commit/src/orion/simd_field_impl.rs b/poly_commit/src/orion/simd_field_impl.rs index db293936..4c36e4c8 100644 --- a/poly_commit/src/orion/simd_field_impl.rs +++ b/poly_commit/src/orion/simd_field_impl.rs @@ -1,39 +1,17 @@ use std::iter; use arith::{ExtensionField, Field, SimdField}; +use gf2::GF2; use itertools::izip; use polynomials::{EqPolynomial, MultilinearExtension, RefMultiLinearPoly}; use transcript::Transcript; use crate::{ - orion::{ - utils::{commit_encoded, orion_mt_openings, orion_mt_verify, transpose_in_place}, - OrionCommitment, OrionProof, OrionResult, OrionSRS, OrionScratchPad, - }, + orion::{utils::*, OrionCommitment, OrionProof, OrionResult, OrionSRS, OrionScratchPad}, traits::TensorCodeIOPPCS, - SubsetSumLUTs, PCS_SOUNDNESS_BITS, + PCS_SOUNDNESS_BITS, }; -#[inline(always)] -fn transpose_and_pack_simd<F, SimdF, PackF>(evaluations: &mut [SimdF], row_num: usize) -> Vec<PackF> -where - F: Field, - SimdF: SimdField<Scalar = F>, - PackF: SimdField<Scalar = F>, -{ - // NOTE: pre transpose evaluations - let mut scratch = vec![SimdF::ZERO; evaluations.len()]; - transpose_in_place(evaluations, &mut scratch, row_num); - drop(scratch); - - // NOTE: SIMD pack each row of transposed matrix - let relative_pack_size = PackF::PACK_SIZE / SimdF::PACK_SIZE; - evaluations - .chunks(relative_pack_size) - .map(PackF::pack_from_simd) - .collect() -} - pub fn orion_commit_simd_field<F, SimdF, ComPackF>( pk: &OrionSRS, poly: &impl MultilinearExtension<SimdF>, @@ -71,39 +49,12 @@ where commit_encoded(pk, &packed_evals, scratch_pad, packed_rows, msg_size) } -#[inline(always)] -fn transpose_and_shuffle_simd<F, SimdF, PackF>( - evaluations: &mut [SimdF], - row_num: usize, -) -> Vec<PackF> -where - F: Field, - SimdF: SimdField<Scalar = F>, - PackF: SimdField<Scalar = F>, -{ - // NOTE: pre transpose evaluations - let mut scratch = vec![SimdF::ZERO; evaluations.len()]; - transpose_in_place(evaluations, &mut scratch, row_num); - drop(scratch); - - // NOTE: reshuffle the transposed matrix, from SIMD over row to SIMD over col - let mut scratch = vec![F::ZERO; SimdF::PACK_SIZE * row_num]; - evaluations - .chunks(row_num) - .flat_map(|row_simds| -> Vec<_> { - let mut elts: Vec<_> = row_simds.iter().flat_map(|f| f.unpack()).collect(); - transpose_in_place(&mut elts, &mut scratch, row_num); - elts.chunks(PackF::PACK_SIZE).map(PackF::pack).collect() - }) - .collect() -} - // NOTE: this implementation doesn't quite align with opening for // multilinear polynomials over base field, // as this directly plug into GKR argument system. // In that context, there is no need to evaluate, // as evaluation statement can be reduced on the verifier side. -pub fn orion_open_simd_field<F, SimdF, EvalF, ComPackF, OpenPackF, T>( +pub fn orion_open_simd_field<F, SimdF, EvalF, ComPackF, T>( pk: &OrionSRS, poly: &impl MultilinearExtension<SimdF>, point: &[EvalF], @@ -115,7 +66,6 @@ where SimdF: SimdField<Scalar = F>, EvalF: ExtensionField<BaseField = F>, ComPackF: SimdField<Scalar = F>, - OpenPackF: SimdField<Scalar = F>, T: Transcript<EvalF>, { let (row_num, msg_size) = { @@ -128,44 +78,45 @@ where }; let num_vars_in_row = row_num.ilog2() as usize; - let num_vars_in_unpacked_msg = point.len() - num_vars_in_row; - - // NOTE: transpose and shuffle evaluations (repack evaluations in another direction) - // for linear combinations in evaulation/proximity tests - let mut evals = poly.hypercube_basis(); - assert_eq!(evals.len() * SimdF::PACK_SIZE % OpenPackF::PACK_SIZE, 0); - - let packed_shuffled_evals: Vec<OpenPackF> = transpose_and_shuffle_simd(&mut evals, row_num); - drop(evals); + let num_vars_in_simd = SimdF::PACK_SIZE.ilog2() as usize; - // NOTE: declare the look up tables for column sums - let tables_num = row_num / OpenPackF::PACK_SIZE; - let mut luts = SubsetSumLUTs::<EvalF>::new(OpenPackF::PACK_SIZE, tables_num); - assert_eq!(row_num % OpenPackF::PACK_SIZE, 0); + // NOTE: transpose SIMD evaluations for linear combinations in evaulation/proximity tests + let mut packed_evals = poly.hypercube_basis(); - // NOTE: working on evaluation response of tensor code IOP based PCS - let mut eval_row = vec![EvalF::ZERO; msg_size * SimdF::PACK_SIZE]; + let mut scratch = vec![SimdF::ZERO; packed_evals.len()]; + transpose_in_place(&mut packed_evals, &mut scratch, row_num); + drop(scratch); - let eq_coeffs = EqPolynomial::build_eq_x_r(&point[num_vars_in_unpacked_msg..]); - luts.build(&eq_coeffs); + // NOTE: pre-compute the eq linear combine coeffs for linear combination + let mut eq_vars = vec![EvalF::ZERO; num_vars_in_row + num_vars_in_simd]; + eq_vars[..num_vars_in_simd].copy_from_slice(&point[..num_vars_in_simd]); + eq_vars[num_vars_in_simd..].copy_from_slice(&point[point.len() - num_vars_in_row..]); + let eq_col_coeffs = EqPolynomial::build_eq_x_r(&eq_vars); - izip!(packed_shuffled_evals.chunks(tables_num), &mut eval_row) - .for_each(|(p_col, res)| *res = luts.lookup_and_sum(p_col)); + // NOTE: pre-declare the spaces for returning evaluation and proximity queries + let mut eval_row = vec![EvalF::ZERO; msg_size]; - // NOTE: draw random linear combination out - // and compose proximity response(s) of tensor code IOP based PCS let proximity_test_num = pk.proximity_repetitions::<EvalF>(PCS_SOUNDNESS_BITS); - let mut proximity_rows = - vec![vec![EvalF::ZERO; msg_size * SimdF::PACK_SIZE]; proximity_test_num]; - - proximity_rows.iter_mut().for_each(|row_buffer| { - let random_coeffs = transcript.generate_challenge_field_elements(row_num); - luts.build(&random_coeffs); - - izip!(packed_shuffled_evals.chunks(tables_num), row_buffer) - .for_each(|(p_col, res)| *res = luts.lookup_and_sum(p_col)); - }); - drop(luts); + let mut proximity_rows = vec![vec![EvalF::ZERO; msg_size]; proximity_test_num]; + + match F::NAME { + GF2::NAME => lut_open_linear_combine( + row_num * SimdF::PACK_SIZE, + &packed_evals, + &eq_col_coeffs, + &mut eval_row, + &mut proximity_rows, + transcript, + ), + _ => simd_open_linear_combine( + row_num * SimdF::PACK_SIZE, + &packed_evals, + &eq_col_coeffs, + &mut eval_row, + &mut proximity_rows, + transcript, + ), + } // NOTE: MT opening for point queries let query_openings = orion_mt_openings(pk, transcript, scratch_pad); @@ -177,7 +128,7 @@ where } } -pub fn orion_verify_simd_field<F, SimdF, EvalF, ComPackF, OpenPackF, T>( +pub fn orion_verify_simd_field<F, SimdF, EvalF, ComPackF, T>( vk: &OrionSRS, commitment: &OrionCommitment, point: &[EvalF], @@ -190,7 +141,6 @@ where SimdF: SimdField<Scalar = F>, EvalF: ExtensionField<BaseField = F>, ComPackF: SimdField<Scalar = F>, - OpenPackF: SimdField<Scalar = F>, T: Transcript<EvalF>, { let (row_num, msg_size) = { @@ -200,12 +150,15 @@ where }; let num_vars_in_row = row_num.ilog2() as usize; - let num_vars_in_unpacked_msg = point.len() - num_vars_in_row; + let num_vars_in_msg = msg_size.ilog2() as usize; + let num_vars_in_simd = SimdF::PACK_SIZE.ilog2() as usize; // NOTE: working on evaluation response, evaluate the rest of the response - let mut scratch = vec![EvalF::ZERO; msg_size * SimdF::PACK_SIZE]; - let final_eval = RefMultiLinearPoly::from_ref(&proof.eval_row) - .evaluate_with_buffer(&point[..num_vars_in_unpacked_msg], &mut scratch); + let mut scratch = vec![EvalF::ZERO; msg_size]; + let final_eval = RefMultiLinearPoly::from_ref(&proof.eval_row).evaluate_with_buffer( + &point[num_vars_in_simd..num_vars_in_simd + num_vars_in_msg], + &mut scratch, + ); if final_eval != evaluation { return false; @@ -215,8 +168,9 @@ where // then draw query points from fiat shamir transcripts let proximity_reps = vk.proximity_repetitions::<EvalF>(PCS_SOUNDNESS_BITS); let random_linear_combinations: Vec<Vec<EvalF>> = (0..proximity_reps) - .map(|_| transcript.generate_challenge_field_elements(row_num)) + .map(|_| transcript.generate_challenge_field_elements(row_num * SimdF::PACK_SIZE)) .collect(); + let query_num = vk.query_complexity(PCS_SOUNDNESS_BITS); let query_indices = transcript.generate_challenge_index_vector(query_num); @@ -227,57 +181,41 @@ where // NOTE: prepare the interleaved alphabets from the MT paths, // but reshuffle the packed elements into another direction - let mut scratch = vec![F::ZERO; SimdF::PACK_SIZE * row_num]; - let shuffled_interleaved_alphabet: Vec<Vec<OpenPackF>> = proof + let packed_interleaved_alphabets: Vec<Vec<SimdF>> = proof .query_openings .iter() .map(|c| -> Vec<_> { - let mut elts = c.unpack_field_elems::<F, ComPackF>(); - transpose_in_place(&mut elts, &mut scratch, row_num); - elts.chunks(OpenPackF::PACK_SIZE) - .map(OpenPackF::pack) - .collect() + let elts = c.unpack_field_elems::<F, ComPackF>(); + elts.chunks(SimdF::PACK_SIZE).map(SimdF::pack).collect() }) .collect(); - // NOTE: declare the look up tables for column sums - let tables_num = row_num / OpenPackF::PACK_SIZE; - let mut luts = SubsetSumLUTs::<EvalF>::new(OpenPackF::PACK_SIZE, tables_num); - assert_eq!(row_num % OpenPackF::PACK_SIZE, 0); - - let eq_linear_combination = EqPolynomial::build_eq_x_r(&point[num_vars_in_unpacked_msg..]); - let mut scratch_msg = vec![EvalF::ZERO; SimdF::PACK_SIZE * msg_size]; - let mut scratch_codeword = vec![EvalF::ZERO; SimdF::PACK_SIZE * vk.codeword_len()]; + let mut eq_vars = vec![EvalF::ZERO; num_vars_in_row + num_vars_in_simd]; + eq_vars[..num_vars_in_simd].copy_from_slice(&point[..num_vars_in_simd]); + eq_vars[num_vars_in_simd..].copy_from_slice(&point[point.len() - num_vars_in_row..]); + let eq_linear_combination = EqPolynomial::build_eq_x_r(&eq_vars); izip!(&random_linear_combinations, &proof.proximity_rows) .chain(iter::once((&eq_linear_combination, &proof.eval_row))) .all(|(rl, msg)| { - let mut msg_cloned = msg.clone(); - transpose_in_place(&mut msg_cloned, &mut scratch_msg, msg_size); - let mut codeword: Vec<_> = msg_cloned - .chunks(msg_size) - .flat_map(|m| vk.code_instance.encode(m).unwrap()) - .collect(); - transpose_in_place(&mut codeword, &mut scratch_codeword, SimdF::PACK_SIZE); - - luts.build(rl); - - izip!(&query_indices, &shuffled_interleaved_alphabet).all( - |(&qi, interleaved_alphabet)| { - let index = qi % vk.codeword_len(); - - let simd_starts = index * SimdF::PACK_SIZE; - let simd_ends = (index + 1) * SimdF::PACK_SIZE; - - izip!( - &codeword[simd_starts..simd_ends], - interleaved_alphabet.chunks(tables_num) - ) - .all(|(expected_alphabet, packed_index)| { - let alphabet = luts.lookup_and_sum(packed_index); - alphabet == *expected_alphabet - }) - }, - ) + let codeword = match vk.code_instance.encode(msg) { + Ok(c) => c, + _ => return false, + }; + + match F::NAME { + GF2::NAME => lut_verify_alphabet_check( + &codeword, + rl, + &query_indices, + &packed_interleaved_alphabets, + ), + _ => simd_verify_alphabet_check( + &codeword, + rl, + &query_indices, + &packed_interleaved_alphabets, + ), + } }) } diff --git a/poly_commit/src/orion/utils.rs b/poly_commit/src/orion/utils.rs index df88ba88..f5ff94e3 100644 --- a/poly_commit/src/orion/utils.rs +++ b/poly_commit/src/orion/utils.rs @@ -209,8 +209,49 @@ pub(crate) fn transpose_in_place<F: Field>(mat: &mut [F], scratch: &mut [F], row mat.copy_from_slice(scratch); } +#[inline(always)] +pub(crate) fn transpose_and_pack<F, SimdF>(evaluations: &mut [F], row_num: usize) -> Vec<SimdF> +where + F: Field, + SimdF: SimdField<Scalar = F>, +{ + // NOTE: pre transpose evaluations + let mut scratch = vec![F::ZERO; evaluations.len()]; + transpose_in_place(evaluations, &mut scratch, row_num); + drop(scratch); + + // NOTE: SIMD pack each row of transposed matrix + evaluations + .chunks(SimdF::PACK_SIZE) + .map(SimdField::pack) + .collect() +} + +#[inline(always)] +pub(crate) fn transpose_and_pack_simd<F, SimdF, PackF>( + evaluations: &mut [SimdF], + row_num: usize, +) -> Vec<PackF> +where + F: Field, + SimdF: SimdField<Scalar = F>, + PackF: SimdField<Scalar = F>, +{ + // NOTE: pre transpose evaluations + let mut scratch = vec![SimdF::ZERO; evaluations.len()]; + transpose_in_place(evaluations, &mut scratch, row_num); + drop(scratch); + + // NOTE: SIMD pack each row of transposed matrix + let relative_pack_size = PackF::PACK_SIZE / SimdF::PACK_SIZE; + evaluations + .chunks(relative_pack_size) + .map(PackF::pack_from_simd) + .collect() +} + /* - * LINEAR OPERATIONS + * LINEAR OPERATIONS FOR GF2 (LOOKUP TABLE BASED) */ pub struct SubsetSumLUTs<F: Field> { @@ -270,6 +311,73 @@ impl<F: Field> SubsetSumLUTs<F> { } } +#[inline(always)] +pub(crate) fn lut_open_linear_combine<F, EvalF, SimdF, T>( + row_num: usize, + packed_evals: &[SimdF], + eq_col_coeffs: &[EvalF], + eval_row: &mut [EvalF], + proximity_rows: &mut [Vec<EvalF>], + transcript: &mut T, +) where + F: Field, + EvalF: ExtensionField<BaseField = F>, + SimdF: SimdField<Scalar = F>, + T: Transcript<EvalF>, +{ + // NOTE: declare the look up tables for column sums + let table_num = row_num / SimdF::PACK_SIZE; + let mut luts = SubsetSumLUTs::<EvalF>::new(SimdF::PACK_SIZE, table_num); + assert_eq!(row_num % SimdF::PACK_SIZE, 0); + + // NOTE: working on evaluation response of tensor code IOP based PCS + luts.build(eq_col_coeffs); + + izip!(packed_evals.chunks(table_num), eval_row) + .for_each(|(p_col, res)| *res = luts.lookup_and_sum(p_col)); + + // NOTE: draw random linear combination out + // and compose proximity response(s) of tensor code IOP based PCS + proximity_rows.iter_mut().for_each(|row_buffer| { + let random_coeffs = transcript.generate_challenge_field_elements(row_num); + luts.build(&random_coeffs); + + izip!(packed_evals.chunks(table_num), row_buffer) + .for_each(|(p_col, res)| *res = luts.lookup_and_sum(p_col)); + }); + drop(luts); +} + +#[inline(always)] +pub(crate) fn lut_verify_alphabet_check<F, SimdF, ExtF>( + codeword: &[ExtF], + fixed_rl: &[ExtF], + query_indices: &[usize], + packed_interleaved_alphabets: &[Vec<SimdF>], +) -> bool +where + F: Field, + SimdF: SimdField<Scalar = F>, + ExtF: ExtensionField<BaseField = F>, +{ + // NOTE: build up lookup table + let tables_num = fixed_rl.len() / SimdF::PACK_SIZE; + assert_eq!(fixed_rl.len() % SimdF::PACK_SIZE, 0); + let mut luts = SubsetSumLUTs::<ExtF>::new(SimdF::PACK_SIZE, tables_num); + + luts.build(fixed_rl); + + izip!(query_indices, packed_interleaved_alphabets).all(|(qi, interleaved_alphabet)| { + let index = qi % codeword.len(); + let alphabet = luts.lookup_and_sum(interleaved_alphabet); + alphabet == codeword[index] + }) +} + +/* + * LINEAR OPERATIONS FOR MERSENNE31 (SIMD BASED) + */ + #[inline(always)] pub(crate) fn simd_inner_product<F, SimdF>(lhs: &[SimdF], rhs: &[SimdF]) -> F where @@ -302,3 +410,65 @@ where ExtF::from_limbs(&ext_limbs) } + +#[inline(always)] +pub(crate) fn simd_open_linear_combine<F, EvalF, SimdF, T>( + row_num: usize, + packed_evals: &[SimdF], + eq_col_coeffs: &[EvalF], + eval_row: &mut [EvalF], + proximity_rows: &mut [Vec<EvalF>], + transcript: &mut T, +) where + F: Field, + EvalF: ExtensionField<BaseField = F>, + SimdF: SimdField<Scalar = F>, + T: Transcript<EvalF>, +{ + // NOTE: check SIMD inner product numbers for column sums + let simd_inner_prods = row_num / SimdF::PACK_SIZE; + assert_eq!(row_num % SimdF::PACK_SIZE, 0); + + // NOTE: working on evaluation response of tensor code IOP based PCS + let mut eq_col_coeffs_limbs: Vec<_> = eq_col_coeffs.iter().flat_map(|e| e.to_limbs()).collect(); + let eq_col_simd_limbs: Vec<_> = transpose_and_pack(&mut eq_col_coeffs_limbs, row_num); + + izip!(packed_evals.chunks(simd_inner_prods), eval_row) + .for_each(|(p_col, res)| *res = simd_ext_base_inner_prod(&eq_col_simd_limbs, p_col)); + + // NOTE: draw random linear combination out + // and compose proximity response(s) of tensor code IOP based PCS + proximity_rows.iter_mut().for_each(|row_buffer| { + let random_coeffs = transcript.generate_challenge_field_elements(row_num); + let mut proximity_limbs: Vec<_> = random_coeffs.iter().flat_map(|e| e.to_limbs()).collect(); + let proximity_simd_limbs: Vec<_> = transpose_and_pack(&mut proximity_limbs, row_num); + + izip!(packed_evals.chunks(simd_inner_prods), row_buffer) + .for_each(|(p_col, res)| *res = simd_ext_base_inner_prod(&proximity_simd_limbs, p_col)); + }); +} + +#[inline(always)] +pub(crate) fn simd_verify_alphabet_check<F, SimdF, ExtF>( + codeword: &[ExtF], + fixed_rl: &[ExtF], + query_indices: &[usize], + packed_interleaved_alphabets: &[Vec<SimdF>], +) -> bool +where + F: Field, + SimdF: SimdField<Scalar = F>, + ExtF: ExtensionField<BaseField = F>, +{ + // NOTE: check SIMD inner product numbers for column sums + assert_eq!(fixed_rl.len() % SimdF::PACK_SIZE, 0); + + let mut rl_limbs: Vec<_> = fixed_rl.iter().flat_map(|e| e.to_limbs()).collect(); + let rl_simd_limbs: Vec<SimdF> = transpose_and_pack(&mut rl_limbs, fixed_rl.len()); + + izip!(query_indices, packed_interleaved_alphabets).all(|(qi, interleaved_alphabet)| { + let index = qi % codeword.len(); + let alphabet: ExtF = simd_ext_base_inner_prod(&rl_simd_limbs, interleaved_alphabet); + alphabet == codeword[index] + }) +} diff --git a/poly_commit/src/traits.rs b/poly_commit/src/traits.rs index 07f82399..a130d57b 100644 --- a/poly_commit/src/traits.rs +++ b/poly_commit/src/traits.rs @@ -80,6 +80,13 @@ impl<C: GKRFieldConfig> ExpanderGKRChallenge<C> { local_xs } + pub fn global_xs(&self) -> Vec<C::ChallengeField> { + let mut global_xs = vec![C::ChallengeField::ZERO; self.num_vars()]; + global_xs[..self.x_simd.len() + self.x.len()].copy_from_slice(&self.local_xs()); + global_xs[self.x_simd.len() + self.x.len()..].copy_from_slice(&self.x_mpi); + global_xs + } + pub fn num_vars(&self) -> usize { self.x.len() + self.x_simd.len() + self.x_mpi.len() } diff --git a/poly_commit/tests/test_orion.rs b/poly_commit/tests/test_orion.rs index aa8759cf..a56ba6ad 100644 --- a/poly_commit/tests/test_orion.rs +++ b/poly_commit/tests/test_orion.rs @@ -4,7 +4,7 @@ use arith::{ExtensionField, Field, SimdField}; use ark_std::test_rng; use gf2::{GF2x128, GF2x64, GF2x8, GF2}; use gf2_128::GF2_128; -use gkr_field_config::{GF2ExtConfig, GKRFieldConfig}; +use gkr_field_config::{GF2ExtConfig, GKRFieldConfig, M31ExtConfig}; use mersenne31::{M31Ext3, M31x16, M31}; use mpi_config::MPIConfig; use poly_commit::*; @@ -55,13 +55,12 @@ fn test_orion_base_field_pcs_full_e2e() { test_orion_base_field_pcs_generics::<M31, M31Ext3, M31x16, M31x16>(16, 22) } -fn test_orion_simd_field_pcs_generics<F, SimdF, EvalF, ComPackF, OpenPackF>() +fn test_orion_simd_field_pcs_generics<F, SimdF, EvalF, ComPackF>() where F: Field, SimdF: SimdField<Scalar = F>, EvalF: ExtensionField<BaseField = F>, ComPackF: SimdField<Scalar = F>, - OpenPackF: SimdField<Scalar = F>, { let mut rng = test_rng(); @@ -84,7 +83,6 @@ where SimdF, EvalF, ComPackF, - OpenPackF, BytesHashTranscript<EvalF, Keccak256hasher>, >, >(&num_vars, &poly, &xs); @@ -93,23 +91,24 @@ where #[test] fn test_orion_simd_field_pcs_full_e2e() { - test_orion_simd_field_pcs_generics::<GF2, GF2x8, GF2_128, GF2x64, GF2x8>(); - test_orion_simd_field_pcs_generics::<GF2, GF2x8, GF2_128, GF2x128, GF2x8>(); + test_orion_simd_field_pcs_generics::<GF2, GF2x8, GF2_128, GF2x64>(); + test_orion_simd_field_pcs_generics::<GF2, GF2x8, GF2_128, GF2x128>(); + test_orion_simd_field_pcs_generics::<M31, M31x16, M31Ext3, M31x16>(); } -fn test_orion_for_expander_gkr_generics<C, ComPackF, OpenPackF, T>(total_num_vars: usize) -where +fn test_orion_for_expander_gkr_generics<C, ComPackF, T>( + mpi_config_ref: &MPIConfig, + total_num_vars: usize, +) where C: GKRFieldConfig, ComPackF: SimdField<Scalar = C::CircuitField>, - OpenPackF: SimdField<Scalar = C::CircuitField>, T: Transcript<C::ChallengeField>, { let mut rng = test_rng(); - let mpi_config = MPIConfig::new(); // NOTE: generate global random polynomial let num_vars_in_simd = C::SimdCircuitField::PACK_SIZE.ilog2() as usize; - let num_vars_in_mpi = mpi_config.world_size().ilog2() as usize; + let num_vars_in_mpi = mpi_config_ref.world_size().ilog2() as usize; let num_vars_in_each_poly = total_num_vars - num_vars_in_mpi - num_vars_in_simd; let num_vars_in_global_poly = total_num_vars - num_vars_in_simd; @@ -133,11 +132,11 @@ where dbg!(global_poly.get_num_vars(), global_poly.coeffs[0]); dbg!(&challenge_point.x_mpi); - dbg!(mpi_config.world_size(), mpi_config.world_rank()); + dbg!(mpi_config_ref.world_size(), mpi_config_ref.world_rank()); // NOTE separate polynomial into different pieces by mpi rank - let poly_vars_stride = (1 << global_poly.get_num_vars()) / mpi_config.world_size(); - let poly_coeff_starts = mpi_config.world_rank() * poly_vars_stride; + let poly_vars_stride = (1 << global_poly.get_num_vars()) / mpi_config_ref.world_size(); + let poly_coeff_starts = mpi_config_ref.world_rank() * poly_vars_stride; let poly_coeff_ends = poly_coeff_starts + poly_vars_stride; let local_poly = MultiLinearPoly::new(global_poly.coeffs[poly_coeff_starts..poly_coeff_ends].to_vec()); @@ -147,31 +146,31 @@ where common::test_pcs_for_expander_gkr::< C, T, - OrionSIMDFieldPCS< - C::CircuitField, - C::SimdCircuitField, - C::ChallengeField, - ComPackF, - OpenPackF, - T, - >, + OrionSIMDFieldPCS<C::CircuitField, C::SimdCircuitField, C::ChallengeField, ComPackF, T>, >( &num_vars_in_each_poly, - &mpi_config, + &mpi_config_ref, &mut transcript, &local_poly, &vec![challenge_point], ); - - MPIConfig::finalize() } #[test] fn test_orion_for_expander_gkr() { + let mpi_config = MPIConfig::new(); + test_orion_for_expander_gkr_generics::< GF2ExtConfig, GF2x128, - GF2x8, BytesHashTranscript<_, Keccak256hasher>, - >(30); + >(&mpi_config, 30); + + test_orion_for_expander_gkr_generics::< + M31ExtConfig, + M31x16, + BytesHashTranscript<_, Keccak256hasher>, + >(&mpi_config, 20); + + MPIConfig::finalize() } From b1cfa707467dec5fc101ab046f75d87ec506f5ad Mon Sep 17 00:00:00 2001 From: Hang Su <tonyfloater@gmail.com> Date: Sun, 12 Jan 2025 04:02:16 -0500 Subject: [PATCH 57/65] minor - each sumcheck IOP prove/verify sync up randomness beforehand --- gkr/src/prover/linear_gkr.rs | 1 + gkr/src/tests/compiler_integration.rs | 59 -------------------- gkr/src/tests/gkr_correctness.rs | 10 ++++ gkr/src/verifier.rs | 13 ++--- poly_commit/src/orion/simd_field_agg_impl.rs | 10 ++-- transcript/src/lib.rs | 2 +- transcript/src/transcript_utils.rs | 13 +++++ 7 files changed, 35 insertions(+), 73 deletions(-) delete mode 100644 gkr/src/tests/compiler_integration.rs diff --git a/gkr/src/prover/linear_gkr.rs b/gkr/src/prover/linear_gkr.rs index 95e0e262..bbb4e5f2 100644 --- a/gkr/src/prover/linear_gkr.rs +++ b/gkr/src/prover/linear_gkr.rs @@ -140,6 +140,7 @@ impl<Cfg: GKRConfig> Prover<Cfg> { ); if let Some(ry) = ry { + transcript_root_broadcast(&mut transcript, &self.config.mpi_config); self.prove_input_layer_claim( &mle_ref, &ExpanderGKRChallenge { diff --git a/gkr/src/tests/compiler_integration.rs b/gkr/src/tests/compiler_integration.rs deleted file mode 100644 index ff99b539..00000000 --- a/gkr/src/tests/compiler_integration.rs +++ /dev/null @@ -1,59 +0,0 @@ -// use std::fs; - -// use arith::{Field, M31x16}; -// use expander_rs::utils::*; -// use expander_rs::{Circuit, Config, GKRScheme, M31ExtConfigSha2, Prover, Verifier}; -// use rand::Rng; - -// const FILENAME_PROOF: &str = "data/proof.bin"; - -// #[test] -// fn test_compiler_format_integration() { -// let config = Config::<M31ExtConfigSha2>::new(GKRScheme::Vanilla); - -// let mut circuit = Circuit::<M31ExtConfigSha2>::load_circuit(KECCAK_M31_CIRCUIT); -// println!("Circuit loaded."); -// circuit.load_witness_file(KECCAK_WITNESS); -// println!("Witness loaded."); -// circuit.evaluate(); -// println!("Circuit evaluated."); - -// // check last layer first output -// let last_layer = circuit.layers.last().unwrap(); -// let last_layer_first_output = last_layer.output_vals[0]; -// assert_eq!(last_layer_first_output, M31x16::zero()); - -// let mut prover = Prover::new(&config); -// prover.prepare_mem(&circuit); -// let (claimed_v, proof) = prover.prove(&mut circuit); -// println!("Proof generated. Size: {} bytes", proof.bytes.len()); -// // write proof to file -// fs::write(FILENAME_PROOF, &proof.bytes).expect("Unable to write proof to file."); - -// let verifier = Verifier::new(&config); -// println!("Verifier created."); -// assert!(verifier.verify(&mut circuit, &claimed_v, &proof)); -// println!("Correct proof verified."); -// let mut bad_proof = proof.clone(); -// let rng = &mut rand::thread_rng(); -// let random_idx = rng.gen_range(0..bad_proof.bytes.len()); -// let random_change = rng.gen_range(1..256) as u8; -// bad_proof.bytes[random_idx] ^= random_change; -// assert!(!verifier.verify(&mut circuit, &claimed_v, &bad_proof)); -// println!("Bad proof rejected."); -// } - -// #[test] -// fn test_compiler_format_integration_no_prove() { -// println!("Config created."); -// let mut circuit = Circuit::<M31ExtConfigSha2>::load_circuit(KECCAK_M31_CIRCUIT); -// println!("Circuit loaded."); -// circuit.load_witness_file(KECCAK_WITNESS); -// println!("Witness loaded."); -// circuit.evaluate(); -// println!("Circuit evaluated."); -// // check last layer first output -// let last_layer = circuit.layers.last().unwrap(); -// let last_layer_first_output = last_layer.output_vals[0]; -// assert_eq!(last_layer_first_output, M31x16::zero()); -// } diff --git a/gkr/src/tests/gkr_correctness.rs b/gkr/src/tests/gkr_correctness.rs index 37433944..5444b6d3 100644 --- a/gkr/src/tests/gkr_correctness.rs +++ b/gkr/src/tests/gkr_correctness.rs @@ -79,6 +79,12 @@ fn test_gkr_correctness() { FiatShamirHashType::Poseidon, PolynomialCommitmentType::Raw, ); + declare_gkr_config!( + C9, + FieldType::M31, + FiatShamirHashType::Poseidon, + PolynomialCommitmentType::Orion, + ); test_gkr_correctness_helper( &Config::<C0>::new(GKRScheme::Vanilla, mpi_config.clone()), @@ -116,6 +122,10 @@ fn test_gkr_correctness() { &Config::<C8>::new(GKRScheme::Vanilla, mpi_config.clone()), None, ); + test_gkr_correctness_helper( + &Config::<C9>::new(GKRScheme::Vanilla, mpi_config.clone()), + None, + ); MPIConfig::finalize(); } diff --git a/gkr/src/verifier.rs b/gkr/src/verifier.rs index 359f1cb1..20238227 100644 --- a/gkr/src/verifier.rs +++ b/gkr/src/verifier.rs @@ -11,7 +11,7 @@ use gkr_field_config::GKRFieldConfig; use mpi_config::MPIConfig; use poly_commit::{ExpanderGKRChallenge, PCSForExpanderGKR, StructuredReferenceString}; use sumcheck::{GKRVerifierHelper, VerifierScratchPad}; -use transcript::{Proof, Transcript}; +use transcript::{transcript_verifier_sync, Proof, Transcript}; #[cfg(feature = "grinding")] use crate::grind; @@ -285,10 +285,7 @@ impl<Cfg: GKRConfig> Verifier<Cfg> { // and use the following line to avoid unnecessary deserialization and serialization // transcript.append_u8_slice(&proof.bytes[..commitment.size()]); - if self.config.mpi_config.world_size() > 1 { - let state = transcript.hash_and_return_state(); // Sync up the Fiat-Shamir randomness - transcript.set_state(&state); - } + transcript_verifier_sync(&mut transcript, &self.config.mpi_config); // ZZ: shall we use probabilistic grinding so the verifier can avoid this cost? // (and also be recursion friendly) @@ -308,10 +305,7 @@ impl<Cfg: GKRConfig> Verifier<Cfg> { log::info!("GKR verification: {}", verified); - if self.config.mpi_config.world_size() > 1 { - let state = transcript.hash_and_return_state(); // Sync up the Fiat-Shamir randomness - transcript.set_state(&state); - } + transcript_verifier_sync(&mut transcript, &self.config.mpi_config); verified &= self.get_pcs_opening_from_proof_and_verify( pcs_params, @@ -328,6 +322,7 @@ impl<Cfg: GKRConfig> Verifier<Cfg> { ); if let Some(rz1) = rz1 { + transcript_verifier_sync(&mut transcript, &self.config.mpi_config); verified &= self.get_pcs_opening_from_proof_and_verify( pcs_params, pcs_verification_key, diff --git a/poly_commit/src/orion/simd_field_agg_impl.rs b/poly_commit/src/orion/simd_field_agg_impl.rs index 3625bb18..d1935510 100644 --- a/poly_commit/src/orion/simd_field_agg_impl.rs +++ b/poly_commit/src/orion/simd_field_agg_impl.rs @@ -83,13 +83,15 @@ where return false; } - if izip!(proof.query_openings.chunks(query_num), &roots) - .any(|(range_openings, root)| !orion_mt_verify(vk, &query_indices, range_openings, root)) - { + if !orion_mt_verify( + vk, + &query_indices, + &proof.query_openings[..query_num], + &roots[0], + ) { return false; } - // NOTE: prepare the interleaved alphabets from the MT paths let mut packed_interleaved_alphabets: Vec<Vec<C::SimdCircuitField>> = vec![Vec::new(); query_num]; diff --git a/transcript/src/lib.rs b/transcript/src/lib.rs index dcbdfaa9..25551d3e 100644 --- a/transcript/src/lib.rs +++ b/transcript/src/lib.rs @@ -5,7 +5,7 @@ mod transcript; pub use transcript::{BytesHashTranscript, FieldHashTranscript, Transcript}; mod transcript_utils; -pub use transcript_utils::transcript_root_broadcast; +pub use transcript_utils::{transcript_root_broadcast, transcript_verifier_sync}; mod proof; pub use proof::Proof; diff --git a/transcript/src/transcript_utils.rs b/transcript/src/transcript_utils.rs index bee14e21..7f713f6e 100644 --- a/transcript/src/transcript_utils.rs +++ b/transcript/src/transcript_utils.rs @@ -14,3 +14,16 @@ where transcript.set_state(&state); } } + +/// sync verifier transcript state. incurs an additional hash if self.world_size > 1 +/// corresponding part to the transcript_root_broadcast on verifier side +pub fn transcript_verifier_sync<F, T>(transcript: &mut T, mpi_config: &MPIConfig) +where + F: Field, + T: Transcript<F>, +{ + if mpi_config.world_size() > 1 { + let state = transcript.hash_and_return_state(); // Sync up the Fiat-Shamir randomness + transcript.set_state(&state); + } +} From 075abe8a98ffde0756e6effc36351f0fd02c2789 Mon Sep 17 00:00:00 2001 From: Hang Su <tonyfloater@gmail.com> Date: Sun, 12 Jan 2025 05:07:50 -0500 Subject: [PATCH 58/65] benchmarking m31 case --- poly_commit/benches/orion.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/poly_commit/benches/orion.rs b/poly_commit/benches/orion.rs index 5c5833e9..12395ef5 100644 --- a/poly_commit/benches/orion.rs +++ b/poly_commit/benches/orion.rs @@ -50,7 +50,7 @@ fn base_field_committing_benchmark_helper<F, ComPackF>( fn orion_base_field_committing_benchmark(c: &mut Criterion) { base_field_committing_benchmark_helper::<GF2, GF2x128>(c, 19, 30); - base_field_committing_benchmark_helper::<M31, M31x16>(c, 19, 26); + base_field_committing_benchmark_helper::<M31, M31x16>(c, 19, 27); } fn simd_field_committing_benchmark_helper<F, SimdF, ComPackF>( @@ -100,6 +100,7 @@ fn simd_field_committing_benchmark_helper<F, SimdF, ComPackF>( fn orion_simd_field_committing_benchmark(c: &mut Criterion) { simd_field_committing_benchmark_helper::<GF2, GF2x8, GF2x128>(c, 19, 30); + simd_field_committing_benchmark_helper::<M31, M31x16, M31x16>(c, 19, 27); } fn base_field_opening_benchmark_helper<F, EvalF, ComPackF, OpenPackF, T>( @@ -236,6 +237,13 @@ fn orion_simd_field_opening_benchmark(c: &mut Criterion) { GF2x128, BytesHashTranscript<_, Keccak256hasher>, >(c, 19, 30); + simd_field_opening_benchmark_helper::< + M31, + M31x16, + M31Ext3, + M31x16, + BytesHashTranscript<_, Keccak256hasher>, + >(c, 19, 27); } criterion_group!( From 7fe83cacfe772e14e028579e7d9653d114febfb9 Mon Sep 17 00:00:00 2001 From: Hang Su <tonyfloater@gmail.com> Date: Mon, 13 Jan 2025 16:07:56 -0500 Subject: [PATCH 59/65] push to 2^32 GF2 benchmark size --- poly_commit/benches/orion.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/poly_commit/benches/orion.rs b/poly_commit/benches/orion.rs index 12395ef5..2582532a 100644 --- a/poly_commit/benches/orion.rs +++ b/poly_commit/benches/orion.rs @@ -99,7 +99,7 @@ fn simd_field_committing_benchmark_helper<F, SimdF, ComPackF>( } fn orion_simd_field_committing_benchmark(c: &mut Criterion) { - simd_field_committing_benchmark_helper::<GF2, GF2x8, GF2x128>(c, 19, 30); + simd_field_committing_benchmark_helper::<GF2, GF2x8, GF2x128>(c, 19, 32); simd_field_committing_benchmark_helper::<M31, M31x16, M31x16>(c, 19, 27); } @@ -236,7 +236,7 @@ fn orion_simd_field_opening_benchmark(c: &mut Criterion) { GF2_128, GF2x128, BytesHashTranscript<_, Keccak256hasher>, - >(c, 19, 30); + >(c, 19, 32); simd_field_opening_benchmark_helper::< M31, M31x16, From 0c359358b41d4eaf705e614ccdd95f05bde4837b Mon Sep 17 00:00:00 2001 From: Zhiyong Fang <zhiyong.fang.1997@gmail.com> Date: Thu, 16 Jan 2025 20:52:59 -0600 Subject: [PATCH 60/65] Add some notes to 'transcript_verifier_sync' and make clippy happy Signed-off-by: Zhiyong Fang <zhiyong.fang.1997@gmail.com> --- transcript/src/transcript_utils.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/transcript/src/transcript_utils.rs b/transcript/src/transcript_utils.rs index 7f713f6e..4a9a2e61 100644 --- a/transcript/src/transcript_utils.rs +++ b/transcript/src/transcript_utils.rs @@ -15,8 +15,11 @@ where } } -/// sync verifier transcript state. incurs an additional hash if self.world_size > 1 -/// corresponding part to the transcript_root_broadcast on verifier side +/// Correspondence to 'transcript_root_broadcast' from the verifier side. +/// +/// Note: Currently, the verifier is assumed to run on a single core with no mpi sync, +/// the word 'sync' here refers to the verifier syncing up with the prover's transcript state, +/// which is updated by 'transcript_root_broadcast' if mpi_size > 1. pub fn transcript_verifier_sync<F, T>(transcript: &mut T, mpi_config: &MPIConfig) where F: Field, From fdc0f01550148037d26066c0b2e552e72ba73120 Mon Sep 17 00:00:00 2001 From: Hang Su <tonyfloater@gmail.com> Date: Fri, 17 Jan 2025 01:12:26 -0500 Subject: [PATCH 61/65] trying to isolate potential problems --- poly_commit/src/orion.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/poly_commit/src/orion.rs b/poly_commit/src/orion.rs index 375b0f30..73675c19 100644 --- a/poly_commit/src/orion.rs +++ b/poly_commit/src/orion.rs @@ -28,8 +28,8 @@ mod simd_field_tests; mod simd_field_agg_impl; -#[cfg(test)] -mod simd_field_agg_tests; +// #[cfg(test)] +// mod simd_field_agg_tests; mod pcs_for_expander_gkr; pub use pcs_for_expander_gkr::OrionPCSForGKR; From 22abffa43d5f8d438aca4230756903f1667add2f Mon Sep 17 00:00:00 2001 From: Hang Su <tonyfloater@gmail.com> Date: Fri, 17 Jan 2025 01:37:46 -0500 Subject: [PATCH 62/65] smaller test size may work? --- poly_commit/tests/test_orion.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/poly_commit/tests/test_orion.rs b/poly_commit/tests/test_orion.rs index a56ba6ad..41a928b2 100644 --- a/poly_commit/tests/test_orion.rs +++ b/poly_commit/tests/test_orion.rs @@ -64,7 +64,7 @@ where { let mut rng = test_rng(); - (19..=25).for_each(|num_vars| { + (19..=22).for_each(|num_vars| { let poly_num_vars = num_vars - SimdF::PACK_SIZE.ilog2() as usize; let xs: Vec<_> = (0..TEST_REPETITION) .map(|_| -> Vec<EvalF> { @@ -164,13 +164,13 @@ fn test_orion_for_expander_gkr() { GF2ExtConfig, GF2x128, BytesHashTranscript<_, Keccak256hasher>, - >(&mpi_config, 30); + >(&mpi_config, 19); test_orion_for_expander_gkr_generics::< M31ExtConfig, M31x16, BytesHashTranscript<_, Keccak256hasher>, - >(&mpi_config, 20); + >(&mpi_config, 19); MPIConfig::finalize() } From 09c605425e02bfe155beaa9491b659f702aaca67 Mon Sep 17 00:00:00 2001 From: Hang Su <tonyfloater@gmail.com> Date: Fri, 17 Jan 2025 05:20:41 -0500 Subject: [PATCH 63/65] still trying smaller test size... --- poly_commit/src/orion.rs | 4 ++-- poly_commit/src/orion/base_field_tests.rs | 4 ++-- poly_commit/src/orion/simd_field_agg_tests.rs | 4 ++-- poly_commit/src/orion/simd_field_tests.rs | 4 ++-- poly_commit/tests/test_orion.rs | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/poly_commit/src/orion.rs b/poly_commit/src/orion.rs index 73675c19..375b0f30 100644 --- a/poly_commit/src/orion.rs +++ b/poly_commit/src/orion.rs @@ -28,8 +28,8 @@ mod simd_field_tests; mod simd_field_agg_impl; -// #[cfg(test)] -// mod simd_field_agg_tests; +#[cfg(test)] +mod simd_field_agg_tests; mod pcs_for_expander_gkr; pub use pcs_for_expander_gkr::OrionPCSForGKR; diff --git a/poly_commit/src/orion/base_field_tests.rs b/poly_commit/src/orion/base_field_tests.rs index 47007497..80d453f2 100644 --- a/poly_commit/src/orion/base_field_tests.rs +++ b/poly_commit/src/orion/base_field_tests.rs @@ -60,12 +60,12 @@ where #[test] fn test_orion_commit_base_field_consistency() { - (19..=25).for_each(|num_vars| { + (16..=19).for_each(|num_vars| { test_orion_commit_base_field_consistency_generic::<GF2, GF2x64>(num_vars); test_orion_commit_base_field_consistency_generic::<GF2, GF2x128>(num_vars); }); - (16..=22).for_each(|num_vars| { + (12..=16).for_each(|num_vars| { test_orion_commit_base_field_consistency_generic::<M31, M31x16>(num_vars) }); } diff --git a/poly_commit/src/orion/simd_field_agg_tests.rs b/poly_commit/src/orion/simd_field_agg_tests.rs index 63d14f3f..9cd1c2e4 100644 --- a/poly_commit/src/orion/simd_field_agg_tests.rs +++ b/poly_commit/src/orion/simd_field_agg_tests.rs @@ -187,7 +187,7 @@ where fn test_orion_simd_aggregate_verify() { let parties = 16; - (25..30).for_each(|num_var| { + (16..18).for_each(|num_var| { test_orion_simd_aggregate_verify_helper::< GF2ExtConfig, GF2x128, @@ -195,7 +195,7 @@ fn test_orion_simd_aggregate_verify() { >(parties, num_var) }); - (18..25).for_each(|num_var| { + (12..15).for_each(|num_var| { test_orion_simd_aggregate_verify_helper::< M31ExtConfig, M31x16, diff --git a/poly_commit/src/orion/simd_field_tests.rs b/poly_commit/src/orion/simd_field_tests.rs index 978110ee..5f30fa2e 100644 --- a/poly_commit/src/orion/simd_field_tests.rs +++ b/poly_commit/src/orion/simd_field_tests.rs @@ -75,13 +75,13 @@ where #[test] fn test_orion_commit_simd_field_consistency() { - (16..=22).for_each(|num_vars| { + (16..=19).for_each(|num_vars| { test_orion_commit_simd_field_consistency_generic::<GF2, GF2x8, GF2x8>(num_vars); test_orion_commit_simd_field_consistency_generic::<GF2, GF2x8, GF2x64>(num_vars); test_orion_commit_simd_field_consistency_generic::<GF2, GF2x8, GF2x128>(num_vars); }); - (12..=18).for_each(|num_vars| { + (12..=16).for_each(|num_vars| { test_orion_commit_simd_field_consistency_generic::<M31, M31x16, M31x16>(num_vars) }) } diff --git a/poly_commit/tests/test_orion.rs b/poly_commit/tests/test_orion.rs index 41a928b2..2e92c11f 100644 --- a/poly_commit/tests/test_orion.rs +++ b/poly_commit/tests/test_orion.rs @@ -164,13 +164,13 @@ fn test_orion_for_expander_gkr() { GF2ExtConfig, GF2x128, BytesHashTranscript<_, Keccak256hasher>, - >(&mpi_config, 19); + >(&mpi_config, 16); test_orion_for_expander_gkr_generics::< M31ExtConfig, M31x16, BytesHashTranscript<_, Keccak256hasher>, - >(&mpi_config, 19); + >(&mpi_config, 15); MPIConfig::finalize() } From a2f62ae1ee3f4e6d168edb18edd0db6961fbe9a4 Mon Sep 17 00:00:00 2001 From: Hang Su <tonyfloater@gmail.com> Date: Sun, 19 Jan 2025 22:24:52 -0500 Subject: [PATCH 64/65] hamming weight naming suggestion --- poly_commit/src/orion/utils.rs | 2 +- poly_commit/src/traits.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/poly_commit/src/orion/utils.rs b/poly_commit/src/orion/utils.rs index f5ff94e3..c715199b 100644 --- a/poly_commit/src/orion/utils.rs +++ b/poly_commit/src/orion/utils.rs @@ -39,7 +39,7 @@ impl TensorCodeIOPPCS for OrionSRS { self.code_instance.code_len() } - fn hamming_weight(&self) -> f64 { + fn minimum_hamming_weight(&self) -> f64 { self.code_instance.hamming_weight() } } diff --git a/poly_commit/src/traits.rs b/poly_commit/src/traits.rs index a130d57b..c2722697 100644 --- a/poly_commit/src/traits.rs +++ b/poly_commit/src/traits.rs @@ -174,7 +174,7 @@ pub(crate) trait TensorCodeIOPPCS { fn codeword_len(&self) -> usize; - fn hamming_weight(&self) -> f64; + fn minimum_hamming_weight(&self) -> f64; fn evals_shape<F: Field>(num_vars: usize) -> (usize, usize) { let elems_for_smallest_tree = tree::leaf_adic::<F>() * Self::LEAVES_IN_RANGE_OPENING; @@ -187,7 +187,7 @@ pub(crate) trait TensorCodeIOPPCS { fn query_complexity(&self, soundness_bits: usize) -> usize { // NOTE: use Ligero (AHIV22) appendix C argument. - let avg_case_dist = self.hamming_weight() / 2f64; + let avg_case_dist = self.minimum_hamming_weight() / 2f64; let sec_bits = -(1f64 - avg_case_dist).log2(); (soundness_bits as f64 / sec_bits).ceil() as usize From 25d4461d3f179bf5d792722cd429d86f3aadb13d Mon Sep 17 00:00:00 2001 From: tonyfloatersu <tonyfloater@gmail.com> Date: Tue, 21 Jan 2025 18:23:26 -0500 Subject: [PATCH 65/65] Minor: Orion related additional test (#180) --- .github/workflows/ci.yml | 2 +- poly_commit/src/orion/utils.rs | 36 ++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7961368e..76202a55 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,7 +41,7 @@ jobs: matrix: include: - os: macos-latest - - os: ubuntu-latest + # - os: ubuntu-latest steps: - name: Checkout code diff --git a/poly_commit/src/orion/utils.rs b/poly_commit/src/orion/utils.rs index c715199b..025a4d88 100644 --- a/poly_commit/src/orion/utils.rs +++ b/poly_commit/src/orion/utils.rs @@ -472,3 +472,39 @@ where alphabet == codeword[index] }) } + +#[cfg(test)] +mod tests { + use arith::{Field, SimdField}; + use ark_std::test_rng; + use gf2::{GF2x8, GF2}; + use gf2_128::{GF2_128x8, GF2_128}; + use itertools::izip; + + use super::SubsetSumLUTs; + + #[test] + fn test_lut_simd_inner_prod_consistency() { + let mut rng = test_rng(); + + let weights: Vec<_> = (0..8).map(|_| GF2_128::random_unsafe(&mut rng)).collect(); + let bases: Vec<_> = (0..8).map(|_| GF2::random_unsafe(&mut rng)).collect(); + + let simd_weights = GF2_128x8::pack(&weights); + let simd_bases = GF2x8::pack(&bases); + + let expected_simd_inner_prod: GF2_128 = (simd_weights * simd_bases).unpack().iter().sum(); + + let expected_vanilla_inner_prod: GF2_128 = + izip!(&weights, &bases).map(|(w, b)| *w * *b).sum(); + + assert_eq!(expected_simd_inner_prod, expected_vanilla_inner_prod); + + let mut table = SubsetSumLUTs::new(8, 1); + table.build(&weights); + + let actual_lut_inner_prod = table.lookup_and_sum(&vec![simd_bases]); + + assert_eq!(expected_simd_inner_prod, actual_lut_inner_prod) + } +}