Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 25 additions & 5 deletions provekit/common/src/prefix_covector.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use {
crate::utils::workload_size,
ark_ff::{Field, One, Zero},
rayon::prelude::*,
whir::{
algebra::{embedding::Embedding, linear_form::LinearForm, mixed_dot, multilinear_extend},
buffer::{Buffer, BufferOps},
Expand Down Expand Up @@ -77,12 +79,11 @@ impl<F: Field> LinearForm<F> for PrefixCovector<F> {
}

fn accumulate(&self, accumulator: &mut [F], scalar: F) {
for (acc, val) in accumulator[..self.vector.len()]
.iter_mut()
accumulator[..self.vector.len()]
.par_iter_mut()
.zip(&self.vector)
{
*acc += scalar * *val;
}
.with_min_len(workload_size::<F>())
.for_each(|(acc, &val)| *acc += scalar * val);
}
}

Expand Down Expand Up @@ -381,6 +382,25 @@ mod tests {
FieldElement::from(n)
}

#[test]
fn prefix_accumulation_preserves_existing_values_and_padding() {
use whir::algebra::fields::Field64_3;

let scalar = Field64_3::new(fe(2), fe(3), fe(5));
for size in [4, 1 << 14] {
let weights: Vec<_> = (0..size)
.map(|i| Field64_3::new(fe(i as u64), fe(7), fe(11)))
.collect();
let mut expected = vec![scalar; 2 * size];
let mut actual = expected.clone();
for (acc, &weight) in expected.iter_mut().zip(&weights) {
*acc += scalar * weight;
}
PrefixCovector::new(weights, 2 * size).accumulate(&mut actual, scalar);
assert_eq!(actual, expected);
}
}

#[test]
fn mle_evaluate_matches_full_vector() {
let domain_size = 16; // 2^4
Expand Down
19 changes: 3 additions & 16 deletions provekit/prover/src/whir_r1cs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ where
.into_iter()
.next()
.expect("single-commitment path requires at least one commitment");
let (mut weights, evals) = create_weights_and_evaluations::<3, _>(
let (mut weights, mut evals) = create_weights_and_evaluations::<3, _>(
&embedding,
scheme.m,
&commitment.polynomial,
Expand Down Expand Up @@ -391,17 +391,16 @@ where
public_weight,
);
merlin.prover_message(&public_eval);
evals.insert(0, public_eval);
}

let evaluations = compute_evaluations(&embedding, &weights, &commitment.polynomial);

let WhirR1CSCommitment { witness, .. } = commitment;

let form_refs = linear_form_refs(&weights);

let final_claim = scheme
.whir_witness
.prove(&mut merlin, witness, &form_refs, &evaluations);
.prove(&mut merlin, witness, &form_refs, &evals);

spark_row.zip(spark_weights).map(|(row, spark_weights)| {
let [claimed_a, claimed_b, claimed_c] =
Expand Down Expand Up @@ -889,18 +888,6 @@ fn create_weights_and_evaluations<const N: usize, M: Embedding>(
(weights, evals)
}

fn compute_evaluations<M: Embedding>(
embedding: &M,
weights: &[PrefixCovector<M::Target>],
polynomial: &Buffer<M::Source>,
) -> Vec<M::Target> {
let polynomial = polynomial.to_slice();
weights
.iter()
.map(|w| mixed_dot(embedding, w.vector(), &polynomial[..w.vector().len()]))
.collect()
}

fn compute_public_weight_evaluation<M: Embedding>(
embedding: &M,
weights: &mut Vec<PrefixCovector<M::Target>>,
Expand Down
38 changes: 37 additions & 1 deletion tooling/provekit-fixtures/tests/profile.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
//! `#[ignore]` prover profiling harness.
//!
//! Two probes (run with `--release`):
//! Profiling probes (run with `--release`):
//!
//! ```text
//! # Repeated squaring-chain benchmarks: Goldilocks first, then BN254
//! RAYON_NUM_THREADS=8 cargo test -p provekit-fixtures --release --test profile field_benchmarks -- --exact --ignored --nocapture
//!
//! # Size sweep: BF vs EF vs bn254 at 2^14..2^20
//! cargo test -p provekit-fixtures --release --test profile size_sweep -- --ignored --nocapture
//!
Expand Down Expand Up @@ -57,6 +60,39 @@ where
)
}

fn benchmark_field<P>(field: &str, log_size: u32)
where
P: FieldHash,
Standard: Distribution<Ext<P>> + Distribution<Base<P>>,
{
P::register();
run_one::<P>(log_size); // Warmup; every proof is verified.
let mut prove = Duration::ZERO;
for _ in 0..3 {
prove += run_one::<P>(log_size).0;
}
println!(
"{field} squaring chain 2^{log_size},{:.3}",
prove.as_secs_f64() * 1000.0 / 3.0,
);
}

#[test]
#[ignore = "manual benchmark; run --release -- --ignored --nocapture"]
fn field_benchmarks() {
assert_eq!(
provekit_common::WitnessCommitmentMode::default(),
provekit_common::WitnessCommitmentMode::ZeroKnowledge,
);
println!("\ncircuit,prover_time_ms");
for size in [18, 20] {
benchmark_field::<provekit_backend_goldilocks::GoldilocksField>("Goldilocks", size);
}
for size in [18, 20] {
benchmark_field::<provekit_backend_bn254::Bn254Field>("BN254", size);
}
}

/// Run one (field, size) cell and print its row immediately (so partial results
/// survive a later OOM). Wrapped in `catch_unwind` so a panic in one cell does
/// not abort the rest of the sweep.
Expand Down
Loading