From f896fdb577b5be5bfb9ef3eaf3116e5bfd8bb38d Mon Sep 17 00:00:00 2001 From: Prashanth Ramakrishna Date: Thu, 2 Jul 2026 07:42:59 -0400 Subject: [PATCH 1/2] Skip shrink_to_fit in per-round folds The fused inner-product path already documents that a realloc per round is pricier than the capacity we carry; apply the same policy to the remaining fold sites. --- src/inner_product_sumcheck.rs | 3 ++- src/multilinear_sumcheck.rs | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/inner_product_sumcheck.rs b/src/inner_product_sumcheck.rs index 9fa3137..e5c176d 100644 --- a/src/inner_product_sumcheck.rs +++ b/src/inner_product_sumcheck.rs @@ -172,7 +172,8 @@ pub fn fold(values: &mut Vec, weight: F) { scalar_mul(tail, F::ONE - weight); values.truncate(half); - values.shrink_to_fit(); + // Skip shrink_to_fit — realloc per round is pricier than the capacity + // we carry; the capacity frees once the Vec drops. } /// Two-pass fold-then-compute; reference version kept for testing. diff --git a/src/multilinear_sumcheck.rs b/src/multilinear_sumcheck.rs index 8b3b598..cbaf64b 100644 --- a/src/multilinear_sumcheck.rs +++ b/src/multilinear_sumcheck.rs @@ -140,7 +140,6 @@ pub fn fold(values: &mut Vec, weight: F) { ))] { if crate::simd_sumcheck::dispatch::try_simd_reduce_msb(values, weight) { - values.shrink_to_fit(); return; } } @@ -174,7 +173,8 @@ pub fn fold(values: &mut Vec, weight: F) { scalar_mul(tail, F::ONE - weight); values.truncate(half); - values.shrink_to_fit(); + // Skip shrink_to_fit — realloc per round is pricier than the capacity + // we carry; the capacity frees once the Vec drops. } /// Two-pass fold-then-compute. Reference only. From 5fcbab653ebbe6e3e15744c2e6b307622bbe4a58 Mon Sep 17 00:00:00 2001 From: Prashanth Ramakrishna Date: Thu, 2 Jul 2026 07:42:59 -0400 Subject: [PATCH 2/2] Delegate InnerProductProver::round to the fused kernel round() previously did two separate fold passes plus a serial compute loop. Delegate to fused_fold_and_compute_polynomial and compute_sumcheck_polynomial (rayon-parallel, one pass), mirroring MultilinearProver. Wire format is unchanged. --- src/provers/inner_product.rs | 51 ++++-------------------------------- 1 file changed, 5 insertions(+), 46 deletions(-) diff --git a/src/provers/inner_product.rs b/src/provers/inner_product.rs index 391a926..2f69893 100644 --- a/src/provers/inner_product.rs +++ b/src/provers/inner_product.rs @@ -59,56 +59,15 @@ where } fn round(&mut self, challenge: Option) -> Vec { - // Fold with previous challenge (if any). - if let Some(w) = challenge { - ip::fold(&mut self.a, w); - ip::fold(&mut self.b, w); - } - // EvalsInfty: emit [q(0), q(∞)] where // q(0) = dot(a_lo, b_lo) // q(∞) = [x²] q(x) = dot(a_hi − a_lo, b_hi − b_lo) - // = Σ (ah − al)·(bh − bl) // The verifier derives q(1) = claim - q(0). - let n = self.a.len(); - if n <= 1 { - let v = if n == 1 { - self.a[0] * self.b[0] - } else { - F::ZERO - }; - return vec![v, F::ZERO]; - } - - let half = n.next_power_of_two() >> 1; - let (a_lo, a_hi) = self.a.split_at(half); - let (b_lo, b_hi) = self.b.split_at(half); - - // a_lo may be longer than a_hi (non-pow2 input, implicit zero padding). - let paired = a_hi.len(); - let a_lo_paired = &a_lo[..paired]; - let b_lo_paired = &b_lo[..paired]; - let a_lo_tail = &a_lo[paired..]; - let b_lo_tail = &b_lo[paired..]; - - let mut q0 = F::ZERO; - let mut q_inf = F::ZERO; - for i in 0..paired { - let al = a_lo_paired[i]; - let ah = a_hi[i]; - let bl = b_lo_paired[i]; - let bh = b_hi[i]; - q0 += al * bl; - q_inf += (ah - al) * (bh - bl); - } - - // Tail (hi implicitly zero): ah = bh = 0, so - // q(0) += al·bl - // q(∞) += (0 − al)·(0 − bl) = al·bl - let tail_dot: F = a_lo_tail.iter().zip(b_lo_tail).map(|(&a, &b)| a * b).sum(); - q0 += tail_dot; - q_inf += tail_dot; - + let (q0, q_inf) = if let Some(w) = challenge { + ip::fused_fold_and_compute_polynomial(&mut self.a, &mut self.b, w) + } else { + ip::compute_sumcheck_polynomial(&self.a, &self.b) + }; vec![q0, q_inf] }