Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sparse poly eval #214

Merged
merged 11 commits into from
Feb 11, 2021
13 changes: 13 additions & 0 deletions ff/src/fields/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,19 @@ pub trait Field:
}
res
}

/// Exponentiates this element by a number represented with `u64` limbs,
/// least significant limb first.
#[inline]
fn pow_with_table<S: AsRef<[u64]>>(pows_2: &[Self], exp: S) -> Self {
let mut res = Self::one();
for (pow, bit) in BitIteratorLE::without_trailing_zeros(exp).enumerate() {
if bit {
res *= &pows_2[pow];
}
}
res
}
}

/// A trait that defines parameters for a field that can be used for FFTs.
Expand Down
26 changes: 25 additions & 1 deletion poly-benches/benches/dense_uv_polynomial.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
extern crate criterion;

use ark_ff::Field;
use ark_poly::{polynomial::univariate::DensePolynomial, Polynomial, UVPolynomial};
use ark_poly::{
polynomial::univariate::DensePolynomial, polynomial::univariate::SparsePolynomial, Polynomial,
UVPolynomial,
};
use ark_poly_benches::size_range;
use ark_std::rand::Rng;
use ark_test_curves::bls12_381::Fr as bls12_381_fr;
use criterion::BenchmarkId;
use criterion::{criterion_group, criterion_main, Bencher, Criterion};
Expand All @@ -14,6 +18,7 @@ const BENCHMARK_LOG_INTERVAL_DEGREE: usize = 1;
const ENABLE_ADD_BENCH: bool = true;
const ENABLE_ADD_ASSIGN_BENCH: bool = true;
const ENABLE_EVALUATE_BENCH: bool = true;
const ENABLE_SPARSE_EVALUATE_BENCH: bool = true;

// returns vec![2^{min}, 2^{min + interval}, ..., 2^{max}], where:
// interval = BENCHMARK_LOG_INTERVAL_DEGREE
Expand All @@ -35,6 +40,21 @@ fn setup_bench<F: Field>(c: &mut Criterion, name: &str, bench_fn: fn(&mut Benche
group.finish();
}

fn bench_sparse_poly_evaluate<F: Field>(b: &mut Bencher, non_zero_entries: &usize) {
const MAX_DEGREE: usize = 1 << 15;
// Per benchmark setup
let mut rng = &mut ark_std::test_rng();
let mut inner: Vec<(usize, F)> = Vec::with_capacity(*non_zero_entries);
(0..*non_zero_entries)
.for_each(|_| inner.push((rng.gen_range(0, MAX_DEGREE), F::rand(&mut rng))));
let poly = SparsePolynomial::<F>::from_coefficients_vec(inner);
b.iter(|| {
// Per benchmark iteration
let pt = F::rand(&mut rng);
poly.evaluate(&pt);
});
}

fn bench_poly_evaluate<F: Field>(b: &mut Bencher, degree: &usize) {
// Per benchmark setup
let mut rng = &mut ark_std::test_rng();
Expand Down Expand Up @@ -81,6 +101,10 @@ fn poly_benches<F: Field>(c: &mut Criterion, name: &'static str) {
let cur_name = format!("{:?} - evaluate_polynomial", name.clone());
setup_bench::<F>(c, &cur_name, bench_poly_evaluate::<F>);
}
if ENABLE_SPARSE_EVALUATE_BENCH {
let cur_name = format!("{:?} - evaluate_sparse_polynomial", name.clone());
setup_bench::<F>(c, &cur_name, bench_sparse_poly_evaluate::<F>);
}
}

fn bench_bls12_381(c: &mut Criterion) {
Expand Down
21 changes: 20 additions & 1 deletion poly/src/polynomial/univariate/sparse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,30 @@ impl<F: Field> Polynomial<F> for SparsePolynomial<F> {
if self.is_zero() {
return F::zero();
}

let total_bits = core::mem::size_of::<usize>() * 8;
let max_pow_2 = total_bits - self.degree().leading_zeros() as usize;

let mut pows_2 = Vec::<F>::with_capacity(max_pow_2);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is computing log2(total_bits), right? You can replace it with let max_pow_2 = ark_std::log2(total_bits);.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fair enough

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

imo this should be a usize default function

Copy link
Contributor Author

@jon-chuang jon-chuang Feb 10, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

btw, I am uncomfortable with the current name, imo it ought to be named log2_then_ceil, or log2_of_next_pow_of_2 or something. I like the second name best.


let mut p = *point;
pows_2.push(p);
for _ in 1..max_pow_2 {
p.square_in_place();
pows_2.push(p);
}
// compute all coeff * point^{i} and then sum the results
let total = self
.coeffs
.iter()
.map(|(i, c)| (*c * point.pow(&[*i as u64])))
.map(|(i, c)| {
debug_assert_eq!(
F::pow_with_table(&pows_2[..], &[*i as u64]),
point.pow(&[*i as u64]),
"pows not equal"
);
*c * F::pow_with_table(&pows_2[..], &[*i as u64])
jon-chuang marked this conversation as resolved.
Show resolved Hide resolved
})
.sum();
total
}
Expand Down