Simplify Poly multiplication, speed up interpolation. (#23)

* Simplify Poly multiplication, speed up interpolation.

Also add more `Mul` implementations.

* Postpone division: speed up interpolation some more.

* Speed up interpolation: re-use base polynomial.
master
Andreas Fackler 7 years ago committed by GitHub
parent 76ac2a5415
commit ded335f236
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 127
      src/poly.rs

@ -211,23 +211,23 @@ impl<'a, B: Borrow<Poly>> ops::Mul<B> for &'a Poly {
type Output = Poly; type Output = Poly;
fn mul(self, rhs: B) -> Self::Output { fn mul(self, rhs: B) -> Self::Output {
let coeff: Vec<Fr> = (0..(self.coeff.len() + rhs.borrow().coeff.len() - 1)) let rhs = rhs.borrow();
.map(|i| { if rhs.coeff.is_empty() || self.coeff.is_empty() {
// TODO: clear these secrets from the stack. return Poly::zero().expect("failed to create zero Poly");
let mut c = Fr::zero();
for j in i.saturating_sub(rhs.borrow().degree())..(1 + cmp::min(i, self.degree())) {
let mut s = self.coeff[j];
s.mul_assign(&rhs.borrow().coeff[i - j]);
c.add_assign(&s);
} }
c let mut coeff = vec![Fr::zero(); self.coeff.len() + rhs.borrow().coeff.len() - 1];
}).collect(); let mut s; // TODO: Mlock and zero on drop.
for (i, cs) in self.coeff.iter().enumerate() {
match Poly::new(coeff) { for (j, cr) in rhs.coeff.iter().enumerate() {
Ok(poly) => poly, s = *cs;
Err(e) => panic!("Failed to create a new `Poly` duing muliplication: {}", e), s.mul_assign(cr);
coeff[i + j].add_assign(&s);
} }
} }
Poly::new(coeff).unwrap_or_else(|e| {
panic!("Failed to create a new `Poly` during muliplication: {}", e);
})
}
} }
impl<B: Borrow<Poly>> ops::Mul<B> for Poly { impl<B: Borrow<Poly>> ops::Mul<B> for Poly {
@ -244,14 +244,26 @@ impl<B: Borrow<Self>> ops::MulAssign<B> for Poly {
} }
} }
impl ops::MulAssign<Fr> for Poly {
fn mul_assign(&mut self, rhs: Fr) {
if rhs.is_zero() {
*self = Poly::zero().expect("failed to create zero Poly");
} else {
for c in &mut self.coeff {
c.mul_assign(&rhs);
}
}
}
}
/// # Panics /// # Panics
/// ///
/// This operation may panic if: when multiplying the polynomial by a zero field element, we fail /// This operation may panic if: when multiplying the polynomial by a zero field element, we fail
/// to munlock the cleared `coeff` vector. /// to munlock the cleared `coeff` vector.
impl<'a> ops::Mul<Fr> for Poly { impl<'a> ops::Mul<&'a Fr> for Poly {
type Output = Poly; type Output = Poly;
fn mul(mut self, rhs: Fr) -> Self::Output { fn mul(mut self, rhs: &Fr) -> Self::Output {
if rhs.is_zero() { if rhs.is_zero() {
self.zero_secret_memory(); self.zero_secret_memory();
if let Err(e) = self.munlock_secret_memory() { if let Err(e) = self.munlock_secret_memory() {
@ -259,13 +271,38 @@ impl<'a> ops::Mul<Fr> for Poly {
} }
self.coeff.clear(); self.coeff.clear();
} else { } else {
self.coeff.iter_mut().for_each(|c| c.mul_assign(&rhs)); self.coeff.iter_mut().for_each(|c| c.mul_assign(rhs));
} }
self self
} }
} }
impl<'a> ops::Mul<u64> for Poly { impl ops::Mul<Fr> for Poly {
type Output = Poly;
fn mul(self, rhs: Fr) -> Self::Output {
let rhs = &rhs;
self * rhs
}
}
impl<'a> ops::Mul<&'a Fr> for &'a Poly {
type Output = Poly;
fn mul(self, rhs: &Fr) -> Self::Output {
(*self).clone() * rhs
}
}
impl<'a> ops::Mul<Fr> for &'a Poly {
type Output = Poly;
fn mul(self, rhs: Fr) -> Self::Output {
(*self).clone() * rhs
}
}
impl ops::Mul<u64> for Poly {
type Output = Poly; type Output = Poly;
fn mul(self, rhs: u64) -> Self::Output { fn mul(self, rhs: u64) -> Self::Output {
@ -436,7 +473,7 @@ impl Poly {
/// Returns the degree. /// Returns the degree.
pub fn degree(&self) -> usize { pub fn degree(&self) -> usize {
self.coeff.len() - 1 self.coeff.len().saturating_sub(1)
} }
/// Returns the value at the point `i`. /// Returns the value at the point `i`.
@ -482,35 +519,35 @@ impl Poly {
/// Returns an `Error::MlockFailed` if we hit the system's locked memory limit and failed to /// Returns an `Error::MlockFailed` if we hit the system's locked memory limit and failed to
/// `mlock` the new `Poly` instance. /// `mlock` the new `Poly` instance.
fn compute_interpolation(samples: &[(Fr, Fr)]) -> Result<Self> { fn compute_interpolation(samples: &[(Fr, Fr)]) -> Result<Self> {
let mut poly; // Interpolates on the first `i` samples.
let mut base; // Is zero on the first `i` samples.
if samples.is_empty() { if samples.is_empty() {
return Poly::zero(); return Poly::zero();
} else if samples.len() == 1 { } else {
return Poly::constant(samples[0].1); poly = Poly::constant(samples[0].1)?;
} let mut minus_s0 = samples[0].0;
// The degree is at least 1 now. minus_s0.negate();
let degree = samples.len() - 1; base = Poly::new(vec![minus_s0, Fr::one()])?;
// Interpolate all but the last sample. }
let prev = Self::compute_interpolation(&samples[..degree])?;
let (x, mut y) = samples[degree]; // The last sample. // We update `base` so that it is always zero on all previous samples, and `poly` so that
y.sub_assign(&prev.evaluate(x)); // it has the correct values on the previous samples.
let step = Self::lagrange(x, &samples[..degree])?; for (ref x, ref y) in &samples[1..] {
Self::constant(y).map(|poly| poly * step + prev) // Scale `base` so that its value at `x` is the difference between `y` and `poly`'s
// current value at `x`: Adding it to `poly` will then make it correct for `x`.
let mut diff = *y;
diff.sub_assign(&poly.evaluate(x));
let mut base_val = base.evaluate(x);
diff.mul_assign(&base_val.inverse().expect("sample points must be distinct"));
base *= diff;
poly += &base;
// Finally, multiply `base` by X - x, so that it is zero at `x`, too, now.
let mut minus_x = *x;
minus_x.negate();
base *= Poly::new(vec![minus_x, Fr::one()])?;
} }
Ok(poly)
/// Returns the Lagrange base polynomial that is `1` in `p` and `0` in every `samples[i].0`.
///
/// # Errors
///
/// Returns an `Error::MlockFailed` if we hit the system's locked memory limit.
fn lagrange(p: Fr, samples: &[(Fr, Fr)]) -> Result<Self> {
let mut result = Self::one()?;
for &(sx, _) in samples {
let mut denom = p;
denom.sub_assign(&sx);
denom = denom.inverse().expect("sample points must be distinct");
result *= (Self::identity()? - Self::constant(sx)?) * Self::constant(denom)?;
}
Ok(result)
} }
// Removes the `mlock` for `len` elements that have been truncated from the `coeff` vector. // Removes the `mlock` for `len` elements that have been truncated from the `coeff` vector.

Loading…
Cancel
Save