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
Merged
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
11 changes: 6 additions & 5 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ The main features of this release are:
- Small speedups to MSMs
- Big speedups to radix-2 FFTs
- Fix in the assembly arithmetic backend
- Adding new traits for basic curve cycles and pairing based curve cycles
- Adding new traits for basic curve cycles and pairing based curve cycles

### Breaking changes
- #20 (ark-poly) Move univariate DensePolynomial and SparsePolynomial into a
- #20 (ark-poly) Move univariate DensePolynomial and SparsePolynomial into a
univariate sub-crate. Make this change by:
find w/ regex `ark_poly::(Dense|Sparse)Polynomial`, and replace with `ark_poly::univariate::$1Polynomial`.
- #36 (ark-ec) In Short-Weierstrass curves, include an infinity bit in `ToConstraintField`.
Expand All @@ -31,12 +31,12 @@ The main features of this release are:
- #129 (ark-ff) Move `ark_ff::{UniformRand, test_rng}` to `ark_std::{UniformRand, test_rng}`.
Importing these from `ark-ff` is still possible, but is deprecated and will be removed in the following release.
- #144 (ark-poly) Add `CanonicalSerialize` and `CanonicalDeserialize` trait bounds for `Polynomial`.
- #160 (ark-serialize, ark-ff, ark-ec)
- #160 (ark-serialize, ark-ff, ark-ec)
- Remove `ConstantSerializedSize`; users should use `serialized_size*` (see next).
- Add `serialized_size_with_flags` method to `CanonicalSerializeWithFlags`.
- Add `serialized_size_with_flags` method to `CanonicalSerializeWithFlags`.
- Change `from_random_bytes_with_flags` to output `ark_serialize::Flags`.
- Change signatures of `Flags::from_u8*` to output `Option`.
- Change `Flags::from_u8*` to be more strict about the inputs they accept:
- Change `Flags::from_u8*` to be more strict about the inputs they accept:
if the top bits of the `u8` value do *not* correspond to one of the possible outputs of `Flags::u8_bitmask`, then these methods output `None`, whereas before they output
a default value.
Downstream users other than `ark-curves` should not see breakage unless they rely on these methods/traits explicitly.
Expand Down Expand Up @@ -85,6 +85,7 @@ The main features of this release are:
- #201 (ark-ec, ark-ff, ark-test-curves, ark-test-templates) Remove the dependency on `rand_xorshift`
- #205 (ark-ec, ark-ff) Unroll loops and conditionally use intrinsics in `biginteger` arithmetic, and reduce copies in `ff` and `ec` arithmetic.
- #207 (ark-ff) Improve performance of extension fields when the non-residue is negative. (Improves fq2, fq12, and g2 speed on bls12 and bn curves)
- #214 (ark-poly) Utilise a more efficient way of evaluating a polynomial at a single point

### Bug fixes
- #36 (ark-ec) In Short-Weierstrass curves, include an infinity bit in `ToConstraintField`.
Expand Down
18 changes: 18 additions & 0 deletions ff/src/fields/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,24 @@ pub trait Field:
}
res
}

/// Exponentiates a field element `f` by a number represented with `u64` limbs,
/// using a precomputed table containing as many powers of 2 of `f`
/// as the 1 + the floor of log2 of the exponent `exp`, starting from the 1st power.
/// That is, `powers_of_2` should equal `&[p, p^2, p^4, ..., p^(2^n)]`
/// when `exp` has at most `n` bits.
///
/// This returns `None` when a power is missing from the table.
#[inline]
fn pow_with_table<S: AsRef<[u64]>>(powers_of_2: &[Self], exp: S) -> Option<Self> {
let mut res = Self::one();
for (pow, bit) in BitIteratorLE::without_trailing_zeros(exp).enumerate() {
if bit {
res *= powers_of_2.get(pow)?;
}
}
Some(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
20 changes: 19 additions & 1 deletion poly/src/polynomial/univariate/sparse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,29 @@ impl<F: Field> Polynomial<F> for SparsePolynomial<F> {
if self.is_zero() {
return F::zero();
}

// We need floor(log2(deg)) + 1 powers, starting from the 0th power p^2^0 = p
let num_powers = 0usize.leading_zeros() - self.degree().leading_zeros();
let mut powers_of_2 = Vec::with_capacity(num_powers as usize);

let mut p = *point;
powers_of_2.push(p);
for _ in 1..num_powers {
p.square_in_place();
powers_of_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(&powers_of_2[..], &[*i as u64]).unwrap(),
point.pow(&[*i as u64]),
"pows not equal"
);
*c * F::pow_with_table(&powers_of_2[..], &[*i as u64]).unwrap()
})
.sum();
total
}
Expand Down