Add implementation of batch normalization for projective elements.

This commit is contained in:
Sean Bowe 2019-08-11 21:27:34 -06:00
parent 15b9d024cf
commit 450587f19b
No known key found for this signature in database
GPG Key ID: 95684257D8F8B031
3 changed files with 180 additions and 0 deletions

View File

@ -42,6 +42,11 @@ fn criterion_benchmark(c: &mut Criterion) {
let a = G1Projective::generator();
let a_affine = G1Affine::generator();
let s = Scalar::from_raw([1, 2, 3, 4]);
const N: usize = 10000;
let v = vec![G1Projective::generator(); N];
let mut q = vec![G1Affine::identity(); N];
c.bench_function(&format!("{} check on curve", name), move |b| {
b.iter(|| black_box(a).is_on_curve())
});
@ -63,6 +68,12 @@ fn criterion_benchmark(c: &mut Criterion) {
c.bench_function(&format!("{} scalar multiplication", name), move |b| {
b.iter(|| black_box(a) * black_box(s))
});
c.bench_function(&format!("{} batch to affine n={}", name, N), move |b| {
b.iter(|| {
G1Projective::batch_normalize(black_box(&v), black_box(&mut q));
black_box(&q)[0]
})
});
}
// G2Affine
@ -100,6 +111,11 @@ fn criterion_benchmark(c: &mut Criterion) {
let a = G2Projective::generator();
let a_affine = G2Affine::generator();
let s = Scalar::from_raw([1, 2, 3, 4]);
const N: usize = 10000;
let v = vec![G2Projective::generator(); N];
let mut q = vec![G2Affine::identity(); N];
c.bench_function(&format!("{} check on curve", name), move |b| {
b.iter(|| black_box(a).is_on_curve())
});
@ -121,6 +137,12 @@ fn criterion_benchmark(c: &mut Criterion) {
c.bench_function(&format!("{} scalar multiplication", name), move |b| {
b.iter(|| black_box(a) * black_box(s))
});
c.bench_function(&format!("{} batch to affine n={}", name, N), move |b| {
b.iter(|| {
G2Projective::batch_normalize(black_box(&v), black_box(&mut q));
black_box(&q)[0]
})
});
}
}

View File

@ -737,6 +737,46 @@ impl G1Projective {
acc
}
/// Converts a batch of `G1Projective` elements into `G1Affine` elements. This
/// function will panic if `p.len() != q.len()`.
pub fn batch_normalize(p: &[Self], q: &mut [G1Affine]) {
assert_eq!(p.len(), q.len());
let mut acc = Fp::one();
for (p, q) in p.iter().zip(q.iter_mut()) {
// We use the `x` field of `G1Affine` to store the product
// of previous z-coordinates seen.
q.x = acc;
// We will end up skipping all identities in p
acc = Fp::conditional_select(&(acc * p.z), &acc, p.is_identity());
}
// This is the inverse, as all z-coordinates are nonzero and the ones
// that are not are skipped.
acc = acc.invert().unwrap();
for (p, q) in p.iter().rev().zip(q.iter_mut().rev()) {
let skip = p.is_identity();
// Compute tmp = 1/z
let tmp = q.x * acc;
// Cancel out z-coordinate in denominator of `acc`
acc = Fp::conditional_select(&(acc * p.z), &acc, skip);
// Set the coordinates to the correct value
let tmp2 = tmp.square();
let tmp3 = tmp2 * tmp;
q.x = p.x * tmp2;
q.y = p.y * tmp3;
q.infinity = Choice::from(0u8);
*q = G1Affine::conditional_select(&q, &G1Affine::identity(), skip);
}
}
/// Returns true if this element is the identity (the point at infinity).
#[inline]
pub fn is_identity(&self) -> Choice {
@ -1264,3 +1304,42 @@ fn test_is_torsion_free() {
assert!(bool::from(G1Affine::identity().is_torsion_free()));
assert!(bool::from(G1Affine::generator().is_torsion_free()));
}
#[test]
fn test_batch_normalize() {
let a = G1Projective::generator().double();
let b = a.double();
let c = b.double();
for a_identity in (0..1).map(|n| n == 1) {
for b_identity in (0..1).map(|n| n == 1) {
for c_identity in (0..1).map(|n| n == 1) {
let mut v = [a, b, c];
if a_identity {
v[0] = G1Projective::identity()
}
if b_identity {
v[1] = G1Projective::identity()
}
if c_identity {
v[2] = G1Projective::identity()
}
let mut t = [
G1Affine::identity(),
G1Affine::identity(),
G1Affine::identity(),
];
let expected = [
G1Affine::from(v[0]),
G1Affine::from(v[1]),
G1Affine::from(v[2]),
];
G1Projective::batch_normalize(&v[..], &mut t[..]);
assert_eq!(&t[..], &expected[..]);
}
}
}
}

View File

@ -829,6 +829,46 @@ impl G2Projective {
acc
}
/// Converts a batch of `G2Projective` elements into `G2Affine` elements. This
/// function will panic if `p.len() != q.len()`.
pub fn batch_normalize(p: &[Self], q: &mut [G2Affine]) {
assert_eq!(p.len(), q.len());
let mut acc = Fp2::one();
for (p, q) in p.iter().zip(q.iter_mut()) {
// We use the `x` field of `G2Affine` to store the product
// of previous z-coordinates seen.
q.x = acc;
// We will end up skipping all identities in p
acc = Fp2::conditional_select(&(acc * p.z), &acc, p.is_identity());
}
// This is the inverse, as all z-coordinates are nonzero and the ones
// that are not are skipped.
acc = acc.invert().unwrap();
for (p, q) in p.iter().rev().zip(q.iter_mut().rev()) {
let skip = p.is_identity();
// Compute tmp = 1/z
let tmp = q.x * acc;
// Cancel out z-coordinate in denominator of `acc`
acc = Fp2::conditional_select(&(acc * p.z), &acc, skip);
// Set the coordinates to the correct value
let tmp2 = tmp.square();
let tmp3 = tmp2 * tmp;
q.x = p.x * tmp2;
q.y = p.y * tmp3;
q.infinity = Choice::from(0u8);
*q = G2Affine::conditional_select(&q, &G2Affine::identity(), skip);
}
}
/// Returns true if this element is the identity (the point at infinity).
#[inline]
pub fn is_identity(&self) -> Choice {
@ -1512,3 +1552,42 @@ fn test_is_torsion_free() {
assert!(bool::from(G2Affine::identity().is_torsion_free()));
assert!(bool::from(G2Affine::generator().is_torsion_free()));
}
#[test]
fn test_batch_normalize() {
let a = G2Projective::generator().double();
let b = a.double();
let c = b.double();
for a_identity in (0..1).map(|n| n == 1) {
for b_identity in (0..1).map(|n| n == 1) {
for c_identity in (0..1).map(|n| n == 1) {
let mut v = [a, b, c];
if a_identity {
v[0] = G2Projective::identity()
}
if b_identity {
v[1] = G2Projective::identity()
}
if c_identity {
v[2] = G2Projective::identity()
}
let mut t = [
G2Affine::identity(),
G2Affine::identity(),
G2Affine::identity(),
];
let expected = [
G2Affine::from(v[0]),
G2Affine::from(v[1]),
G2Affine::from(v[2]),
];
G2Projective::batch_normalize(&v[..], &mut t[..]);
assert_eq!(&t[..], &expected[..]);
}
}
}
}