From 7eba030883a2bf34bf3bd927db9f66e4dd99b0b7 Mon Sep 17 00:00:00 2001 From: Tom Wambsgans Date: Wed, 29 Oct 2025 11:16:35 +0400 Subject: [PATCH 01/11] wip --- Cargo.lock | 4 - Cargo.toml | 4 +- .../src/gkr_layers/full_round.rs | 121 +++--------------- crates/poseidon_circuit/src/gkr_layers/mod.rs | 35 +---- crates/poseidon_circuit/src/lib.rs | 3 + crates/poseidon_circuit/src/prove.rs | 84 ++++++------ crates/poseidon_circuit/src/tests.rs | 4 +- crates/poseidon_circuit/src/utils.rs | 40 ++++++ crates/poseidon_circuit/src/verify.rs | 79 +++++------- crates/poseidon_circuit/src/witness_gen.rs | 101 +++++++-------- 10 files changed, 195 insertions(+), 280 deletions(-) create mode 100644 crates/poseidon_circuit/src/utils.rs diff --git a/Cargo.lock b/Cargo.lock index 3511ba66..3cb82865 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -95,7 +95,6 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "backend" version = "0.3.0" -source = "git+https://github.com/leanEthereum/multilinear-toolkit.git#ed923559dcb49da4c83cfd6ccd5e00ffb8119aa8" dependencies = [ "fiat-shamir", "itertools", @@ -179,7 +178,6 @@ dependencies = [ [[package]] name = "constraints-folder" version = "0.3.0" -source = "git+https://github.com/leanEthereum/multilinear-toolkit.git#ed923559dcb49da4c83cfd6ccd5e00ffb8119aa8" dependencies = [ "fiat-shamir", "p3-air", @@ -488,7 +486,6 @@ checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" [[package]] name = "multilinear-toolkit" version = "0.3.0" -source = "git+https://github.com/leanEthereum/multilinear-toolkit.git#ed923559dcb49da4c83cfd6ccd5e00ffb8119aa8" dependencies = [ "backend", "constraints-folder", @@ -1092,7 +1089,6 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "sumcheck" version = "0.3.0" -source = "git+https://github.com/leanEthereum/multilinear-toolkit.git#ed923559dcb49da4c83cfd6ccd5e00ffb8119aa8" dependencies = [ "backend", "constraints-folder", diff --git a/Cargo.toml b/Cargo.toml index eae85faa..84efe502 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -106,8 +106,8 @@ poseidon_circuit.workspace = true # [patch."https://github.com/TomWambsgans/whir-p3.git"] # whir-p3 = { path = "../zk/whir/fork-whir-p3" } -# [patch."https://github.com/leanEthereum/multilinear-toolkit.git"] -# multilinear-toolkit = { path = "../zk/multilinear-toolkit" } +[patch."https://github.com/leanEthereum/multilinear-toolkit.git"] +multilinear-toolkit = { path = "../zk/multilinear-toolkit" } [profile.release] lto = "thin" diff --git a/crates/poseidon_circuit/src/gkr_layers/full_round.rs b/crates/poseidon_circuit/src/gkr_layers/full_round.rs index fd1214b3..e75e8a2f 100644 --- a/crates/poseidon_circuit/src/gkr_layers/full_round.rs +++ b/crates/poseidon_circuit/src/gkr_layers/full_round.rs @@ -1,144 +1,55 @@ -use std::array; - use multilinear_toolkit::prelude::*; use p3_field::ExtensionField; -use p3_koala_bear::{ - GenericPoseidon2LinearLayersKoalaBear, KoalaBearInternalLayerParameters, KoalaBearParameters, -}; +use p3_koala_bear::{KoalaBearInternalLayerParameters, KoalaBearParameters}; use p3_monty_31::InternalLayerBaseParameters; -use p3_poseidon2::GenericPoseidon2LinearLayers; use crate::{EF, F}; #[derive(Debug)] -pub struct FullRoundComputation { - pub constants: [F; WIDTH], - pub compressed_output: Option, -} +pub struct FullRoundComputation {} -impl, const WIDTH: usize, const FIRST: bool> SumcheckComputation - for FullRoundComputation +impl, const WIDTH: usize> SumcheckComputation + for FullRoundComputation where KoalaBearInternalLayerParameters: InternalLayerBaseParameters, EF: ExtensionField, { fn degree(&self) -> usize { - 3 + self.compressed_output.is_some() as usize + 3 } fn eval(&self, point: &[NF], alpha_powers: &[EF]) -> EF { - debug_assert_eq!( - point.len(), - WIDTH - + if self.compressed_output.is_some() { - 1 - } else { - 0 - } - ); - let mut buff: [NF; WIDTH] = array::from_fn(|j| point[j]); - if FIRST { - GenericPoseidon2LinearLayersKoalaBear::external_linear_layer(&mut buff); - } - buff.iter_mut().enumerate().for_each(|(j, val)| { - *val = (*val + self.constants[j]).cube(); - }); - GenericPoseidon2LinearLayersKoalaBear::external_linear_layer(&mut buff); + debug_assert_eq!(point.len(), WIDTH); let mut res = EF::ZERO; - if let Some(compression_output_width) = self.compressed_output { - let compressed = point[WIDTH]; - for i in 0..compression_output_width { - res += alpha_powers[i] * buff[i]; - } - for i in compression_output_width..WIDTH { - res += alpha_powers[i] * buff[i] * (EF::ONE - compressed); - } - } else { - for i in 0..WIDTH { - res += alpha_powers[i] * buff[i]; - } + for i in 0..WIDTH { + res += alpha_powers[i] * point[i].cube(); } res } } -impl SumcheckComputationPacked - for FullRoundComputation +impl SumcheckComputationPacked for FullRoundComputation where KoalaBearInternalLayerParameters: InternalLayerBaseParameters, { fn degree(&self) -> usize { - 3 + self.compressed_output.is_some() as usize + 3 } fn eval_packed_base(&self, point: &[PFPacking], alpha_powers: &[EF]) -> EFPacking { - debug_assert_eq!( - point.len(), - WIDTH - + if self.compressed_output.is_some() { - 1 - } else { - 0 - } - ); - let mut buff: [PFPacking; WIDTH] = array::from_fn(|j| point[j]); - if FIRST { - GenericPoseidon2LinearLayersKoalaBear::external_linear_layer(&mut buff); - } - buff.iter_mut().enumerate().for_each(|(j, val)| { - *val = (*val + self.constants[j]).cube(); - }); - GenericPoseidon2LinearLayersKoalaBear::external_linear_layer(&mut buff); + debug_assert_eq!(point.len(), WIDTH); let mut res = EFPacking::::ZERO; - if let Some(compression_output_width) = self.compressed_output { - let compressed = point[WIDTH]; - for i in 0..compression_output_width { - res += EFPacking::::from(alpha_powers[i]) * buff[i]; - } - for i in compression_output_width..WIDTH { - res += EFPacking::::from(alpha_powers[i]) - * buff[i] - * (PFPacking::::ONE - compressed); - } - } else { - for j in 0..WIDTH { - res += EFPacking::::from(alpha_powers[j]) * buff[j]; - } + for i in 0..WIDTH { + res += EFPacking::::from(alpha_powers[i]) * point[i].cube(); } res } fn eval_packed_extension(&self, point: &[EFPacking], alpha_powers: &[EF]) -> EFPacking { - debug_assert_eq!( - point.len(), - WIDTH - + if self.compressed_output.is_some() { - 1 - } else { - 0 - } - ); - let mut buff: [EFPacking; WIDTH] = array::from_fn(|j| point[j]); - if FIRST { - GenericPoseidon2LinearLayersKoalaBear::external_linear_layer(&mut buff); - } - buff.iter_mut().enumerate().for_each(|(j, val)| { - *val = (*val + PFPacking::::from(self.constants[j])).cube(); - }); - GenericPoseidon2LinearLayersKoalaBear::external_linear_layer(&mut buff); + debug_assert_eq!(point.len(), WIDTH); let mut res = EFPacking::::ZERO; - if let Some(compression_output_width) = self.compressed_output { - let compressed = point[WIDTH]; - for i in 0..compression_output_width { - res += buff[i] * alpha_powers[i]; - } - for i in compression_output_width..WIDTH { - res += buff[i] * (EFPacking::::ONE - compressed) * alpha_powers[i]; - } - } else { - for j in 0..WIDTH { - res += buff[j] * alpha_powers[j]; - } + for i in 0..WIDTH { + res += point[i].cube() * alpha_powers[i]; } res } diff --git a/crates/poseidon_circuit/src/gkr_layers/mod.rs b/crates/poseidon_circuit/src/gkr_layers/mod.rs index a3e453b6..ad9661e0 100644 --- a/crates/poseidon_circuit/src/gkr_layers/mod.rs +++ b/crates/poseidon_circuit/src/gkr_layers/mod.rs @@ -16,11 +16,11 @@ use crate::F; #[derive(Debug)] pub struct PoseidonGKRLayers { - pub initial_full_round: FullRoundComputation, - pub initial_full_rounds_remaining: Vec>, + pub initial_full_rounds: Vec<[F; WIDTH]>, pub batch_partial_rounds: BatchPartialRounds, pub partial_rounds_remaining: Vec>, - pub final_full_rounds: Vec>, + pub final_full_rounds: Vec<[F; WIDTH]>, + pub compressed_output: Option, } impl PoseidonGKRLayers { @@ -54,17 +54,7 @@ impl PoseidonGKRLayers, ) -> Self { - let initial_full_round = FullRoundComputation { - constants: initial_constants[0], - compressed_output: None, - }; - let initial_full_rounds_remaining = initial_constants[1..] - .iter() - .map(|&constants| FullRoundComputation { - constants, - compressed_output: None, - }) - .collect::>(); + let initial_full_rounds = initial_constants.to_vec(); let batch_partial_rounds = BatchPartialRounds { constants: internal_constants[..N_COMMITED_CUBES].try_into().unwrap(), last_constant: internal_constants[N_COMMITED_CUBES], @@ -73,24 +63,13 @@ impl PoseidonGKRLayers>(); - let final_full_rounds = final_constants - .iter() - .enumerate() - .map(|(i, &constants)| FullRoundComputation { - constants, - compressed_output: if i == final_constants.len() - 1 { - compressed_output - } else { - None - }, - }) - .collect::>(); + let final_full_rounds = final_constants.to_vec(); Self { - initial_full_round, - initial_full_rounds_remaining, + initial_full_rounds, batch_partial_rounds, partial_rounds_remaining, final_full_rounds, + compressed_output, } } } diff --git a/crates/poseidon_circuit/src/lib.rs b/crates/poseidon_circuit/src/lib.rs index 91eff4a2..1a864f97 100644 --- a/crates/poseidon_circuit/src/lib.rs +++ b/crates/poseidon_circuit/src/lib.rs @@ -9,6 +9,9 @@ pub use prove::*; mod verify; pub use verify::*; +mod utils; +pub use utils::*; + mod witness_gen; pub use witness_gen::*; diff --git a/crates/poseidon_circuit/src/prove.rs b/crates/poseidon_circuit/src/prove.rs index b17cbbdd..0200fdb8 100644 --- a/crates/poseidon_circuit/src/prove.rs +++ b/crates/poseidon_circuit/src/prove.rs @@ -1,8 +1,8 @@ -use crate::GKRPoseidonResult; use crate::{ - EF, F, PoseidonWitness, + EF, F, FullRoundComputation, PoseidonWitness, gkr_layers::{BatchPartialRounds, PoseidonGKRLayers}, }; +use crate::{GKRPoseidonResult, build_poseidon_matrices}; use multilinear_toolkit::prelude::*; use p3_koala_bear::{KoalaBearInternalLayerParameters, KoalaBearParameters}; use p3_monty_31::InternalLayerBaseParameters; @@ -12,7 +12,7 @@ use tracing::{info_span, instrument}; pub fn prove_poseidon_gkr( prover_state: &mut FSProver>, witness: &PoseidonWitness, WIDTH, N_COMMITED_CUBES>, - mut claim_point: Vec, + mut point: Vec, univariate_skips: usize, layers: &PoseidonGKRLayers, ) -> GKRPoseidonResult @@ -20,11 +20,13 @@ where KoalaBearInternalLayerParameters: InternalLayerBaseParameters, { let selectors = univariate_selectors::(univariate_skips); + let (_mds_matrix, inv_mds_matrix, _light_matrix, _inv_light_matrix) = + build_poseidon_matrices::(); - assert_eq!(claim_point.len(), log2_strict_usize(witness.n_poseidons())); + assert_eq!(point.len(), log2_strict_usize(witness.n_poseidons())); let (output_claims, mut claims) = info_span!("computing output claims").in_scope(|| { - let eq_poly = eval_eq(&claim_point[univariate_skips..]); + let eq_poly = eval_eq(&point[univariate_skips..]); let inner_evals = witness .output_layer .par_iter() @@ -48,40 +50,35 @@ where let mut claims = vec![]; for evals in inner_evals { output_claims - .push(evals.evaluate(&MultilinearPoint(claim_point[..univariate_skips].to_vec()))); + .push(evals.evaluate(&MultilinearPoint(point[..univariate_skips].to_vec()))); claims.push(dot_product( selectors_at_alpha.iter().copied(), evals.into_iter(), )) } - claim_point = [vec![alpha], claim_point[univariate_skips..].to_vec()].concat(); + point = [vec![alpha], point[univariate_skips..].to_vec()].concat(); (output_claims, claims) }); - for (i, (input_layers, full_round)) in witness - .final_full_round_inputs + for (layer, full_round_constants) in witness + .final_full_layers .iter() .zip(&layers.final_full_rounds) .rev() - .enumerate() { - let mut input_layers = input_layers.iter().map(Vec::as_slice).collect::>(); - if i == 0 - && let Some((_, compression_indicator)) = &witness.compression - { - input_layers.push(compression_indicator); - } - (claim_point, claims) = prove_gkr_round( + claims = apply_matrix(&inv_mds_matrix, &claims); + + (point, claims) = prove_gkr_round( prover_state, - full_round, - &input_layers, - &claim_point, + &FullRoundComputation {}, + &layer.iter().map(Vec::as_slice).collect::>(), + &point, &claims, univariate_skips, ); - if i == 0 && witness.compression.is_some() { - let _ = claims.pop().unwrap(); // the claim on the compression indicator columns can be evaluated by the verifier directly + for (claim, c) in claims.iter_mut().zip(full_round_constants) { + *claim -= *c; } } @@ -91,54 +88,55 @@ where .zip(&layers.partial_rounds_remaining) .rev() { - (claim_point, claims) = prove_gkr_round( + (point, claims) = prove_gkr_round( prover_state, partial_round, input_layers, - &claim_point, + &point, &claims, univariate_skips, ); } - (claim_point, claims) = prove_batch_internal_round( + (point, claims) = prove_batch_internal_round( prover_state, &witness.batch_partial_round_input, &witness.committed_cubes, &layers.batch_partial_rounds, - &claim_point, + &point, &claims, &selectors, ); - let pcs_point_for_cubes = claim_point.clone(); + let pcs_point_for_cubes = point.clone(); claims = claims[..WIDTH].to_vec(); - for (input_layers, full_round) in witness - .remaining_initial_full_round_inputs + for (layer, full_round_constants) in witness + .initial_full_layers .iter() - .zip(&layers.initial_full_rounds_remaining) + .zip(&layers.initial_full_rounds) .rev() { - (claim_point, claims) = prove_gkr_round( + claims = apply_matrix(&inv_mds_matrix, &claims); + + (point, claims) = prove_gkr_round( prover_state, - full_round, - input_layers, - &claim_point, + &FullRoundComputation {}, + &layer.iter().map(Vec::as_slice).collect::>(), + &point, &claims, univariate_skips, ); + + for (claim, c) in claims.iter_mut().zip(full_round_constants) { + *claim -= *c; + } } - (claim_point, _) = prove_gkr_round( - prover_state, - &layers.initial_full_round, - &witness.input_layer, - &claim_point, - &claims, - univariate_skips, - ); - let pcs_point_for_inputs = claim_point.clone(); + + claims = apply_matrix(&inv_mds_matrix, &claims); + + let pcs_point_for_inputs = point.clone(); let input_statements = inner_evals_on_commited_columns( prover_state, diff --git a/crates/poseidon_circuit/src/tests.rs b/crates/poseidon_circuit/src/tests.rs index 8f9ddc42..ad27f750 100644 --- a/crates/poseidon_circuit/src/tests.rs +++ b/crates/poseidon_circuit/src/tests.rs @@ -30,8 +30,8 @@ const COMPRESSION_OUTPUT_WIDTH: usize = 8; #[test] fn test_poseidon_benchmark() { - run_poseidon_benchmark(15, true); - run_poseidon_benchmark(15, false); + run_poseidon_benchmark(12, false); + // run_poseidon_benchmark(12, true); } pub fn run_poseidon_benchmark(log_n_poseidons: usize, compress: bool) { diff --git a/crates/poseidon_circuit/src/utils.rs b/crates/poseidon_circuit/src/utils.rs new file mode 100644 index 00000000..ac61331b --- /dev/null +++ b/crates/poseidon_circuit/src/utils.rs @@ -0,0 +1,40 @@ +use std::array; + +use multilinear_toolkit::prelude::*; +use p3_koala_bear::{ + GenericPoseidon2LinearLayersKoalaBear, KoalaBearInternalLayerParameters, KoalaBearParameters, +}; +use p3_monty_31::InternalLayerBaseParameters; +use p3_poseidon2::GenericPoseidon2LinearLayers; +use tracing::instrument; + +use crate::F; + +#[instrument(skip_all)] +pub fn build_poseidon_matrices() -> ( + [[F; WIDTH]; WIDTH], + [[F; WIDTH]; WIDTH], + [[F; WIDTH]; WIDTH], + [[F; WIDTH]; WIDTH], +) +where + KoalaBearInternalLayerParameters: InternalLayerBaseParameters, +{ + let mut mds_matrix: [[F; WIDTH]; WIDTH] = array::from_fn(|_| array::from_fn(|_| F::ZERO)); + for i in 0..WIDTH { + mds_matrix[i][i] = F::ONE; + GenericPoseidon2LinearLayersKoalaBear::external_linear_layer(&mut mds_matrix[i]); + } + mds_matrix = transpose_matrix(&mds_matrix); + let inv_mds_matrix = inverse_matrix(&mds_matrix); + + let mut light_matrix: [[F; WIDTH]; WIDTH] = array::from_fn(|_| array::from_fn(|_| F::ZERO)); + for i in 0..WIDTH { + light_matrix[i][i] = F::ONE; + GenericPoseidon2LinearLayersKoalaBear::internal_linear_layer(&mut light_matrix[i]); + } + light_matrix = transpose_matrix(&light_matrix); + let inv_light_matrix = inverse_matrix(&light_matrix); + + (mds_matrix, inv_mds_matrix, light_matrix, inv_light_matrix) +} diff --git a/crates/poseidon_circuit/src/verify.rs b/crates/poseidon_circuit/src/verify.rs index f21a45a3..06b67d7e 100644 --- a/crates/poseidon_circuit/src/verify.rs +++ b/crates/poseidon_circuit/src/verify.rs @@ -2,7 +2,10 @@ use multilinear_toolkit::prelude::*; use p3_koala_bear::{KoalaBearInternalLayerParameters, KoalaBearParameters}; use p3_monty_31::InternalLayerBaseParameters; -use crate::{EF, F, GKRPoseidonResult, gkr_layers::PoseidonGKRLayers}; +use crate::{ + EF, F, FullRoundComputation, GKRPoseidonResult, build_poseidon_matrices, + gkr_layers::PoseidonGKRLayers, +}; pub fn verify_poseidon_gkr( verifier_state: &mut FSVerifier>, @@ -16,11 +19,13 @@ where KoalaBearInternalLayerParameters: InternalLayerBaseParameters, { let selectors = univariate_selectors::(univariate_skips); + let (_mds_matrix, inv_mds_matrix, _light_matrix, _inv_light_matrix) = + build_poseidon_matrices::(); let mut output_claims = vec![]; let mut claims = vec![]; - let mut claim_point = { + let mut point = { let inner_evals = (0..WIDTH) .map(|_| { verifier_state @@ -45,43 +50,30 @@ where [vec![alpha], output_claim_point[univariate_skips..].to_vec()].concat() }; - for (i, full_round) in layers.final_full_rounds.iter().rev().enumerate() { - let n_inputs = if i == 0 && n_compressions.is_some() { - WIDTH + 1 - } else { - WIDTH - }; - (claim_point, claims) = verify_gkr_round( + for full_round_constants in layers.final_full_rounds.iter().rev() { + claims = apply_matrix(&inv_mds_matrix, &claims); + + (point, claims) = verify_gkr_round( verifier_state, - full_round, + &FullRoundComputation {}, log_n_poseidons, - &claim_point, + &point, &claims, univariate_skips, - n_inputs, + WIDTH, ); - if i == 0 - && let Some(n_compressions) = n_compressions - { - assert!(n_compressions <= 1 << log_n_poseidons); - let compression_eval = claims.pop().unwrap(); - assert_eq!( - compression_eval, - skipped_mle_of_zeros_then_ones( - (1 << log_n_poseidons) - n_compressions, - &claim_point, - &selectors - ) - ); + + for (claim, c) in claims.iter_mut().zip(full_round_constants) { + *claim -= *c; } } for partial_round in layers.partial_rounds_remaining.iter().rev() { - (claim_point, claims) = verify_gkr_round( + (point, claims) = verify_gkr_round( verifier_state, partial_round, log_n_poseidons, - &claim_point, + &point, &claims, univariate_skips, WIDTH, @@ -91,43 +83,42 @@ where .next_extension_scalars_vec(N_COMMITED_CUBES) .unwrap(); - (claim_point, claims) = verify_gkr_round( + (point, claims) = verify_gkr_round( verifier_state, &layers.batch_partial_rounds, log_n_poseidons, - &claim_point, + &point, &[claims, claimed_cubes_evals.clone()].concat(), univariate_skips, WIDTH + N_COMMITED_CUBES, ); - let pcs_point_for_cubes = claim_point.clone(); + let pcs_point_for_cubes = point.clone(); let pcs_evals_for_cubes = claims[WIDTH..].to_vec(); claims = claims[..WIDTH].to_vec(); - for full_round in layers.initial_full_rounds_remaining.iter().rev() { - (claim_point, claims) = verify_gkr_round( + for full_round_constants in layers.initial_full_rounds.iter().rev() { + claims = apply_matrix(&inv_mds_matrix, &claims); + + (point, claims) = verify_gkr_round( verifier_state, - full_round, + &FullRoundComputation {}, log_n_poseidons, - &claim_point, + &point, &claims, univariate_skips, WIDTH, ); + + for (claim, c) in claims.iter_mut().zip(full_round_constants) { + *claim -= *c; + } } - (claim_point, claims) = verify_gkr_round( - verifier_state, - &layers.initial_full_round, - log_n_poseidons, - &claim_point, - &claims, - univariate_skips, - WIDTH, - ); - let pcs_point_for_inputs = claim_point.clone(); + claims = apply_matrix(&inv_mds_matrix, &claims); + + let pcs_point_for_inputs = point.clone(); let pcs_evals_for_inputs = claims; let input_statements = verify_inner_evals_on_commited_columns( diff --git a/crates/poseidon_circuit/src/witness_gen.rs b/crates/poseidon_circuit/src/witness_gen.rs index fa090e53..cfcb0d70 100644 --- a/crates/poseidon_circuit/src/witness_gen.rs +++ b/crates/poseidon_circuit/src/witness_gen.rs @@ -8,19 +8,19 @@ use p3_monty_31::InternalLayerBaseParameters; use p3_poseidon2::GenericPoseidon2LinearLayers; use utils::transposed_par_iter_mut; +use crate::F; use crate::gkr_layers::BatchPartialRounds; use crate::gkr_layers::PartialRoundComputation; use crate::gkr_layers::PoseidonGKRLayers; -use crate::{F, gkr_layers::FullRoundComputation}; #[derive(Debug)] pub struct PoseidonWitness { pub input_layer: [Vec; WIDTH], // input of the permutation - pub remaining_initial_full_round_inputs: Vec<[Vec; WIDTH]>, // the remaining input of each initial full round + pub initial_full_layers: Vec<[Vec; WIDTH]>, // just before cubing pub batch_partial_round_input: [Vec; WIDTH], // again, the input of the batch (partial) round pub committed_cubes: [Vec; N_COMMITED_CUBES], // the cubes commited in the batch (partial) rounds pub remaining_partial_round_inputs: Vec<[Vec; WIDTH]>, // the input of each remaining partial round - pub final_full_round_inputs: Vec<[Vec; WIDTH]>, // the input of each final full round + pub final_full_layers: Vec<[Vec; WIDTH]>, // just before cubing pub output_layer: [Vec; WIDTH], // output of the permutation pub compression: Option<(usize, Vec)>, // num compressions, compression indicator column } @@ -42,20 +42,21 @@ where A: Algebra + Copy + Send + Sync, KoalaBearInternalLayerParameters: InternalLayerBaseParameters, { - let mut remaining_initial_full_layers = vec![apply_full_round::<_, _, true>( + let mut initial_full_layers = vec![apply_full_round::<_, _, false, true, true>( &input, - &layers.initial_full_round, - None, + &layers.initial_full_rounds[0], )]; - for round in &layers.initial_full_rounds_remaining { - remaining_initial_full_layers.push(apply_full_round::<_, _, false>( - remaining_initial_full_layers.last().unwrap(), - round, - None, + for constants in &layers.initial_full_rounds[1..] { + initial_full_layers.push(apply_full_round::<_, _, true, true, true>( + initial_full_layers.last().unwrap(), + constants, )); } - let batch_partial_round_layer = remaining_initial_full_layers.pop().unwrap(); + let batch_partial_round_layer = apply_full_round::<_, _, true, true, false>( + initial_full_layers.last().unwrap(), + &[F::ZERO; WIDTH], // unused + ); let (next_layer, committed_cubes) = apply_batch_partial_rounds(&batch_partial_round_layer, &layers.batch_partial_rounds); @@ -67,38 +68,44 @@ where )); } - let mut final_full_layer_inputs = vec![remaining_partial_inputs.pop().unwrap()]; - for (i, round) in layers.final_full_rounds.iter().enumerate() { - final_full_layer_inputs.push(apply_full_round::<_, _, false>( - final_full_layer_inputs.last().unwrap(), - round, - if i == layers.final_full_rounds.len() - 1 { - compression.as_ref().map(|(_, v)| v.as_slice()) - } else { - None - }, + let mut final_full_layers = vec![apply_full_round::<_, _, false, false, true>( + &remaining_partial_inputs.pop().unwrap(), // we remove it + &layers.final_full_rounds[0], + )]; + for constants in &layers.final_full_rounds[1..] { + final_full_layers.push(apply_full_round::<_, _, true, true, true>( + final_full_layers.last().unwrap(), + constants, )); } - let output_layer = final_full_layer_inputs.pop().unwrap(); + let output_layer = apply_full_round::<_, _, true, true, false>( + final_full_layers.last().unwrap(), + &[F::ZERO; WIDTH], // unused + ); PoseidonWitness { input_layer: input, - remaining_initial_full_round_inputs: remaining_initial_full_layers, + initial_full_layers, batch_partial_round_input: batch_partial_round_layer, committed_cubes, remaining_partial_round_inputs: remaining_partial_inputs, - final_full_round_inputs: final_full_layer_inputs, + final_full_layers, output_layer, compression, } } // #[instrument(skip_all)] -fn apply_full_round( +fn apply_full_round< + A, + const WIDTH: usize, + const CUBE: bool, + const MDS: bool, + const ADD_CONSTANTS: bool, +>( input_layers: &[Vec; WIDTH], - full_round: &FullRoundComputation, - compression_indicator: Option<&[A]>, + constants: &[F; WIDTH], ) -> [Vec; WIDTH] where A: Algebra + Copy + Send + Sync, @@ -109,26 +116,22 @@ where .enumerate() .for_each(|(row_index, output_row)| { let mut buff: [A; WIDTH] = array::from_fn(|j| input_layers[j][row_index]); - if FIRST { - GenericPoseidon2LinearLayersKoalaBear::external_linear_layer(&mut buff); - } - buff.iter_mut().enumerate().for_each(|(j, val)| { - *val = (*val + full_round.constants[j]).cube(); - }); - GenericPoseidon2LinearLayersKoalaBear::external_linear_layer(&mut buff); - if let Some(compression_output_width) = full_round.compressed_output { - let compressed = compression_indicator.unwrap()[row_index]; - for i in 0..compression_output_width { - *output_row[i] = buff[i]; - } - for i in compression_output_width..WIDTH { - *output_row[i] = buff[i] * (A::ONE - compressed); - } - } else { + if CUBE { for j in 0..WIDTH { - *output_row[j] = buff[j]; + buff[j] = buff[j].cube(); } } + if MDS { + GenericPoseidon2LinearLayersKoalaBear::external_linear_layer(&mut buff); + } + if ADD_CONSTANTS { + buff.iter_mut().enumerate().for_each(|(j, val)| { + *val += constants[j]; + }); + } + for j in 0..WIDTH { + *output_row[j] = buff[j]; + } }); output_layers } @@ -200,13 +203,7 @@ where generate_poseidon_witness::( array::from_fn(|_| vec![A::ZERO]), layers, - if layers - .final_full_rounds - .last() - .unwrap() - .compressed_output - .is_some() - { + if layers.compressed_output.is_some() { Some((0, vec![A::ZERO])) } else { None From e10ed6f16b5c5ace751e08c90b6655695b8c01e3 Mon Sep 17 00:00:00 2001 From: Tom Wambsgans Date: Wed, 29 Oct 2025 12:02:08 +0400 Subject: [PATCH 02/11] full rounds OK --- .../src/gkr_layers/compression.rs | 71 +++++++++++++++++++ crates/poseidon_circuit/src/gkr_layers/mod.rs | 3 + crates/poseidon_circuit/src/prove.rs | 45 +++++++++--- crates/poseidon_circuit/src/tests.rs | 7 +- crates/poseidon_circuit/src/verify.rs | 29 +++++++- crates/poseidon_circuit/src/witness_gen.rs | 16 ++++- 6 files changed, 155 insertions(+), 16 deletions(-) create mode 100644 crates/poseidon_circuit/src/gkr_layers/compression.rs diff --git a/crates/poseidon_circuit/src/gkr_layers/compression.rs b/crates/poseidon_circuit/src/gkr_layers/compression.rs new file mode 100644 index 00000000..eeaf8cc3 --- /dev/null +++ b/crates/poseidon_circuit/src/gkr_layers/compression.rs @@ -0,0 +1,71 @@ + +use multilinear_toolkit::prelude::*; +use p3_field::ExtensionField; + +use crate::{EF, F}; + +#[derive(Debug)] +pub struct CompressionComputation { + pub compressed_output: usize, +} + +impl, const WIDTH: usize> SumcheckComputation + for CompressionComputation +where + EF: ExtensionField, +{ + fn degree(&self) -> usize { + 2 + } + + fn eval(&self, point: &[NF], alpha_powers: &[EF]) -> EF { + debug_assert_eq!(point.len(), WIDTH + 1); + let mut res = EF::ZERO; + let compressed = point[WIDTH]; + for i in 0..self.compressed_output { + res += alpha_powers[i] * point[i]; + } + for i in self.compressed_output..WIDTH { + res += alpha_powers[i] * point[i] * (EF::ONE - compressed); + } + + res + } +} + +impl SumcheckComputationPacked for CompressionComputation +{ + fn degree(&self) -> usize { + 2 + } + + fn eval_packed_base(&self, point: &[PFPacking], alpha_powers: &[EF]) -> EFPacking { + debug_assert_eq!(point.len(), WIDTH + 1); + let mut res = EFPacking::::ZERO; + let compressed = point[WIDTH]; + for i in 0..self.compressed_output { + res += EFPacking::::from(alpha_powers[i]) * point[i]; + } + for i in self.compressed_output..WIDTH { + res += EFPacking::::from(alpha_powers[i]) + * point[i] + * (PFPacking::::ONE - compressed); + } + + res + } + + fn eval_packed_extension(&self, point: &[EFPacking], alpha_powers: &[EF]) -> EFPacking { + debug_assert_eq!(point.len(), WIDTH + 1); + let mut res = EFPacking::::ZERO; + let compressed = point[WIDTH]; + for i in 0..self.compressed_output { + res += point[i] * alpha_powers[i]; + } + for i in self.compressed_output..WIDTH { + res += point[i] * (EFPacking::::ONE - compressed) * alpha_powers[i]; + } + + res + } +} diff --git a/crates/poseidon_circuit/src/gkr_layers/mod.rs b/crates/poseidon_circuit/src/gkr_layers/mod.rs index ad9661e0..d4988348 100644 --- a/crates/poseidon_circuit/src/gkr_layers/mod.rs +++ b/crates/poseidon_circuit/src/gkr_layers/mod.rs @@ -7,6 +7,9 @@ pub use partial_round::*; mod batch_partial_rounds; pub use batch_partial_rounds::*; +mod compression; +pub use compression::*; + use p3_koala_bear::{ KOALABEAR_RC16_EXTERNAL_FINAL, KOALABEAR_RC16_EXTERNAL_INITIAL, KOALABEAR_RC16_INTERNAL, KOALABEAR_RC24_EXTERNAL_FINAL, KOALABEAR_RC24_EXTERNAL_INITIAL, KOALABEAR_RC24_INTERNAL, diff --git a/crates/poseidon_circuit/src/prove.rs b/crates/poseidon_circuit/src/prove.rs index 0200fdb8..f60c5896 100644 --- a/crates/poseidon_circuit/src/prove.rs +++ b/crates/poseidon_circuit/src/prove.rs @@ -1,5 +1,5 @@ use crate::{ - EF, F, FullRoundComputation, PoseidonWitness, + CompressionComputation, EF, F, FullRoundComputation, PoseidonWitness, gkr_layers::{BatchPartialRounds, PoseidonGKRLayers}, }; use crate::{GKRPoseidonResult, build_poseidon_matrices}; @@ -27,16 +27,18 @@ where let (output_claims, mut claims) = info_span!("computing output claims").in_scope(|| { let eq_poly = eval_eq(&point[univariate_skips..]); - let inner_evals = witness - .output_layer - .par_iter() - .map(|poly| { - FPacking::::unpack_slice(poly) - .chunks_exact(eq_poly.len()) - .map(|chunk| dot_product(eq_poly.iter().copied(), chunk.iter().copied())) - .collect::>() - }) - .collect::>(); + let inner_evals = match &witness.compression { + Some((_, _, compressed_output)) => compressed_output, + None => &witness.output_layer, + } + .par_iter() + .map(|poly| { + FPacking::::unpack_slice(poly) + .chunks_exact(eq_poly.len()) + .map(|chunk| dot_product(eq_poly.iter().copied(), chunk.iter().copied())) + .collect::>() + }) + .collect::>(); for evals in &inner_evals { prover_state.add_extension_scalars(evals); } @@ -60,6 +62,26 @@ where (output_claims, claims) }); + if let Some((_, compression_indicator, _)) = &witness.compression { + (point, claims) = prove_gkr_round( + prover_state, + &CompressionComputation:: { + compressed_output: layers.compressed_output.unwrap(), + }, + &witness + .output_layer + .iter() + .chain(std::iter::once(compression_indicator)) + .map(Vec::as_slice) + .collect::>(), + &point, + &claims, + univariate_skips, + ); + + let _ = claims.pop().unwrap(); // the claim on the compression indicator columns can be evaluated by the verifier directly + } + for (layer, full_round_constants) in witness .final_full_layers .iter() @@ -135,6 +157,7 @@ where } claims = apply_matrix(&inv_mds_matrix, &claims); + let _ = claims; let pcs_point_for_inputs = point.clone(); diff --git a/crates/poseidon_circuit/src/tests.rs b/crates/poseidon_circuit/src/tests.rs index ad27f750..73205f06 100644 --- a/crates/poseidon_circuit/src/tests.rs +++ b/crates/poseidon_circuit/src/tests.rs @@ -31,7 +31,7 @@ const COMPRESSION_OUTPUT_WIDTH: usize = 8; #[test] fn test_poseidon_benchmark() { run_poseidon_benchmark(12, false); - // run_poseidon_benchmark(12, true); + run_poseidon_benchmark(12, true); } pub fn run_poseidon_benchmark(log_n_poseidons: usize, compress: bool) { @@ -169,7 +169,10 @@ pub fn run_poseidon_benchmark(log_n_poseidons: usize, compress: bool) { ( build_verifier_state(&prover_state), prover_state.proof_size(), - witness.output_layer, + match compress { + false => witness.output_layer, + true => witness.compression.unwrap().2, + }, prover_duration, output_values, claim_point, diff --git a/crates/poseidon_circuit/src/verify.rs b/crates/poseidon_circuit/src/verify.rs index 06b67d7e..f93168cc 100644 --- a/crates/poseidon_circuit/src/verify.rs +++ b/crates/poseidon_circuit/src/verify.rs @@ -3,8 +3,8 @@ use p3_koala_bear::{KoalaBearInternalLayerParameters, KoalaBearParameters}; use p3_monty_31::InternalLayerBaseParameters; use crate::{ - EF, F, FullRoundComputation, GKRPoseidonResult, build_poseidon_matrices, - gkr_layers::PoseidonGKRLayers, + CompressionComputation, EF, F, FullRoundComputation, GKRPoseidonResult, + build_poseidon_matrices, gkr_layers::PoseidonGKRLayers, }; pub fn verify_poseidon_gkr( @@ -50,6 +50,31 @@ where [vec![alpha], output_claim_point[univariate_skips..].to_vec()].concat() }; + if let Some(n_compressions) = n_compressions { + (point, claims) = verify_gkr_round( + verifier_state, + &CompressionComputation:: { + compressed_output: layers.compressed_output.unwrap(), + }, + log_n_poseidons, + &point, + &claims, + univariate_skips, + WIDTH + 1, + ); + + assert!(n_compressions <= 1 << log_n_poseidons); + let compression_eval = claims.pop().unwrap(); + assert_eq!( + compression_eval, + skipped_mle_of_zeros_then_ones( + (1 << log_n_poseidons) - n_compressions, + &point, + &selectors + ) + ); + } + for full_round_constants in layers.final_full_rounds.iter().rev() { claims = apply_matrix(&inv_mds_matrix, &claims); diff --git a/crates/poseidon_circuit/src/witness_gen.rs b/crates/poseidon_circuit/src/witness_gen.rs index cfcb0d70..d1fcd023 100644 --- a/crates/poseidon_circuit/src/witness_gen.rs +++ b/crates/poseidon_circuit/src/witness_gen.rs @@ -22,7 +22,7 @@ pub struct PoseidonWitness pub remaining_partial_round_inputs: Vec<[Vec; WIDTH]>, // the input of each remaining partial round pub final_full_layers: Vec<[Vec; WIDTH]>, // just before cubing pub output_layer: [Vec; WIDTH], // output of the permutation - pub compression: Option<(usize, Vec)>, // num compressions, compression indicator column + pub compression: Option<(usize, Vec, [Vec; WIDTH])>, // num compressions, compression indicator column, compressed output } impl @@ -84,6 +84,20 @@ where &[F::ZERO; WIDTH], // unused ); + let compression = compression.map(|(n_compressions, indicator)| { + let mut compressed_output = output_layer.clone(); + for _ in 0..n_compressions { + compressed_output[layers.compressed_output.unwrap()..] + .par_iter_mut() + .for_each(|col| { + col.iter_mut().zip(&indicator).for_each(|(out, ind)| { + *out *= A::ONE - *ind; + }); + }); + } + (n_compressions, indicator, compressed_output) + }); + PoseidonWitness { input_layer: input, initial_full_layers, From 6ab657c4eb3e5ee6950b3927b13b70310d30908f Mon Sep 17 00:00:00 2001 From: Tom Wambsgans Date: Wed, 29 Oct 2025 12:25:01 +0400 Subject: [PATCH 03/11] fix weird memory slowdown --- .../witness_generation/src/poseidon_tables.rs | 2 ++ crates/poseidon_circuit/src/witness_gen.rs | 26 ++++++++++++------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/crates/lean_prover/witness_generation/src/poseidon_tables.rs b/crates/lean_prover/witness_generation/src/poseidon_tables.rs index 245ee343..9223b4a1 100644 --- a/crates/lean_prover/witness_generation/src/poseidon_tables.rs +++ b/crates/lean_prover/witness_generation/src/poseidon_tables.rs @@ -6,6 +6,7 @@ use p3_field::PrimeCharacteristicRing; use p3_koala_bear::{KoalaBearInternalLayerParameters, KoalaBearParameters}; use p3_monty_31::InternalLayerBaseParameters; use poseidon_circuit::{PoseidonGKRLayers, PoseidonWitness, generate_poseidon_witness}; +use tracing::instrument; use utils::{padd_with_zero_to_next_power_of_two, transposed_par_iter_mut}; pub fn all_poseidon_16_indexes(poseidons_16: &[WitnessPoseidon16]) -> [Vec; 3] { @@ -84,6 +85,7 @@ pub fn full_poseidon_indexes_poly( all_poseidon_indexes } +#[instrument(skip_all)] pub fn generate_poseidon_witness_helper< const WIDTH: usize, const N_COMMITED_CUBES: usize, diff --git a/crates/poseidon_circuit/src/witness_gen.rs b/crates/poseidon_circuit/src/witness_gen.rs index d1fcd023..71532468 100644 --- a/crates/poseidon_circuit/src/witness_gen.rs +++ b/crates/poseidon_circuit/src/witness_gen.rs @@ -85,16 +85,22 @@ where ); let compression = compression.map(|(n_compressions, indicator)| { - let mut compressed_output = output_layer.clone(); - for _ in 0..n_compressions { - compressed_output[layers.compressed_output.unwrap()..] - .par_iter_mut() - .for_each(|col| { - col.iter_mut().zip(&indicator).for_each(|(out, ind)| { - *out *= A::ONE - *ind; - }); - }); - } + let compressed_output = (0..WIDTH) + .into_par_iter() + .map(|col_idx| { + if col_idx < layers.compressed_output.unwrap() { + output_layer[col_idx].clone() + } else { + output_layer[col_idx] + .iter() + .zip(&indicator) + .map(|(out, ind)| *out * (A::ONE - *ind)) + .collect::>() + } + }) + .collect::>() + .try_into() + .unwrap(); (n_compressions, indicator, compressed_output) }); From 2d9022b16637da492dcb864be4224d3ba2341667 Mon Sep 17 00:00:00 2001 From: Tom Wambsgans Date: Wed, 29 Oct 2025 12:39:41 +0400 Subject: [PATCH 04/11] display proof size GKR / PCS --- crates/poseidon_circuit/src/tests.rs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/crates/poseidon_circuit/src/tests.rs b/crates/poseidon_circuit/src/tests.rs index 73205f06..6436ff08 100644 --- a/crates/poseidon_circuit/src/tests.rs +++ b/crates/poseidon_circuit/src/tests.rs @@ -78,7 +78,8 @@ pub fn run_poseidon_benchmark(log_n_poseidons: usize, compress: bool) { let ( mut verifier_state, - proof_size, + proof_size_pcs, + proof_size_gkr, output_layer, prover_duration, output_values_prover, @@ -150,6 +151,8 @@ pub fn run_poseidon_benchmark(log_n_poseidons: usize, compress: bool) { } } + let proof_size_gkr = prover_state.proof_size(); + let global_statements = packed_pcs_global_statements_for_prover( &committed_polys, &committed_col_dims, @@ -166,9 +169,11 @@ pub fn run_poseidon_benchmark(log_n_poseidons: usize, compress: bool) { let prover_duration = prover_time.elapsed(); + let proof_size_pcs = prover_state.proof_size() - proof_size_gkr; ( build_verifier_state(&prover_state), - prover_state.proof_size(), + proof_size_pcs, + proof_size_gkr, match compress { false => witness.output_layer, true => witness.compression.unwrap().2, @@ -311,8 +316,10 @@ pub fn run_poseidon_benchmark(log_n_poseidons: usize, compress: bool) { prover_duration.as_secs_f64() / plaintext_duration.as_secs_f64() ); println!( - "Proof size: {:.1} KiB (without merkle pruning)", - (proof_size * F::bits()) as f64 / (8.0 * 1024.0) + "Proof size: GKR = {:.1} KiB, PCS = {:.1} KiB . Total = {:.1} KiB (available optimizations: GKR = 40%, PCS = 15%)", + (proof_size_gkr * F::bits()) as f64 / (8.0 * 1024.0), + (proof_size_pcs * F::bits()) as f64 / (8.0 * 1024.0), + ((proof_size_gkr + proof_size_pcs) * F::bits()) as f64 / (8.0 * 1024.0), ); println!("Verifier time: {}ms", verifier_duration.as_millis()); } From 08fc8dac64ed5a2ffce83af0daa957bffc0847c1 Mon Sep 17 00:00:00 2001 From: Tom Wambsgans Date: Wed, 29 Oct 2025 13:09:54 +0400 Subject: [PATCH 05/11] partial rounds OK --- crates/poseidon_circuit/src/gkr_layers/mod.rs | 7 +-- .../src/gkr_layers/partial_round.rs | 48 +++++-------------- crates/poseidon_circuit/src/prove.rs | 13 +++-- crates/poseidon_circuit/src/verify.rs | 13 +++-- crates/poseidon_circuit/src/witness_gen.rs | 32 ++++++++----- 5 files changed, 48 insertions(+), 65 deletions(-) diff --git a/crates/poseidon_circuit/src/gkr_layers/mod.rs b/crates/poseidon_circuit/src/gkr_layers/mod.rs index d4988348..408c7bcb 100644 --- a/crates/poseidon_circuit/src/gkr_layers/mod.rs +++ b/crates/poseidon_circuit/src/gkr_layers/mod.rs @@ -21,7 +21,7 @@ use crate::F; pub struct PoseidonGKRLayers { pub initial_full_rounds: Vec<[F; WIDTH]>, pub batch_partial_rounds: BatchPartialRounds, - pub partial_rounds_remaining: Vec>, + pub partial_rounds_remaining: Vec, pub final_full_rounds: Vec<[F; WIDTH]>, pub compressed_output: Option, } @@ -62,10 +62,7 @@ impl PoseidonGKRLayers>(); + let partial_rounds_remaining = internal_constants[N_COMMITED_CUBES + 1..].to_vec(); let final_full_rounds = final_constants.to_vec(); Self { initial_full_rounds, diff --git a/crates/poseidon_circuit/src/gkr_layers/partial_round.rs b/crates/poseidon_circuit/src/gkr_layers/partial_round.rs index aed313af..b5b2ca4c 100644 --- a/crates/poseidon_circuit/src/gkr_layers/partial_round.rs +++ b/crates/poseidon_circuit/src/gkr_layers/partial_round.rs @@ -1,22 +1,14 @@ use multilinear_toolkit::prelude::*; use p3_field::ExtensionField; -use p3_koala_bear::{ - GenericPoseidon2LinearLayersKoalaBear, KoalaBearInternalLayerParameters, KoalaBearParameters, -}; -use p3_monty_31::InternalLayerBaseParameters; -use p3_poseidon2::GenericPoseidon2LinearLayers; use crate::{EF, F}; #[derive(Debug)] -pub struct PartialRoundComputation { - pub constant: F, -} +pub struct PartialRoundComputation; impl, const WIDTH: usize> SumcheckComputation for PartialRoundComputation where - KoalaBearInternalLayerParameters: InternalLayerBaseParameters, EF: ExtensionField, { fn degree(&self) -> usize { @@ -25,51 +17,33 @@ where fn eval(&self, point: &[NF], alpha_powers: &[EF]) -> EF { debug_assert_eq!(point.len(), WIDTH); - let first_cubed = (point[0] + self.constant).cube(); - let mut buff = [NF::ZERO; WIDTH]; - buff[0] = first_cubed; - buff[1..WIDTH].copy_from_slice(&point[1..WIDTH]); - GenericPoseidon2LinearLayersKoalaBear::internal_linear_layer(&mut buff); - let mut res = EF::ZERO; - for i in 0..WIDTH { - res += alpha_powers[i] * buff[i]; + let mut res = EF::from(point[0].cube()); + for i in 1..WIDTH { + res += alpha_powers[i] * point[i]; } res } } -impl SumcheckComputationPacked for PartialRoundComputation -where - KoalaBearInternalLayerParameters: InternalLayerBaseParameters, -{ +impl SumcheckComputationPacked for PartialRoundComputation { fn degree(&self) -> usize { 3 } fn eval_packed_base(&self, point: &[FPacking], alpha_powers: &[EF]) -> EFPacking { debug_assert_eq!(point.len(), WIDTH); - let first_cubed = (point[0] + self.constant).cube(); - let mut buff = [PFPacking::::ZERO; WIDTH]; - buff[0] = first_cubed; - buff[1..WIDTH].copy_from_slice(&point[1..WIDTH]); - GenericPoseidon2LinearLayersKoalaBear::internal_linear_layer(&mut buff); - let mut res = EFPacking::::ZERO; - for j in 0..WIDTH { - res += EFPacking::::from(alpha_powers[j]) * buff[j]; + let mut res = EFPacking::::from(point[0].cube()); + for i in 1..WIDTH { + res += EFPacking::::from(alpha_powers[i]) * point[i]; } res } fn eval_packed_extension(&self, point: &[EFPacking], alpha_powers: &[EF]) -> EFPacking { debug_assert_eq!(point.len(), WIDTH); - let first_cubed = (point[0] + PFPacking::::from(self.constant)).cube(); - let mut buff = [EFPacking::::ZERO; WIDTH]; - buff[0] = first_cubed; - buff[1..WIDTH].copy_from_slice(&point[1..WIDTH]); - GenericPoseidon2LinearLayersKoalaBear::internal_linear_layer(&mut buff); - let mut res = EFPacking::::ZERO; - for j in 0..WIDTH { - res += buff[j] * alpha_powers[j]; + let mut res = point[0].cube(); + for i in 1..WIDTH { + res += point[i] * alpha_powers[i]; } res } diff --git a/crates/poseidon_circuit/src/prove.rs b/crates/poseidon_circuit/src/prove.rs index f60c5896..42cbe9ff 100644 --- a/crates/poseidon_circuit/src/prove.rs +++ b/crates/poseidon_circuit/src/prove.rs @@ -1,5 +1,5 @@ use crate::{ - CompressionComputation, EF, F, FullRoundComputation, PoseidonWitness, + CompressionComputation, EF, F, FullRoundComputation, PartialRoundComputation, PoseidonWitness, gkr_layers::{BatchPartialRounds, PoseidonGKRLayers}, }; use crate::{GKRPoseidonResult, build_poseidon_matrices}; @@ -20,7 +20,7 @@ where KoalaBearInternalLayerParameters: InternalLayerBaseParameters, { let selectors = univariate_selectors::(univariate_skips); - let (_mds_matrix, inv_mds_matrix, _light_matrix, _inv_light_matrix) = + let (_mds_matrix, inv_mds_matrix, _light_matrix, inv_light_matrix) = build_poseidon_matrices::(); assert_eq!(point.len(), log2_strict_usize(witness.n_poseidons())); @@ -104,20 +104,23 @@ where } } - for (input_layers, partial_round) in witness - .remaining_partial_round_inputs + for (input_layers, partial_round_constant) in witness + .remaining_partial_round_layers .iter() .zip(&layers.partial_rounds_remaining) .rev() { + claims = apply_matrix(&inv_light_matrix, &claims); + (point, claims) = prove_gkr_round( prover_state, - partial_round, + &PartialRoundComputation:: {}, input_layers, &point, &claims, univariate_skips, ); + claims[0] -= *partial_round_constant; } (point, claims) = prove_batch_internal_round( diff --git a/crates/poseidon_circuit/src/verify.rs b/crates/poseidon_circuit/src/verify.rs index f93168cc..aa3e1795 100644 --- a/crates/poseidon_circuit/src/verify.rs +++ b/crates/poseidon_circuit/src/verify.rs @@ -3,8 +3,7 @@ use p3_koala_bear::{KoalaBearInternalLayerParameters, KoalaBearParameters}; use p3_monty_31::InternalLayerBaseParameters; use crate::{ - CompressionComputation, EF, F, FullRoundComputation, GKRPoseidonResult, - build_poseidon_matrices, gkr_layers::PoseidonGKRLayers, + CompressionComputation, EF, F, FullRoundComputation, GKRPoseidonResult, PartialRoundComputation, build_poseidon_matrices, gkr_layers::PoseidonGKRLayers }; pub fn verify_poseidon_gkr( @@ -19,7 +18,7 @@ where KoalaBearInternalLayerParameters: InternalLayerBaseParameters, { let selectors = univariate_selectors::(univariate_skips); - let (_mds_matrix, inv_mds_matrix, _light_matrix, _inv_light_matrix) = + let (_mds_matrix, inv_mds_matrix, _light_matrix, inv_light_matrix) = build_poseidon_matrices::(); let mut output_claims = vec![]; @@ -93,16 +92,20 @@ where } } - for partial_round in layers.partial_rounds_remaining.iter().rev() { + for partial_round_constant in layers.partial_rounds_remaining.iter().rev() { + claims = apply_matrix(&inv_light_matrix, &claims); + (point, claims) = verify_gkr_round( verifier_state, - partial_round, + &PartialRoundComputation:: {}, log_n_poseidons, &point, &claims, univariate_skips, WIDTH, ); + + claims[0] -= *partial_round_constant; } let claimed_cubes_evals = verifier_state .next_extension_scalars_vec(N_COMMITED_CUBES) diff --git a/crates/poseidon_circuit/src/witness_gen.rs b/crates/poseidon_circuit/src/witness_gen.rs index 71532468..dba2021b 100644 --- a/crates/poseidon_circuit/src/witness_gen.rs +++ b/crates/poseidon_circuit/src/witness_gen.rs @@ -10,7 +10,6 @@ use utils::transposed_par_iter_mut; use crate::F; use crate::gkr_layers::BatchPartialRounds; -use crate::gkr_layers::PartialRoundComputation; use crate::gkr_layers::PoseidonGKRLayers; #[derive(Debug)] @@ -19,7 +18,7 @@ pub struct PoseidonWitness pub initial_full_layers: Vec<[Vec; WIDTH]>, // just before cubing pub batch_partial_round_input: [Vec; WIDTH], // again, the input of the batch (partial) round pub committed_cubes: [Vec; N_COMMITED_CUBES], // the cubes commited in the batch (partial) rounds - pub remaining_partial_round_inputs: Vec<[Vec; WIDTH]>, // the input of each remaining partial round + pub remaining_partial_round_layers: Vec<[Vec; WIDTH]>, // the input of each remaining partial round, just before cubing the first element pub final_full_layers: Vec<[Vec; WIDTH]>, // just before cubing pub output_layer: [Vec; WIDTH], // output of the permutation pub compression: Option<(usize, Vec, [Vec; WIDTH])>, // num compressions, compression indicator column, compressed output @@ -57,19 +56,23 @@ where initial_full_layers.last().unwrap(), &[F::ZERO; WIDTH], // unused ); - let (next_layer, committed_cubes) = + let (mut next_layer, committed_cubes) = apply_batch_partial_rounds(&batch_partial_round_layer, &layers.batch_partial_rounds); - let mut remaining_partial_inputs = vec![next_layer]; - for constant in &layers.partial_rounds_remaining { - remaining_partial_inputs.push(apply_partial_round( - remaining_partial_inputs.last().unwrap(), - constant, + next_layer[0] = next_layer[0] + .par_iter() + .map(|&val| val + layers.partial_rounds_remaining[0]) + .collect(); + let mut remaining_partial_round_layers = vec![next_layer]; + for &constant in &layers.partial_rounds_remaining[1..] { + remaining_partial_round_layers.push(apply_partial_round( + remaining_partial_round_layers.last().unwrap(), + Some(constant), )); } let mut final_full_layers = vec![apply_full_round::<_, _, false, false, true>( - &remaining_partial_inputs.pop().unwrap(), // we remove it + &apply_partial_round(remaining_partial_round_layers.last().unwrap(), None), &layers.final_full_rounds[0], )]; for constants in &layers.final_full_rounds[1..] { @@ -109,7 +112,7 @@ where initial_full_layers, batch_partial_round_input: batch_partial_round_layer, committed_cubes, - remaining_partial_round_inputs: remaining_partial_inputs, + remaining_partial_round_layers, final_full_layers, output_layer, compression, @@ -159,23 +162,26 @@ where // #[instrument(skip_all)] fn apply_partial_round( input_layers: &[Vec], - partial_round: &PartialRoundComputation, + partial_round_constant: Option, ) -> [Vec; WIDTH] where A: Algebra + Copy + Send + Sync, KoalaBearInternalLayerParameters: InternalLayerBaseParameters, { + // cube single, light matrix mul, add single constant let mut output_layers: [_; WIDTH] = array::from_fn(|_| A::zero_vec(input_layers[0].len())); transposed_par_iter_mut(&mut output_layers) .enumerate() .for_each(|(row_index, output_row)| { - let first_cubed = (input_layers[0][row_index] + partial_round.constant).cube(); let mut buff = [A::ZERO; WIDTH]; - buff[0] = first_cubed; + buff[0] = input_layers[0][row_index].cube(); for j in 1..WIDTH { buff[j] = input_layers[j][row_index]; } GenericPoseidon2LinearLayersKoalaBear::internal_linear_layer(&mut buff); + if let Some(constant) = partial_round_constant { + buff[0] += constant; + } for j in 0..WIDTH { *output_row[j] = buff[j]; } From 58249db77347840efde5e0c263754efea13b502c Mon Sep 17 00:00:00 2001 From: Tom Wambsgans Date: Wed, 29 Oct 2025 13:54:12 +0400 Subject: [PATCH 06/11] allow N_COMMITED_CUBES = 0 --- crates/poseidon_circuit/src/gkr_layers/mod.rs | 16 ++++-- crates/poseidon_circuit/src/prove.rs | 36 +++++++------ crates/poseidon_circuit/src/tests.rs | 27 +++++++--- crates/poseidon_circuit/src/verify.rs | 54 +++++++++++-------- crates/poseidon_circuit/src/witness_gen.rs | 18 +++++-- src/main.rs | 2 +- 6 files changed, 98 insertions(+), 55 deletions(-) diff --git a/crates/poseidon_circuit/src/gkr_layers/mod.rs b/crates/poseidon_circuit/src/gkr_layers/mod.rs index 408c7bcb..d9501734 100644 --- a/crates/poseidon_circuit/src/gkr_layers/mod.rs +++ b/crates/poseidon_circuit/src/gkr_layers/mod.rs @@ -20,7 +20,7 @@ use crate::F; #[derive(Debug)] pub struct PoseidonGKRLayers { pub initial_full_rounds: Vec<[F; WIDTH]>, - pub batch_partial_rounds: BatchPartialRounds, + pub batch_partial_rounds: Option>, pub partial_rounds_remaining: Vec, pub final_full_rounds: Vec<[F; WIDTH]>, pub compressed_output: Option, @@ -58,11 +58,17 @@ impl PoseidonGKRLayers, ) -> Self { let initial_full_rounds = initial_constants.to_vec(); - let batch_partial_rounds = BatchPartialRounds { - constants: internal_constants[..N_COMMITED_CUBES].try_into().unwrap(), - last_constant: internal_constants[N_COMMITED_CUBES], + let (batch_partial_rounds, partial_rounds_remaining) = if N_COMMITED_CUBES == 0 { + (None, internal_constants.to_vec()) + } else { + ( + Some(BatchPartialRounds { + constants: internal_constants[..N_COMMITED_CUBES].try_into().unwrap(), + last_constant: internal_constants[N_COMMITED_CUBES], + }), + internal_constants[N_COMMITED_CUBES + 1..].to_vec(), + ) }; - let partial_rounds_remaining = internal_constants[N_COMMITED_CUBES + 1..].to_vec(); let final_full_rounds = final_constants.to_vec(); Self { initial_full_rounds, diff --git a/crates/poseidon_circuit/src/prove.rs b/crates/poseidon_circuit/src/prove.rs index 42cbe9ff..cecf206f 100644 --- a/crates/poseidon_circuit/src/prove.rs +++ b/crates/poseidon_circuit/src/prove.rs @@ -123,15 +123,17 @@ where claims[0] -= *partial_round_constant; } - (point, claims) = prove_batch_internal_round( - prover_state, - &witness.batch_partial_round_input, - &witness.committed_cubes, - &layers.batch_partial_rounds, - &point, - &claims, - &selectors, - ); + if let Some(batch_partial_round_input) = &witness.batch_partial_round_input { + (point, claims) = prove_batch_internal_round( + prover_state, + batch_partial_round_input, + &witness.committed_cubes, + layers.batch_partial_rounds.as_ref().unwrap(), + &point, + &claims, + &selectors, + ); + } let pcs_point_for_cubes = point.clone(); @@ -170,12 +172,16 @@ where univariate_skips, &witness.input_layer, ); - let cubes_statements = inner_evals_on_commited_columns( - prover_state, - &pcs_point_for_cubes, - univariate_skips, - &witness.committed_cubes, - ); + let cubes_statements = if N_COMMITED_CUBES == 0 { + Default::default() + } else { + inner_evals_on_commited_columns( + prover_state, + &pcs_point_for_cubes, + univariate_skips, + &witness.committed_cubes, + ) + }; GKRPoseidonResult { output_values: output_claims, diff --git a/crates/poseidon_circuit/src/tests.rs b/crates/poseidon_circuit/src/tests.rs index 6436ff08..17e92b68 100644 --- a/crates/poseidon_circuit/src/tests.rs +++ b/crates/poseidon_circuit/src/tests.rs @@ -1,5 +1,8 @@ use multilinear_toolkit::prelude::*; -use p3_koala_bear::{KoalaBear, QuinticExtensionFieldKB}; +use p3_koala_bear::{ + KoalaBear, KoalaBearInternalLayerParameters, KoalaBearParameters, QuinticExtensionFieldKB, +}; +use p3_monty_31::InternalLayerBaseParameters; use packed_pcs::{ ColDims, packed_pcs_commit, packed_pcs_global_statements_for_prover, packed_pcs_global_statements_for_verifier, packed_pcs_parse_commitment, @@ -22,19 +25,26 @@ use crate::{ type F = KoalaBear; type EF = QuinticExtensionFieldKB; -const WIDTH: usize = 16; - -const UNIVARIATE_SKIPS: usize = 3; -const N_COMMITED_CUBES: usize = 16; const COMPRESSION_OUTPUT_WIDTH: usize = 8; #[test] fn test_poseidon_benchmark() { - run_poseidon_benchmark(12, false); - run_poseidon_benchmark(12, true); + run_poseidon_benchmark::<16, 0, 3>(12, false); + run_poseidon_benchmark::<16, 0, 3>(12, true); + run_poseidon_benchmark::<16, 16, 3>(12, false); + run_poseidon_benchmark::<16, 16, 3>(12, true); } -pub fn run_poseidon_benchmark(log_n_poseidons: usize, compress: bool) { +pub fn run_poseidon_benchmark< + const WIDTH: usize, + const N_COMMITED_CUBES: usize, + const UNIVARIATE_SKIPS: usize, +>( + log_n_poseidons: usize, + compress: bool, +) where + KoalaBearInternalLayerParameters: InternalLayerBaseParameters, +{ init_tracing(); precompute_dft_twiddles::(1 << 24); @@ -67,6 +77,7 @@ pub fn run_poseidon_benchmark(log_n_poseidons: usize, compress: bool) { ); let default_cubes = default_cube_layers::(&layers); + let input_col_dims = vec![ColDims::padded(n_poseidons, F::ZERO); WIDTH]; let cubes_col_dims = default_cubes .iter() diff --git a/crates/poseidon_circuit/src/verify.rs b/crates/poseidon_circuit/src/verify.rs index aa3e1795..9554dee2 100644 --- a/crates/poseidon_circuit/src/verify.rs +++ b/crates/poseidon_circuit/src/verify.rs @@ -3,7 +3,8 @@ use p3_koala_bear::{KoalaBearInternalLayerParameters, KoalaBearParameters}; use p3_monty_31::InternalLayerBaseParameters; use crate::{ - CompressionComputation, EF, F, FullRoundComputation, GKRPoseidonResult, PartialRoundComputation, build_poseidon_matrices, gkr_layers::PoseidonGKRLayers + CompressionComputation, EF, F, FullRoundComputation, GKRPoseidonResult, + PartialRoundComputation, build_poseidon_matrices, gkr_layers::PoseidonGKRLayers, }; pub fn verify_poseidon_gkr( @@ -107,24 +108,29 @@ where claims[0] -= *partial_round_constant; } - let claimed_cubes_evals = verifier_state - .next_extension_scalars_vec(N_COMMITED_CUBES) - .unwrap(); - (point, claims) = verify_gkr_round( - verifier_state, - &layers.batch_partial_rounds, - log_n_poseidons, - &point, - &[claims, claimed_cubes_evals.clone()].concat(), - univariate_skips, - WIDTH + N_COMMITED_CUBES, - ); + let mut pcs_point_for_cubes = vec![]; + let mut pcs_evals_for_cubes = vec![]; + if N_COMMITED_CUBES > 0 { + let claimed_cubes_evals = verifier_state + .next_extension_scalars_vec(N_COMMITED_CUBES) + .unwrap(); + + (point, claims) = verify_gkr_round( + verifier_state, + layers.batch_partial_rounds.as_ref().unwrap(), + log_n_poseidons, + &point, + &[claims, claimed_cubes_evals.clone()].concat(), + univariate_skips, + WIDTH + N_COMMITED_CUBES, + ); - let pcs_point_for_cubes = point.clone(); - let pcs_evals_for_cubes = claims[WIDTH..].to_vec(); + pcs_point_for_cubes = point.clone(); + pcs_evals_for_cubes = claims[WIDTH..].to_vec(); - claims = claims[..WIDTH].to_vec(); + claims = claims[..WIDTH].to_vec(); + } for full_round_constants in layers.initial_full_rounds.iter().rev() { claims = apply_matrix(&inv_mds_matrix, &claims); @@ -156,12 +162,16 @@ where &selectors, ); - let cubes_statements = verify_inner_evals_on_commited_columns( - verifier_state, - &pcs_point_for_cubes, - &pcs_evals_for_cubes, - &selectors, - ); + let cubes_statements = if N_COMMITED_CUBES == 0 { + Default::default() + } else { + verify_inner_evals_on_commited_columns( + verifier_state, + &pcs_point_for_cubes, + &pcs_evals_for_cubes, + &selectors, + ) + }; GKRPoseidonResult { output_values: output_claims, diff --git a/crates/poseidon_circuit/src/witness_gen.rs b/crates/poseidon_circuit/src/witness_gen.rs index dba2021b..3f8fda1d 100644 --- a/crates/poseidon_circuit/src/witness_gen.rs +++ b/crates/poseidon_circuit/src/witness_gen.rs @@ -16,7 +16,7 @@ use crate::gkr_layers::PoseidonGKRLayers; pub struct PoseidonWitness { pub input_layer: [Vec; WIDTH], // input of the permutation pub initial_full_layers: Vec<[Vec; WIDTH]>, // just before cubing - pub batch_partial_round_input: [Vec; WIDTH], // again, the input of the batch (partial) round + pub batch_partial_round_input: Option<[Vec; WIDTH]>, // again, the input of the batch (partial) round pub committed_cubes: [Vec; N_COMMITED_CUBES], // the cubes commited in the batch (partial) rounds pub remaining_partial_round_layers: Vec<[Vec; WIDTH]>, // the input of each remaining partial round, just before cubing the first element pub final_full_layers: Vec<[Vec; WIDTH]>, // just before cubing @@ -52,12 +52,18 @@ where )); } - let batch_partial_round_layer = apply_full_round::<_, _, true, true, false>( + let layer = apply_full_round::<_, _, true, true, false>( initial_full_layers.last().unwrap(), &[F::ZERO; WIDTH], // unused ); - let (mut next_layer, committed_cubes) = - apply_batch_partial_rounds(&batch_partial_round_layer, &layers.batch_partial_rounds); + + let (batch_partial_round_layer, mut next_layer, committed_cubes) = if N_COMMITED_CUBES == 0 { + (None, layer, vec![].try_into().unwrap()) + } else { + let (next_layer, committed_cubes) = + apply_batch_partial_rounds(&layer, &layers.batch_partial_rounds.as_ref().unwrap()); + (Some(layer), next_layer, committed_cubes) + }; next_layer[0] = next_layer[0] .par_iter() @@ -226,6 +232,10 @@ where A: Algebra + Copy + Send + Sync, KoalaBearInternalLayerParameters: InternalLayerBaseParameters, { + if N_COMMITED_CUBES == 0 { + return vec![].try_into().unwrap(); + } + generate_poseidon_witness::( array::from_fn(|_| vec![A::ZERO]), layers, diff --git a/src/main.rs b/src/main.rs index a0c1db73..4b87d4db 100644 --- a/src/main.rs +++ b/src/main.rs @@ -35,7 +35,7 @@ fn main() { Cli::Poseidon { log_n_perms: log_count, } => { - run_poseidon_benchmark(log_count, false); + run_poseidon_benchmark::<16, 16, 3>(log_count, false); } } } From 6ba64d6160774a470c23f01d8a75851d45f9ccd5 Mon Sep 17 00:00:00 2001 From: Tom Wambsgans Date: Wed, 29 Oct 2025 14:04:36 +0400 Subject: [PATCH 07/11] fix --- crates/lean_prover/src/common.rs | 4 ++-- crates/poseidon_circuit/src/gkr_layers/mod.rs | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/lean_prover/src/common.rs b/crates/lean_prover/src/common.rs index 26141ccf..f4866211 100644 --- a/crates/lean_prover/src/common.rs +++ b/crates/lean_prover/src/common.rs @@ -9,8 +9,8 @@ use poseidon_circuit::{PoseidonGKRLayers, default_cube_layers}; use crate::*; use lean_vm::*; -pub(crate) const N_COMMITED_CUBES_P16: usize = KOALABEAR_RC16_INTERNAL.len() - 1; -pub(crate) const N_COMMITED_CUBES_P24: usize = KOALABEAR_RC24_INTERNAL.len() - 1; +pub(crate) const N_COMMITED_CUBES_P16: usize = KOALABEAR_RC16_INTERNAL.len() - 2; +pub(crate) const N_COMMITED_CUBES_P24: usize = KOALABEAR_RC24_INTERNAL.len() - 2; pub fn get_base_dims( n_cycles: usize, diff --git a/crates/poseidon_circuit/src/gkr_layers/mod.rs b/crates/poseidon_circuit/src/gkr_layers/mod.rs index d9501734..53c171ca 100644 --- a/crates/poseidon_circuit/src/gkr_layers/mod.rs +++ b/crates/poseidon_circuit/src/gkr_layers/mod.rs @@ -57,6 +57,7 @@ impl PoseidonGKRLayers, ) -> Self { + assert!(N_COMMITED_CUBES < internal_constants.len() - 1); // TODO we could go up to internal_constants.len() in theory let initial_full_rounds = initial_constants.to_vec(); let (batch_partial_rounds, partial_rounds_remaining) = if N_COMMITED_CUBES == 0 { (None, internal_constants.to_vec()) From afaa044e82cb9e66e60452c20cc13dd3855f0293 Mon Sep 17 00:00:00 2001 From: Tom Wambsgans Date: Wed, 29 Oct 2025 14:26:05 +0400 Subject: [PATCH 08/11] graphs --- .../benchmark_graphs/graphs/raw_poseidons.svg | 134 ++++++------- .../graphs/xmss_aggregated_overhead.svg | 180 +++++++++--------- docs/benchmark_graphs/main.py | 2 + 3 files changed, 163 insertions(+), 153 deletions(-) diff --git a/docs/benchmark_graphs/graphs/raw_poseidons.svg b/docs/benchmark_graphs/graphs/raw_poseidons.svg index 20e44048..5e475cf1 100644 --- a/docs/benchmark_graphs/graphs/raw_poseidons.svg +++ b/docs/benchmark_graphs/graphs/raw_poseidons.svg @@ -6,7 +6,7 @@ - 2025-10-27T17:01:34.195704 + 2025-10-29T14:25:42.416126 image/svg+xml @@ -42,16 +42,16 @@ z +" clip-path="url(#pb3489d7956)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - - + @@ -192,11 +192,11 @@ z +" clip-path="url(#pb3489d7956)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -246,11 +246,11 @@ z +" clip-path="url(#pb3489d7956)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -268,11 +268,11 @@ L 175.488611 34.3575 +" clip-path="url(#pb3489d7956)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -306,11 +306,11 @@ z +" clip-path="url(#pb3489d7956)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -362,11 +362,11 @@ z +" clip-path="url(#pb3489d7956)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -384,11 +384,11 @@ L 365.375278 34.3575 +" clip-path="url(#pb3489d7956)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -418,11 +418,11 @@ z +" clip-path="url(#pb3489d7956)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -461,11 +461,11 @@ z +" clip-path="url(#pb3489d7956)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -483,11 +483,11 @@ L 555.261944 34.3575 +" clip-path="url(#pb3489d7956)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -505,11 +505,11 @@ L 618.5575 34.3575 +" clip-path="url(#pb3489d7956)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -529,16 +529,16 @@ L 681.853056 34.3575 +" clip-path="url(#pb3489d7956)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - - + @@ -563,11 +563,11 @@ z +" clip-path="url(#pb3489d7956)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -583,11 +583,11 @@ L 699.9375 331.172479 +" clip-path="url(#pb3489d7956)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -603,11 +603,11 @@ L 699.9375 290.232482 +" clip-path="url(#pb3489d7956)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -623,11 +623,11 @@ L 699.9375 249.292485 +" clip-path="url(#pb3489d7956)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -643,11 +643,11 @@ L 699.9375 208.352488 +" clip-path="url(#pb3489d7956)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -663,11 +663,11 @@ L 699.9375 167.412491 +" clip-path="url(#pb3489d7956)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -683,11 +683,11 @@ L 699.9375 126.472494 +" clip-path="url(#pb3489d7956)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -703,11 +703,11 @@ L 699.9375 85.532496 +" clip-path="url(#pb3489d7956)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -976,9 +976,10 @@ L 473.881944 332.195979 L 482.924167 330.14898 L 528.135278 329.12548 L 609.515278 247.245485 -" clip-path="url(#p7e61cc75bf)" style="fill: none; stroke: #2e86ab; stroke-width: 2; stroke-linecap: square"/> +L 627.599722 239.057486 +" clip-path="url(#pb3489d7956)" style="fill: none; stroke: #2e86ab; stroke-width: 2; stroke-linecap: square"/> - - - - - - - - - - - - + + + + + + + + + + + + +L 627.599722 106.002495 +" clip-path="url(#pb3489d7956)" style="fill: none; stroke: #a23b72; stroke-width: 2; stroke-linecap: square"/> - - - - + + + + +" clip-path="url(#pb3489d7956)" style="fill: none; stroke-dasharray: 7.4,3.2; stroke-dashoffset: 0; stroke: #2e86ab; stroke-width: 2"/> - + @@ -1209,7 +1213,7 @@ L 71.6975 202.346238 L 83.6975 202.346238 " style="fill: none; stroke: #a23b72; stroke-width: 2; stroke-linecap: square"/> - + @@ -1472,7 +1476,7 @@ z - + diff --git a/docs/benchmark_graphs/graphs/xmss_aggregated_overhead.svg b/docs/benchmark_graphs/graphs/xmss_aggregated_overhead.svg index 35758e13..310570e3 100644 --- a/docs/benchmark_graphs/graphs/xmss_aggregated_overhead.svg +++ b/docs/benchmark_graphs/graphs/xmss_aggregated_overhead.svg @@ -6,7 +6,7 @@ - 2025-10-27T17:01:34.555931 + 2025-10-29T14:25:42.770537 image/svg+xml @@ -42,16 +42,16 @@ z +" clip-path="url(#pa67d272b1c)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - - + @@ -192,11 +192,11 @@ z +" clip-path="url(#pa67d272b1c)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -246,11 +246,11 @@ z +" clip-path="url(#pa67d272b1c)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -268,11 +268,11 @@ L 169.337639 34.3575 +" clip-path="url(#pa67d272b1c)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -306,11 +306,11 @@ z +" clip-path="url(#pa67d272b1c)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -362,11 +362,11 @@ z +" clip-path="url(#pa67d272b1c)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -384,11 +384,11 @@ L 361.965972 34.3575 +" clip-path="url(#pa67d272b1c)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -418,11 +418,11 @@ z +" clip-path="url(#pa67d272b1c)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -461,11 +461,11 @@ z +" clip-path="url(#pa67d272b1c)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -483,11 +483,11 @@ L 554.594306 34.3575 +" clip-path="url(#pa67d272b1c)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -505,11 +505,11 @@ L 618.80375 34.3575 +" clip-path="url(#pa67d272b1c)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -529,16 +529,16 @@ L 683.013194 34.3575 +" clip-path="url(#pa67d272b1c)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - - + @@ -563,11 +563,11 @@ z +" clip-path="url(#pa67d272b1c)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -610,11 +610,11 @@ z +" clip-path="url(#pa67d272b1c)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -630,11 +630,11 @@ L 701.35875 278.860261 +" clip-path="url(#pa67d272b1c)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -650,11 +650,11 @@ L 701.35875 232.234153 +" clip-path="url(#pa67d272b1c)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -671,11 +671,11 @@ L 701.35875 185.608045 +" clip-path="url(#pa67d272b1c)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -692,11 +692,11 @@ L 701.35875 138.981937 +" clip-path="url(#pa67d272b1c)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -713,11 +713,11 @@ L 701.35875 92.355829 +" clip-path="url(#pa67d272b1c)" style="fill: none; stroke: #b0b0b0; stroke-opacity: 0.3; stroke-width: 0.8; stroke-linecap: square"/> - + @@ -746,9 +746,10 @@ L 472.039306 263.473645 L 481.212083 267.571835 L 527.075972 273.315535 L 609.630972 141.336982 -" clip-path="url(#pd6f5031cb3)" style="fill: none; stroke: #2e86ab; stroke-width: 2; stroke-linecap: square"/> +L 627.976528 126.204163 +" clip-path="url(#pa67d272b1c)" style="fill: none; stroke: #2e86ab; stroke-width: 2; stroke-linecap: square"/> - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + +L 627.976528 93.859466 +" clip-path="url(#pa67d272b1c)" style="fill: none; stroke: #a23b72; stroke-width: 2; stroke-linecap: square"/> - - - - + + + + +" clip-path="url(#pa67d272b1c)" style="fill: none; stroke-dasharray: 7.4,3.2; stroke-dashoffset: 0; stroke: #2e86ab; stroke-width: 2"/> - - - + - + - - + - + - - + + diff --git a/docs/benchmark_graphs/main.py b/docs/benchmark_graphs/main.py index b5e51645..85159159 100644 --- a/docs/benchmark_graphs/main.py +++ b/docs/benchmark_graphs/main.py @@ -123,6 +123,7 @@ def create_duration_graph(data, target=None, target_label=None, title="", y_lege ('2025-10-13', 205000, None), ('2025-10-18', 210000, 620_000), ('2025-10-27', 610_000, 1_250_000), + ('2025-10-29', 650_000, 1_300_000), ], target=1_500_000, target_label="Target (1.5M Poseidon2 / s)", @@ -201,6 +202,7 @@ def create_duration_graph(data, target=None, target_label=None, title="", y_lege ('2025-10-13', 2.13 / 0.38, None), ('2025-10-18', 1.96 / 0.37, 1.07 / 0.12), ('2025-10-27', (610_000 / 157) / 314, (1_250_000 / 157) / 555), + ('2025-10-29', (650_000 / 157) / 314, (1_300_000 / 157) / 555), ], target=2, target_label="Target (2x)", From d82dc49eb6bf95a957c7dc5d5da486b102ed614b Mon Sep 17 00:00:00 2001 From: Tom Wambsgans Date: Wed, 29 Oct 2025 14:26:43 +0400 Subject: [PATCH 09/11] branch --- Cargo.lock | 4 ++++ Cargo.toml | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3cb82865..1518772a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -95,6 +95,7 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "backend" version = "0.3.0" +source = "git+https://github.com/leanEthereum/multilinear-toolkit.git#1d8cb80f851100c8ec81e12d0088d9a278200c4c" dependencies = [ "fiat-shamir", "itertools", @@ -178,6 +179,7 @@ dependencies = [ [[package]] name = "constraints-folder" version = "0.3.0" +source = "git+https://github.com/leanEthereum/multilinear-toolkit.git#1d8cb80f851100c8ec81e12d0088d9a278200c4c" dependencies = [ "fiat-shamir", "p3-air", @@ -486,6 +488,7 @@ checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" [[package]] name = "multilinear-toolkit" version = "0.3.0" +source = "git+https://github.com/leanEthereum/multilinear-toolkit.git#1d8cb80f851100c8ec81e12d0088d9a278200c4c" dependencies = [ "backend", "constraints-folder", @@ -1089,6 +1092,7 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "sumcheck" version = "0.3.0" +source = "git+https://github.com/leanEthereum/multilinear-toolkit.git#1d8cb80f851100c8ec81e12d0088d9a278200c4c" dependencies = [ "backend", "constraints-folder", diff --git a/Cargo.toml b/Cargo.toml index 84efe502..eae85faa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -106,8 +106,8 @@ poseidon_circuit.workspace = true # [patch."https://github.com/TomWambsgans/whir-p3.git"] # whir-p3 = { path = "../zk/whir/fork-whir-p3" } -[patch."https://github.com/leanEthereum/multilinear-toolkit.git"] -multilinear-toolkit = { path = "../zk/multilinear-toolkit" } +# [patch."https://github.com/leanEthereum/multilinear-toolkit.git"] +# multilinear-toolkit = { path = "../zk/multilinear-toolkit" } [profile.release] lto = "thin" From 8d8d5074196d0c895b5a3584198ec1c03326aa8f Mon Sep 17 00:00:00 2001 From: Tom Wambsgans Date: Wed, 29 Oct 2025 14:30:07 +0400 Subject: [PATCH 10/11] clippy --- .../src/gkr_layers/compression.rs | 4 +--- crates/poseidon_circuit/src/prove.rs | 5 ++--- crates/poseidon_circuit/src/utils.rs | 22 ++++++++----------- crates/poseidon_circuit/src/verify.rs | 5 ++--- crates/poseidon_circuit/src/witness_gen.rs | 6 ++--- 5 files changed, 17 insertions(+), 25 deletions(-) diff --git a/crates/poseidon_circuit/src/gkr_layers/compression.rs b/crates/poseidon_circuit/src/gkr_layers/compression.rs index eeaf8cc3..f556bae3 100644 --- a/crates/poseidon_circuit/src/gkr_layers/compression.rs +++ b/crates/poseidon_circuit/src/gkr_layers/compression.rs @@ -1,4 +1,3 @@ - use multilinear_toolkit::prelude::*; use p3_field::ExtensionField; @@ -33,8 +32,7 @@ where } } -impl SumcheckComputationPacked for CompressionComputation -{ +impl SumcheckComputationPacked for CompressionComputation { fn degree(&self) -> usize { 2 } diff --git a/crates/poseidon_circuit/src/prove.rs b/crates/poseidon_circuit/src/prove.rs index cecf206f..1836b283 100644 --- a/crates/poseidon_circuit/src/prove.rs +++ b/crates/poseidon_circuit/src/prove.rs @@ -2,7 +2,7 @@ use crate::{ CompressionComputation, EF, F, FullRoundComputation, PartialRoundComputation, PoseidonWitness, gkr_layers::{BatchPartialRounds, PoseidonGKRLayers}, }; -use crate::{GKRPoseidonResult, build_poseidon_matrices}; +use crate::{GKRPoseidonResult, build_poseidon_inv_matrices}; use multilinear_toolkit::prelude::*; use p3_koala_bear::{KoalaBearInternalLayerParameters, KoalaBearParameters}; use p3_monty_31::InternalLayerBaseParameters; @@ -20,8 +20,7 @@ where KoalaBearInternalLayerParameters: InternalLayerBaseParameters, { let selectors = univariate_selectors::(univariate_skips); - let (_mds_matrix, inv_mds_matrix, _light_matrix, inv_light_matrix) = - build_poseidon_matrices::(); + let (inv_mds_matrix, inv_light_matrix) = build_poseidon_inv_matrices::(); assert_eq!(point.len(), log2_strict_usize(witness.n_poseidons())); diff --git a/crates/poseidon_circuit/src/utils.rs b/crates/poseidon_circuit/src/utils.rs index ac61331b..30236d57 100644 --- a/crates/poseidon_circuit/src/utils.rs +++ b/crates/poseidon_circuit/src/utils.rs @@ -11,30 +11,26 @@ use tracing::instrument; use crate::F; #[instrument(skip_all)] -pub fn build_poseidon_matrices() -> ( - [[F; WIDTH]; WIDTH], - [[F; WIDTH]; WIDTH], - [[F; WIDTH]; WIDTH], - [[F; WIDTH]; WIDTH], -) +pub fn build_poseidon_inv_matrices() +-> ([[F; WIDTH]; WIDTH], [[F; WIDTH]; WIDTH]) where KoalaBearInternalLayerParameters: InternalLayerBaseParameters, { let mut mds_matrix: [[F; WIDTH]; WIDTH] = array::from_fn(|_| array::from_fn(|_| F::ZERO)); - for i in 0..WIDTH { - mds_matrix[i][i] = F::ONE; - GenericPoseidon2LinearLayersKoalaBear::external_linear_layer(&mut mds_matrix[i]); + for (i, row) in mds_matrix.iter_mut().enumerate() { + row[i] = F::ONE; + GenericPoseidon2LinearLayersKoalaBear::internal_linear_layer(row); } mds_matrix = transpose_matrix(&mds_matrix); let inv_mds_matrix = inverse_matrix(&mds_matrix); let mut light_matrix: [[F; WIDTH]; WIDTH] = array::from_fn(|_| array::from_fn(|_| F::ZERO)); - for i in 0..WIDTH { - light_matrix[i][i] = F::ONE; - GenericPoseidon2LinearLayersKoalaBear::internal_linear_layer(&mut light_matrix[i]); + for (i, row) in light_matrix.iter_mut().enumerate() { + row[i] = F::ONE; + GenericPoseidon2LinearLayersKoalaBear::internal_linear_layer(row); } light_matrix = transpose_matrix(&light_matrix); let inv_light_matrix = inverse_matrix(&light_matrix); - (mds_matrix, inv_mds_matrix, light_matrix, inv_light_matrix) + (inv_mds_matrix, inv_light_matrix) } diff --git a/crates/poseidon_circuit/src/verify.rs b/crates/poseidon_circuit/src/verify.rs index 9554dee2..0c98808b 100644 --- a/crates/poseidon_circuit/src/verify.rs +++ b/crates/poseidon_circuit/src/verify.rs @@ -4,7 +4,7 @@ use p3_monty_31::InternalLayerBaseParameters; use crate::{ CompressionComputation, EF, F, FullRoundComputation, GKRPoseidonResult, - PartialRoundComputation, build_poseidon_matrices, gkr_layers::PoseidonGKRLayers, + PartialRoundComputation, build_poseidon_inv_matrices, gkr_layers::PoseidonGKRLayers, }; pub fn verify_poseidon_gkr( @@ -19,8 +19,7 @@ where KoalaBearInternalLayerParameters: InternalLayerBaseParameters, { let selectors = univariate_selectors::(univariate_skips); - let (_mds_matrix, inv_mds_matrix, _light_matrix, inv_light_matrix) = - build_poseidon_matrices::(); + let (inv_mds_matrix, inv_light_matrix) = build_poseidon_inv_matrices::(); let mut output_claims = vec![]; let mut claims = vec![]; diff --git a/crates/poseidon_circuit/src/witness_gen.rs b/crates/poseidon_circuit/src/witness_gen.rs index 3f8fda1d..0c8c4605 100644 --- a/crates/poseidon_circuit/src/witness_gen.rs +++ b/crates/poseidon_circuit/src/witness_gen.rs @@ -61,7 +61,7 @@ where (None, layer, vec![].try_into().unwrap()) } else { let (next_layer, committed_cubes) = - apply_batch_partial_rounds(&layer, &layers.batch_partial_rounds.as_ref().unwrap()); + apply_batch_partial_rounds(&layer, layers.batch_partial_rounds.as_ref().unwrap()); (Some(layer), next_layer, committed_cubes) }; @@ -146,8 +146,8 @@ where .for_each(|(row_index, output_row)| { let mut buff: [A; WIDTH] = array::from_fn(|j| input_layers[j][row_index]); if CUBE { - for j in 0..WIDTH { - buff[j] = buff[j].cube(); + for v in &mut buff { + *v = v.cube(); } } if MDS { From 1fdb4f8c795478a121e2d7e43f257273216611a3 Mon Sep 17 00:00:00 2001 From: Tom Wambsgans Date: Wed, 29 Oct 2025 14:33:12 +0400 Subject: [PATCH 11/11] typo --- crates/poseidon_circuit/src/utils.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/poseidon_circuit/src/utils.rs b/crates/poseidon_circuit/src/utils.rs index 30236d57..36674132 100644 --- a/crates/poseidon_circuit/src/utils.rs +++ b/crates/poseidon_circuit/src/utils.rs @@ -19,7 +19,7 @@ where let mut mds_matrix: [[F; WIDTH]; WIDTH] = array::from_fn(|_| array::from_fn(|_| F::ZERO)); for (i, row) in mds_matrix.iter_mut().enumerate() { row[i] = F::ONE; - GenericPoseidon2LinearLayersKoalaBear::internal_linear_layer(row); + GenericPoseidon2LinearLayersKoalaBear::external_linear_layer(row); } mds_matrix = transpose_matrix(&mds_matrix); let inv_mds_matrix = inverse_matrix(&mds_matrix);