From e09cf4cc05c6bad238eae145ad9b7cb2a5b2cdd1 Mon Sep 17 00:00:00 2001 From: Ryan Lehmkuhl Date: Sat, 3 Apr 2021 21:51:23 -0700 Subject: [PATCH 1/6] Add/Sub impl for Sparse and Dense polys, Mul for SparsePolynomial --- CHANGELOG.md | 1 + poly/src/polynomial/univariate/dense.rs | 142 ++++++++++++++++++++--- poly/src/polynomial/univariate/sparse.rs | 41 ++++++- 3 files changed, 170 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b290b328e..dcee9e16b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Features - [\#230](https://github.com/arkworks-rs/algebra/pull/230) (ark-ec) Add `wnaf_mul` implementation for `ProjectiveCurve`. +- [\#258](https://github.com/arkworks-rs/algebra/pull/258) (ark-poly) Add `Mul` implementation for `DensePolynomial` ### Improvements diff --git a/poly/src/polynomial/univariate/dense.rs b/poly/src/polynomial/univariate/dense.rs index aedd6c6b7..5e205625e 100644 --- a/poly/src/polynomial/univariate/dense.rs +++ b/poly/src/polynomial/univariate/dense.rs @@ -1,14 +1,13 @@ //! A dense univariate polynomial represented in coefficient form. use crate::univariate::DenseOrSparsePolynomial; +use crate::{univariate::SparsePolynomial, Polynomial, UVPolynomial}; use crate::{EvaluationDomain, Evaluations, GeneralEvaluationDomain}; -use crate::{Polynomial, UVPolynomial}; use ark_serialize::*; use ark_std::{ fmt, ops::{Add, AddAssign, Deref, DerefMut, Div, Mul, Neg, Sub, SubAssign}, vec::Vec, }; - use ark_ff::{FftField, Field, Zero}; use ark_std::rand::Rng; @@ -262,6 +261,37 @@ impl<'a, 'b, F: Field> Add<&'a DensePolynomial> for &'b DensePolynomial { } } +impl<'a, 'b, F: Field> Add<&'a SparsePolynomial> for &'b DensePolynomial { + type Output = DensePolynomial; + + #[inline] + fn add(self, other: &'a SparsePolynomial) -> DensePolynomial { + let result = if self.is_zero() { + other.clone().into() + } else if other.is_zero() { + self.clone() + } else { + let mut result = self.clone(); + // If `other` has higher degree than `self`, create a dense vector + // storing the upper coefficients of the addition + let mut upper_coeffs = match other.degree() > result.degree() { + true => vec![F::zero(); other.degree() - result.degree()], + false => Vec::new(), + }; + for (pow, coeff) in other.iter() { + if *pow <= result.degree() { + result.coeffs[*pow] += coeff; + } else { + upper_coeffs[*pow - result.degree() - 1] = *coeff; + } + } + result.coeffs.extend(upper_coeffs); + result + }; + result + } +} + impl<'a, 'b, F: Field> AddAssign<&'a DensePolynomial> for DensePolynomial { fn add_assign(&mut self, other: &'a DensePolynomial) { if self.is_zero() { @@ -362,6 +392,38 @@ impl<'a, 'b, F: Field> Sub<&'a DensePolynomial> for &'b DensePolynomial { } } +impl<'a, 'b, F: Field> Sub<&'a SparsePolynomial> for &'b DensePolynomial { + type Output = DensePolynomial; + + #[inline] + fn sub(self, other: &'a SparsePolynomial) -> DensePolynomial { + let result = if self.is_zero() { + let result = other.clone(); + result.neg().into() + } else if other.is_zero() { + self.clone() + } else { + let mut result = self.clone(); + // If `other` has higher degree than `self`, create a dense vector + // storing the upper coefficients of the subtraction + let mut upper_coeffs = match other.degree() > result.degree() { + true => vec![F::zero(); other.degree() - result.degree()], + false => Vec::new(), + }; + for (pow, coeff) in other.iter() { + if *pow <= result.degree() { + result.coeffs[*pow] -= coeff; + } else { + upper_coeffs[*pow - result.degree() - 1] = -*coeff; + } + } + result.coeffs.extend(upper_coeffs); + result + }; + result + } +} + impl<'a, 'b, F: Field> SubAssign<&'a DensePolynomial> for DensePolynomial { #[inline] fn sub_assign(&mut self, other: &'a DensePolynomial) { @@ -398,6 +460,23 @@ impl<'a, 'b, F: Field> Div<&'a DensePolynomial> for &'b DensePolynomial { } } +impl<'a, 'b, F: Field> Mul for &'b DensePolynomial { + type Output = DensePolynomial; + + #[inline] + fn mul(self, elem: F) -> DensePolynomial { + if self.is_zero() || elem.is_zero() { + DensePolynomial::zero() + } else { + let mut result = self.clone(); + cfg_iter_mut!(result).for_each(|e| { + *e *= elem; + }); + result + } + } +} + /// Performs O(nlogn) multiplication of polynomials if F is smooth. impl<'a, 'b, F: FftField> Mul<&'a DensePolynomial> for &'b DensePolynomial { type Output = DensePolynomial; @@ -434,7 +513,7 @@ mod tests { use crate::polynomial::univariate::*; use crate::{EvaluationDomain, GeneralEvaluationDomain}; use ark_ff::{Field, One, UniformRand, Zero}; - use ark_std::test_rng; + use ark_std::{rand::Rng, test_rng}; use ark_test_curves::bls12_381::Fr; #[test] @@ -484,16 +563,40 @@ mod tests { #[test] fn sub_polynomials() { let rng = &mut test_rng(); - let p1 = DensePolynomial::::rand(5, rng); - let p2 = DensePolynomial::::rand(3, rng); - let res1 = &p1 - &p2; - let res2 = &p2 - &p1; - assert_eq!( - &res1 + &p2, - p1, - "Subtraction should be inverse of addition!" - ); - assert_eq!(res1, -res2, "p2 - p1 = -(p1 - p2)"); + for a_degree in 0..70 { + for b_degree in 0..70 { + let p1 = DensePolynomial::::rand(a_degree, rng); + let p2 = DensePolynomial::::rand(b_degree, rng); + let res1 = &p1 - &p2; + let res2 = &p2 - &p1; + assert_eq!(&res1 + &p2, p1); + assert_eq!(res1, -res2); + } + } + } + + fn rand_sparse_poly(degree: usize, rng: &mut R) -> SparsePolynomial { + // Initialize coeffs so that its guaranteed to have a x^{degree} term + let mut coeffs = vec![(degree, Fr::rand(rng))]; + for i in 0..degree { + if !rng.gen_bool(0.8) { + coeffs.push((i, Fr::rand(rng))); + } + } + SparsePolynomial::from_coefficients_vec(coeffs) + } + + #[test] + fn sub_sparse_polynomials() { + let rng = &mut test_rng(); + for a_degree in 0..70 { + for b_degree in 0..70 { + let p1 = DensePolynomial::::rand(a_degree, rng); + let p2 = rand_sparse_poly(b_degree, rng); + let res = &p1 - &p2; + assert_eq!(res, &p1 - &Into::>::into(p2)); + } + } } #[test] @@ -566,6 +669,19 @@ mod tests { } } + #[test] + fn mul_random_element() { + let rng = &mut test_rng(); + for degree in 0..70 { + let a = DensePolynomial::::rand(degree, rng); + let e = Fr::rand(rng); + assert_eq!( + &a * e, + a.naive_mul(&DensePolynomial::from_coefficients_slice(&[e])) + ) + } + } + #[test] fn mul_polynomials_random() { let rng = &mut test_rng(); diff --git a/poly/src/polynomial/univariate/sparse.rs b/poly/src/polynomial/univariate/sparse.rs index 1b72bce1e..afe00cbff 100644 --- a/poly/src/polynomial/univariate/sparse.rs +++ b/poly/src/polynomial/univariate/sparse.rs @@ -8,10 +8,13 @@ use ark_std::{ collections::BTreeMap, fmt, io::{Read, Write}, - ops::{Add, AddAssign, Neg, SubAssign}, + ops::{Add, AddAssign, Mul, Neg, SubAssign}, vec::Vec, }; +#[cfg(feature = "parallel")] +use rayon::prelude::*; + /// Stores a sparse polynomial in coefficient form. #[derive(Clone, PartialEq, Eq, Hash, Default, CanonicalSerialize, CanonicalDeserialize)] pub struct SparsePolynomial { @@ -44,6 +47,12 @@ impl core::ops::Deref for SparsePolynomial { } } +impl core::ops::DerefMut for SparsePolynomial { + fn deref_mut(&mut self) -> &mut [(usize, F)] { + &mut self.coeffs + } +} + impl Polynomial for SparsePolynomial { type Point = F; @@ -186,6 +195,23 @@ impl<'a, 'b, F: Field> SubAssign<&'a SparsePolynomial> for SparsePolynomial Mul for &'b SparsePolynomial { + type Output = SparsePolynomial; + + #[inline] + fn mul(self, elem: F) -> SparsePolynomial { + if self.is_zero() || elem.is_zero() { + SparsePolynomial::zero() + } else { + let mut result = self.clone(); + cfg_iter_mut!(result).for_each(|e| { + (*e).1 *= elem; + }); + result + } + } +} + impl Zero for SparsePolynomial { /// Returns the zero polynomial. fn zero() -> Self { @@ -384,6 +410,19 @@ mod tests { } } + #[test] + fn mul_random_element() { + let rng = &mut test_rng(); + for degree in 0..20 { + let a = rand_sparse_poly(degree, rng); + let e = Fr::rand(rng); + assert_eq!( + &a * e, + a.mul(&SparsePolynomial::from_coefficients_slice(&[(0, e)])) + ) + } + } + #[test] fn mul_polynomial() { // Test multiplying polynomials over their domains, and over the native representation. From b86539b21153e4820511fe4cb040e49171905194 Mon Sep 17 00:00:00 2001 From: Ryan Lehmkuhl Date: Sat, 3 Apr 2021 21:55:13 -0700 Subject: [PATCH 2/6] Add test and update changelog --- CHANGELOG.md | 1 + poly/src/polynomial/univariate/dense.rs | 35 +++++++++++++++++-------- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dcee9e16b..db03eb011 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - [\#230](https://github.com/arkworks-rs/algebra/pull/230) (ark-ec) Add `wnaf_mul` implementation for `ProjectiveCurve`. - [\#258](https://github.com/arkworks-rs/algebra/pull/258) (ark-poly) Add `Mul` implementation for `DensePolynomial` +- [\#259](https://github.com/arkworks-rs/algebra/pull/259) (ark-poly) Add `Mul` implementation for `SparsePolynomial` and `Add>/Sub>` for `DensePolynomial` ### Improvements diff --git a/poly/src/polynomial/univariate/dense.rs b/poly/src/polynomial/univariate/dense.rs index 5e205625e..8521e0fb6 100644 --- a/poly/src/polynomial/univariate/dense.rs +++ b/poly/src/polynomial/univariate/dense.rs @@ -516,6 +516,17 @@ mod tests { use ark_std::{rand::Rng, test_rng}; use ark_test_curves::bls12_381::Fr; + fn rand_sparse_poly(degree: usize, rng: &mut R) -> SparsePolynomial { + // Initialize coeffs so that its guaranteed to have a x^{degree} term + let mut coeffs = vec![(degree, Fr::rand(rng))]; + for i in 0..degree { + if !rng.gen_bool(0.8) { + coeffs.push((i, Fr::rand(rng))); + } + } + SparsePolynomial::from_coefficients_vec(coeffs) + } + #[test] fn double_polynomials_random() { let rng = &mut test_rng(); @@ -541,6 +552,19 @@ mod tests { } } + #[test] + fn add_sparse_polynomials() { + let rng = &mut test_rng(); + for a_degree in 0..70 { + for b_degree in 0..70 { + let p1 = DensePolynomial::::rand(a_degree, rng); + let p2 = rand_sparse_poly(b_degree, rng); + let res = &p1 + &p2; + assert_eq!(res, &p1 + &Into::>::into(p2)); + } + } + } + #[test] fn add_polynomials_with_mul() { let rng = &mut test_rng(); @@ -575,17 +599,6 @@ mod tests { } } - fn rand_sparse_poly(degree: usize, rng: &mut R) -> SparsePolynomial { - // Initialize coeffs so that its guaranteed to have a x^{degree} term - let mut coeffs = vec![(degree, Fr::rand(rng))]; - for i in 0..degree { - if !rng.gen_bool(0.8) { - coeffs.push((i, Fr::rand(rng))); - } - } - SparsePolynomial::from_coefficients_vec(coeffs) - } - #[test] fn sub_sparse_polynomials() { let rng = &mut test_rng(); From 772cc8054d552296f61edbbfaea36d29d304e61a Mon Sep 17 00:00:00 2001 From: Ryan Lehmkuhl Date: Sat, 3 Apr 2021 21:58:47 -0700 Subject: [PATCH 3/6] Fmt --- poly/src/polynomial/univariate/dense.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/poly/src/polynomial/univariate/dense.rs b/poly/src/polynomial/univariate/dense.rs index 8521e0fb6..bab620fe6 100644 --- a/poly/src/polynomial/univariate/dense.rs +++ b/poly/src/polynomial/univariate/dense.rs @@ -2,14 +2,14 @@ use crate::univariate::DenseOrSparsePolynomial; use crate::{univariate::SparsePolynomial, Polynomial, UVPolynomial}; use crate::{EvaluationDomain, Evaluations, GeneralEvaluationDomain}; +use ark_ff::{FftField, Field, Zero}; use ark_serialize::*; +use ark_std::rand::Rng; use ark_std::{ fmt, ops::{Add, AddAssign, Deref, DerefMut, Div, Mul, Neg, Sub, SubAssign}, vec::Vec, }; -use ark_ff::{FftField, Field, Zero}; -use ark_std::rand::Rng; #[cfg(feature = "parallel")] use ark_std::cmp::max; From b706a4dd939132fc015ef14801287c547a1be052 Mon Sep 17 00:00:00 2001 From: Weikeng Chen Date: Sat, 3 Apr 2021 23:01:19 -0700 Subject: [PATCH 4/6] Update CHANGELOG.md --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index db03eb011..e5f5d6c25 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,8 +5,8 @@ ### Features - [\#230](https://github.com/arkworks-rs/algebra/pull/230) (ark-ec) Add `wnaf_mul` implementation for `ProjectiveCurve`. -- [\#258](https://github.com/arkworks-rs/algebra/pull/258) (ark-poly) Add `Mul` implementation for `DensePolynomial` -- [\#259](https://github.com/arkworks-rs/algebra/pull/259) (ark-poly) Add `Mul` implementation for `SparsePolynomial` and `Add>/Sub>` for `DensePolynomial` +- [\#258](https://github.com/arkworks-rs/algebra/pull/258) (ark-poly) Add `Mul` implementation for `DensePolynomial`. +- [\#259](https://github.com/arkworks-rs/algebra/pull/259) (ark-poly) Add `Mul` implementation for `SparsePolynomial` and `Add>/Sub>` for `DensePolynomial`. ### Improvements From 05dd8db47d496295f32c41e76405f4bbb5bb9c71 Mon Sep 17 00:00:00 2001 From: weikeng Date: Thu, 13 May 2021 23:16:08 -0700 Subject: [PATCH 5/6] add add_assign and sub_assign --- poly/src/polynomial/univariate/dense.rs | 95 ++++++++++++++++++++++++ poly/src/polynomial/univariate/sparse.rs | 6 +- 2 files changed, 98 insertions(+), 3 deletions(-) diff --git a/poly/src/polynomial/univariate/dense.rs b/poly/src/polynomial/univariate/dense.rs index bab620fe6..eaf9246fe 100644 --- a/poly/src/polynomial/univariate/dense.rs +++ b/poly/src/polynomial/univariate/dense.rs @@ -346,6 +346,39 @@ impl<'a, 'b, F: Field> AddAssign<(F, &'a DensePolynomial)> for DensePolynomia } } + +impl<'a, F: Field> AddAssign<&'a SparsePolynomial> for DensePolynomial { + #[inline] + fn add_assign(&mut self, other: &'a SparsePolynomial) { + if self.is_zero() { + self.coeffs.truncate(0); + self.coeffs.resize(other.degree() + 1, F::zero()); + + for (i, coeff) in other.iter() { + self.coeffs[*i] = *coeff; + } + return; + } else if other.is_zero() { + return; + } else { + // If `other` has higher degree than `self`, create a dense vector + // storing the upper coefficients of the addition + let mut upper_coeffs = match other.degree() > self.degree() { + true => vec![F::zero(); other.degree() - self.degree()], + false => Vec::new(), + }; + for (pow, coeff) in other.iter() { + if *pow <= self.degree() { + self.coeffs[*pow] += coeff; + } else { + upper_coeffs[*pow - self.degree() - 1] = *coeff; + } + } + self.coeffs.extend(upper_coeffs); + } + } +} + impl Neg for DensePolynomial { type Output = DensePolynomial; @@ -449,6 +482,38 @@ impl<'a, 'b, F: Field> SubAssign<&'a DensePolynomial> for DensePolynomial } } +impl<'a, F: Field> SubAssign<&'a SparsePolynomial> for DensePolynomial { + #[inline] + fn sub_assign(&mut self, other: &'a SparsePolynomial) { + if self.is_zero() { + self.coeffs.truncate(0); + self.coeffs.resize(other.degree() + 1, F::zero()); + + for (i, coeff) in other.iter() { + self.coeffs[*i] = (*coeff).neg(); + } + return; + } else if other.is_zero() { + return; + } else { + // If `other` has higher degree than `self`, create a dense vector + // storing the upper coefficients of the subtraction + let mut upper_coeffs = match other.degree() > self.degree() { + true => vec![F::zero(); other.degree() - self.degree()], + false => Vec::new(), + }; + for (pow, coeff) in other.iter() { + if *pow <= self.degree() { + self.coeffs[*pow] -= coeff; + } else { + upper_coeffs[*pow - self.degree() - 1] = -*coeff; + } + } + self.coeffs.extend(upper_coeffs); + } + } +} + impl<'a, 'b, F: Field> Div<&'a DensePolynomial> for &'b DensePolynomial { type Output = DensePolynomial; @@ -565,6 +630,21 @@ mod tests { } } + #[test] + fn add_assign_sparse_polynomials() { + let rng = &mut test_rng(); + for a_degree in 0..70 { + for b_degree in 0..70 { + let p1 = DensePolynomial::::rand(a_degree, rng); + let p2 = rand_sparse_poly(b_degree, rng); + + let mut res = p1.clone(); + res += &p2; + assert_eq!(res, &p1 + &Into::>::into(p2)); + } + } + } + #[test] fn add_polynomials_with_mul() { let rng = &mut test_rng(); @@ -612,6 +692,21 @@ mod tests { } } + #[test] + fn sub_assign_sparse_polynomials() { + let rng = &mut test_rng(); + for a_degree in 0..70 { + for b_degree in 0..70 { + let p1 = DensePolynomial::::rand(a_degree, rng); + let p2 = rand_sparse_poly(b_degree, rng); + + let mut res = p1.clone(); + res -= &p2; + assert_eq!(res, &p1 - &Into::>::into(p2)); + } + } + } + #[test] fn polynomial_additive_identity() { // Test adding polynomials with its negative equals 0 diff --git a/poly/src/polynomial/univariate/sparse.rs b/poly/src/polynomial/univariate/sparse.rs index afe00cbff..3e68dd320 100644 --- a/poly/src/polynomial/univariate/sparse.rs +++ b/poly/src/polynomial/univariate/sparse.rs @@ -8,7 +8,7 @@ use ark_std::{ collections::BTreeMap, fmt, io::{Read, Write}, - ops::{Add, AddAssign, Mul, Neg, SubAssign}, + ops::{Add, AddAssign, Deref, DerefMut, Mul, Neg, SubAssign}, vec::Vec, }; @@ -39,7 +39,7 @@ impl fmt::Debug for SparsePolynomial { } } -impl core::ops::Deref for SparsePolynomial { +impl Deref for SparsePolynomial { type Target = [(usize, F)]; fn deref(&self) -> &[(usize, F)] { @@ -47,7 +47,7 @@ impl core::ops::Deref for SparsePolynomial { } } -impl core::ops::DerefMut for SparsePolynomial { +impl DerefMut for SparsePolynomial { fn deref_mut(&mut self) -> &mut [(usize, F)] { &mut self.coeffs } From f656583e312a651380ee7d88420e702c02644214 Mon Sep 17 00:00:00 2001 From: weikeng Date: Thu, 13 May 2021 23:17:31 -0700 Subject: [PATCH 6/6] fmt --- poly/src/polynomial/univariate/dense.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/poly/src/polynomial/univariate/dense.rs b/poly/src/polynomial/univariate/dense.rs index eaf9246fe..f49671745 100644 --- a/poly/src/polynomial/univariate/dense.rs +++ b/poly/src/polynomial/univariate/dense.rs @@ -346,7 +346,6 @@ impl<'a, 'b, F: Field> AddAssign<(F, &'a DensePolynomial)> for DensePolynomia } } - impl<'a, F: Field> AddAssign<&'a SparsePolynomial> for DensePolynomial { #[inline] fn add_assign(&mut self, other: &'a SparsePolynomial) {