1use crate::{
3 polynomial::Polynomial,
4 univariate::{DenseOrSparsePolynomial, DensePolynomial},
5 DenseUVPolynomial, EvaluationDomain, Evaluations,
6};
7use ark_ff::{FftField, Field, Zero};
8use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
9use ark_std::{
10 cfg_iter_mut,
11 cmp::Ordering,
12 collections::BTreeMap,
13 fmt,
14 ops::{Add, AddAssign, Deref, DerefMut, Mul, Neg, SubAssign},
15 vec,
16 vec::*,
17};
18
19#[cfg(feature = "parallel")]
20use rayon::prelude::*;
21
22#[derive(Clone, PartialEq, Eq, Hash, Default, CanonicalSerialize, CanonicalDeserialize)]
24pub struct SparsePolynomial<F: Field> {
25 pub coeffs: Vec<(usize, F)>,
29}
30
31impl<F: Field> fmt::Debug for SparsePolynomial<F> {
32 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
33 for (i, coeff) in self.coeffs.iter().filter(|(_, c)| !c.is_zero()) {
34 if *i == 0 {
35 write!(f, "\n{coeff:?}")?;
36 } else if *i == 1 {
37 write!(f, " + \n{coeff:?} * x")?;
38 } else {
39 write!(f, " + \n{coeff:?} * x^{i}")?;
40 }
41 }
42 Ok(())
43 }
44}
45
46impl<F: Field> Deref for SparsePolynomial<F> {
47 type Target = [(usize, F)];
48
49 fn deref(&self) -> &[(usize, F)] {
50 &self.coeffs
51 }
52}
53
54impl<F: Field> DerefMut for SparsePolynomial<F> {
55 fn deref_mut(&mut self) -> &mut [(usize, F)] {
56 &mut self.coeffs
57 }
58}
59
60impl<F: Field> Polynomial<F> for SparsePolynomial<F> {
61 type Point = F;
62
63 fn degree(&self) -> usize {
65 if self.is_zero() {
66 0
67 } else {
68 assert!(self.coeffs.last().is_some_and(|(_, c)| !c.is_zero()));
69 self.coeffs.last().unwrap().0
70 }
71 }
72
73 fn evaluate(&self, point: &F) -> F {
75 if self.is_zero() {
76 return F::zero();
77 }
78
79 let num_powers = 0usize.leading_zeros() - self.degree().leading_zeros();
81 let mut powers_of_2 = Vec::with_capacity(num_powers as usize);
82
83 let mut p = *point;
84 powers_of_2.push(p);
85 for _ in 1..num_powers {
86 p.square_in_place();
87 powers_of_2.push(p);
88 }
89 let total = self
91 .coeffs
92 .iter()
93 .map(|(i, c)| {
94 debug_assert_eq!(
95 F::pow_with_table(&powers_of_2[..], [*i as u64]).unwrap(),
96 point.pow([*i as u64]),
97 "pows not equal"
98 );
99 *c * F::pow_with_table(&powers_of_2[..], [*i as u64]).unwrap()
100 })
101 .sum();
102 total
103 }
104}
105
106impl<F: Field> Add for SparsePolynomial<F> {
107 type Output = Self;
108
109 fn add(self, other: Self) -> Self {
110 &self + &other
111 }
112}
113
114impl<'a, F: Field> Add<&'a SparsePolynomial<F>> for &SparsePolynomial<F> {
115 type Output = SparsePolynomial<F>;
116
117 fn add(self, other: &'a SparsePolynomial<F>) -> SparsePolynomial<F> {
118 if self.is_zero() {
119 return other.clone();
120 } else if other.is_zero() {
121 return self.clone();
122 }
123 let mut result = SparsePolynomial::<F>::zero();
125 let mut self_index = 0;
127 let mut other_index = 0;
128 loop {
129 if self_index == self.coeffs.len() && other_index == other.coeffs.len() {
132 return result;
133 } else if self_index == self.coeffs.len() {
134 result.append_coeffs(&other.coeffs[other_index..]);
135 return result;
136 } else if other_index == other.coeffs.len() {
137 result.append_coeffs(&self.coeffs[self_index..]);
138 return result;
139 }
140
141 let (self_term_degree, self_term_coeff) = self.coeffs[self_index];
143 let (other_term_degree, other_term_coeff) = other.coeffs[other_index];
144 match self_term_degree.cmp(&other_term_degree) {
146 Ordering::Less => {
147 result.coeffs.push((self_term_degree, self_term_coeff));
148 self_index += 1;
149 },
150 Ordering::Equal => {
151 let term_sum = self_term_coeff + other_term_coeff;
152 if !term_sum.is_zero() {
153 result.coeffs.push((self_term_degree, term_sum));
154 }
155 self_index += 1;
156 other_index += 1;
157 },
158 Ordering::Greater => {
159 result.coeffs.push((other_term_degree, other_term_coeff));
160 other_index += 1;
161 },
162 }
163 }
164 }
165}
166
167impl<'a, F: Field> AddAssign<&'a Self> for SparsePolynomial<F> {
168 fn add_assign(&mut self, other: &'a Self) {
170 self.coeffs = (self.clone() + other.clone()).coeffs;
171 }
172}
173
174impl<'a, F: Field> AddAssign<(F, &'a Self)> for SparsePolynomial<F> {
175 fn add_assign(&mut self, (f, other): (F, &'a Self)) {
177 self.coeffs = (self.clone() + other.clone()).coeffs;
178 for i in 0..self.coeffs.len() {
179 self.coeffs[i].1 *= f;
180 }
181 }
182}
183
184impl<F: Field> Neg for SparsePolynomial<F> {
185 type Output = Self;
186
187 #[inline]
188 fn neg(mut self) -> Self {
189 for (_, coeff) in &mut self.coeffs {
190 *coeff = -*coeff;
191 }
192 self
193 }
194}
195
196impl<'a, F: Field> SubAssign<&'a Self> for SparsePolynomial<F> {
197 #[inline]
199 fn sub_assign(&mut self, other: &'a Self) {
200 let self_copy = -self.clone();
201 self.coeffs = (self_copy + other.clone()).coeffs;
202 }
203}
204
205impl<F: Field> Mul<F> for &SparsePolynomial<F> {
206 type Output = SparsePolynomial<F>;
207
208 #[inline]
209 fn mul(self, elem: F) -> SparsePolynomial<F> {
210 if self.is_zero() || elem.is_zero() {
211 SparsePolynomial::zero()
212 } else {
213 let mut result = self.clone();
214 cfg_iter_mut!(result).for_each(|e| {
215 e.1 *= elem;
216 });
217 result
218 }
219 }
220}
221
222impl<F: Field> Zero for SparsePolynomial<F> {
223 fn zero() -> Self {
225 Self { coeffs: Vec::new() }
226 }
227
228 fn is_zero(&self) -> bool {
230 self.coeffs.is_empty() || self.coeffs.iter().all(|(_, c)| c.is_zero())
231 }
232}
233
234impl<F: Field> SparsePolynomial<F> {
235 pub fn from_coefficients_slice(coeffs: &[(usize, F)]) -> Self {
237 Self::from_coefficients_vec(coeffs.to_vec())
238 }
239
240 pub fn from_coefficients_vec(mut coeffs: Vec<(usize, F)>) -> Self {
244 while coeffs.last().is_some_and(|(_, c)| c.is_zero()) {
246 coeffs.pop();
247 }
248 coeffs.sort_by(|(c1, _), (c2, _)| c1.cmp(c2));
250 assert!(coeffs.last().map_or(true, |(_, c)| !c.is_zero()));
253
254 Self { coeffs }
255 }
256
257 pub fn mul(&self, other: &Self) -> Self {
259 if self.is_zero() || other.is_zero() {
260 Self::zero()
261 } else {
262 let mut result = BTreeMap::new();
263 for (i, self_coeff) in &self.coeffs {
264 for (j, other_coeff) in &other.coeffs {
265 result
266 .entry(i + j)
267 .and_modify(|cur_coeff| *cur_coeff += *self_coeff * other_coeff)
268 .or_insert_with(|| *self_coeff * other_coeff);
269 }
270 }
271 Self::from_coefficients_vec(result.into_iter().collect())
272 }
273 }
274
275 pub fn div(&self, other: &Self) -> DensePolynomial<F> {
277 let dividend: DenseOrSparsePolynomial<'_, F> = self.into();
278 let divisor: DenseOrSparsePolynomial<'_, F> = other.into();
279
280 dividend.naive_div(&divisor).expect("division failed").0
281 }
282
283 fn append_coeffs(&mut self, append_coeffs: &[(usize, F)]) {
287 assert!(append_coeffs.is_empty() || self.degree() < append_coeffs[0].0);
288 self.coeffs.extend_from_slice(append_coeffs);
289 }
290}
291
292impl<F: FftField> SparsePolynomial<F> {
293 pub fn evaluate_over_domain_by_ref<D: EvaluationDomain<F>>(
295 &self,
296 domain: D,
297 ) -> Evaluations<F, D> {
298 let poly: DenseOrSparsePolynomial<'_, F> = self.into();
299 DenseOrSparsePolynomial::evaluate_over_domain(poly, domain)
300 }
301
302 pub fn evaluate_over_domain<D: EvaluationDomain<F>>(self, domain: D) -> Evaluations<F, D> {
304 let poly: DenseOrSparsePolynomial<'_, F> = self.into();
305 DenseOrSparsePolynomial::evaluate_over_domain(poly, domain)
306 }
307}
308
309impl<F: Field> From<SparsePolynomial<F>> for DensePolynomial<F> {
310 fn from(other: SparsePolynomial<F>) -> Self {
311 let mut result = vec![F::zero(); other.degree() + 1];
312 for (i, coeff) in other.coeffs {
313 result[i] = coeff;
314 }
315 Self::from_coefficients_vec(result)
316 }
317}
318
319impl<F: Field> From<DensePolynomial<F>> for SparsePolynomial<F> {
320 fn from(dense_poly: DensePolynomial<F>) -> Self {
321 Self::from_coefficients_vec(
322 dense_poly
323 .coeffs()
324 .iter()
325 .enumerate()
326 .filter(|&(_, coeff)| !coeff.is_zero())
327 .map(|(i, coeff)| (i, *coeff))
328 .collect(),
329 )
330 }
331}
332
333#[cfg(test)]
334mod tests {
335 use crate::{
336 polynomial::Polynomial,
337 univariate::{DensePolynomial, SparsePolynomial},
338 EvaluationDomain, GeneralEvaluationDomain,
339 };
340 use ark_ff::{UniformRand, Zero};
341 use ark_std::{cmp::max, ops::Mul, rand::Rng, test_rng, vec};
342 use ark_test_curves::bls12_381::Fr;
343
344 const ZERO_COEFF_PROBABILITY: f64 = 0.8f64;
346
347 fn rand_sparse_poly<R: Rng>(degree: usize, rng: &mut R) -> SparsePolynomial<Fr> {
348 let mut coeffs = vec![(degree, Fr::rand(rng))];
350 for i in 0..degree {
351 if !rng.gen_bool(ZERO_COEFF_PROBABILITY) {
352 coeffs.push((i, Fr::rand(rng)));
353 }
354 }
355 SparsePolynomial::from_coefficients_vec(coeffs)
356 }
357
358 #[test]
359 fn evaluate_at_point() {
360 let mut rng = test_rng();
361 for degree in 0..60 {
363 let sparse_poly = rand_sparse_poly(degree, &mut rng);
364 let dense_poly: DensePolynomial<Fr> = sparse_poly.clone().into();
365 let pt = Fr::rand(&mut rng);
366 assert_eq!(sparse_poly.evaluate(&pt), dense_poly.evaluate(&pt));
367 }
368 }
369
370 #[test]
371 fn add_polynomial() {
372 let mut rng = test_rng();
374 for degree_a in 0..20 {
375 let sparse_poly_a = rand_sparse_poly(degree_a, &mut rng);
376 let dense_poly_a: DensePolynomial<Fr> = sparse_poly_a.clone().into();
377 for degree_b in 0..20 {
378 let sparse_poly_b = rand_sparse_poly(degree_b, &mut rng);
379 let dense_poly_b: DensePolynomial<Fr> = sparse_poly_b.clone().into();
380
381 let sparse_sum = sparse_poly_a.clone() + sparse_poly_b.clone();
383 assert_eq!(
384 sparse_sum.degree(),
385 max(degree_a, degree_b),
386 "degree_a = {}, degree_b = {}",
387 degree_a,
388 degree_b
389 );
390 let actual_dense_sum: DensePolynomial<Fr> = sparse_sum.into();
391 let expected_dense_sum = dense_poly_a.clone() + dense_poly_b;
392 assert_eq!(
393 actual_dense_sum, expected_dense_sum,
394 "degree_a = {}, degree_b = {}",
395 degree_a, degree_b
396 );
397 let mut sparse_add_assign_sum = sparse_poly_a.clone();
399 sparse_add_assign_sum += &sparse_poly_b;
400 let actual_add_assign_dense_sum: DensePolynomial<Fr> = sparse_add_assign_sum.into();
401 assert_eq!(
402 actual_add_assign_dense_sum, expected_dense_sum,
403 "degree_a = {}, degree_b = {}",
404 degree_a, degree_b
405 );
406 }
407 }
408 }
409
410 #[test]
411 fn polynomial_additive_identity() {
412 let mut rng = test_rng();
414 for degree in 0..70 {
415 let sparse_poly = rand_sparse_poly(degree, &mut rng);
417 let neg = -sparse_poly.clone();
418 assert!((sparse_poly + neg).is_zero());
419
420 let sparse_poly = rand_sparse_poly(degree, &mut rng);
422 let mut result = sparse_poly.clone();
423 result -= &sparse_poly;
424 assert!(result.is_zero());
425 }
426 }
427
428 #[test]
429 fn mul_random_element() {
430 let rng = &mut test_rng();
431 for degree in 0..20 {
432 let a = rand_sparse_poly(degree, rng);
433 let e = Fr::rand(rng);
434 assert_eq!(
435 &a * e,
436 a.mul(&SparsePolynomial::from_coefficients_slice(&[(0, e)]))
437 )
438 }
439 }
440
441 #[test]
442 fn mul_polynomial() {
443 let mut rng = test_rng();
447 for degree_a in 0..20 {
448 let sparse_poly_a = rand_sparse_poly(degree_a, &mut rng);
449 let dense_poly_a: DensePolynomial<Fr> = sparse_poly_a.clone().into();
450 for degree_b in 0..20 {
451 let sparse_poly_b = rand_sparse_poly(degree_b, &mut rng);
452 let dense_poly_b: DensePolynomial<Fr> = sparse_poly_b.clone().into();
453
454 let sparse_prod = sparse_poly_a.mul(&sparse_poly_b);
456 assert_eq!(
457 sparse_prod.degree(),
458 degree_a + degree_b,
459 "degree_a = {}, degree_b = {}",
460 degree_a,
461 degree_b
462 );
463 let dense_prod = dense_poly_a.naive_mul(&dense_poly_b);
464 assert_eq!(sparse_prod.degree(), dense_prod.degree());
465 assert_eq!(
466 sparse_prod,
467 SparsePolynomial::<Fr>::from(dense_prod),
468 "degree_a = {}, degree_b = {}",
469 degree_a,
470 degree_b
471 );
472
473 let domain = GeneralEvaluationDomain::new(sparse_prod.degree() + 1).unwrap();
475 let poly_a_evals = sparse_poly_a.evaluate_over_domain_by_ref(domain);
476 let poly_b_evals = sparse_poly_b.evaluate_over_domain_by_ref(domain);
477 let poly_prod_evals = sparse_prod.evaluate_over_domain_by_ref(domain);
478 assert_eq!(poly_a_evals.mul(&poly_b_evals), poly_prod_evals);
479 }
480 }
481 }
482
483 #[test]
484 fn div_polynomial() {
485 let mut rng = test_rng();
486 for degree_a in 0..10 {
487 let sparse_poly_a = rand_sparse_poly(degree_a, &mut rng);
488 let dense_poly_a: DensePolynomial<Fr> = sparse_poly_a.clone().into();
489 for degree_b in 0..degree_a {
490 let sparse_poly_b = rand_sparse_poly(degree_b, &mut rng);
491 let dense_poly_b: DensePolynomial<Fr> = sparse_poly_b.clone().into();
492
493 let sparse_quotient = sparse_poly_a.div(&sparse_poly_b);
495 assert_eq!(
496 sparse_quotient.degree(),
497 degree_a - degree_b,
498 "degree_a = {}, degree_b = {}, degree_q = {}",
499 degree_a - degree_b,
500 degree_b,
501 sparse_quotient.degree(),
502 );
503
504 let dense_quotient = &dense_poly_a / &dense_poly_b;
508 assert_eq!(sparse_quotient.degree(), dense_quotient.degree());
509 assert_eq!(
510 sparse_quotient,
511 dense_quotient,
512 "sparse quotient different from dense quotient for the same division! sparse {:?}, dense {:?}",
513 sparse_quotient,
514 dense_quotient,
515 );
516 }
517 }
518 }
519
520 #[test]
521 fn evaluate_over_domain() {
522 let mut rng = test_rng();
525 for poly_degree_dim in 0..5 {
526 let poly_degree = (1 << poly_degree_dim) - 1;
527 let sparse_poly = rand_sparse_poly(poly_degree, &mut rng);
528
529 for domain_dim in poly_degree_dim..(poly_degree_dim + 2) {
530 let domain_size = 1 << domain_dim;
531 let domain = GeneralEvaluationDomain::new(domain_size).unwrap();
532
533 let sparse_evals = sparse_poly.evaluate_over_domain_by_ref(domain);
534
535 let dense_poly: DensePolynomial<Fr> = sparse_poly.clone().into();
537 let dense_evals = dense_poly.clone().evaluate_over_domain(domain);
538 assert_eq!(
539 sparse_evals.clone().interpolate(),
540 dense_evals.clone().interpolate(),
541 "poly_degree_dim = {}, domain_dim = {}",
542 poly_degree_dim,
543 domain_dim
544 );
545 assert_eq!(
546 sparse_evals.interpolate(),
547 dense_poly,
548 "poly_degree_dim = {}, domain_dim = {}",
549 poly_degree_dim,
550 domain_dim
551 );
552 assert_eq!(
554 dense_evals.interpolate(),
555 dense_poly,
556 "poly_degree_dim = {}, domain_dim = {}",
557 poly_degree_dim,
558 domain_dim
559 );
560 }
561 }
562 }
563
564 #[test]
565 fn evaluate_over_small_domain() {
566 let mut rng = test_rng();
569 for poly_degree_dim in 1..5 {
570 let poly_degree = (1 << poly_degree_dim) - 1;
571 let sparse_poly = rand_sparse_poly(poly_degree, &mut rng);
572
573 for domain_dim in 0..poly_degree_dim {
574 let domain_size = 1 << domain_dim;
575 let domain = GeneralEvaluationDomain::new(domain_size).unwrap();
576
577 let sparse_evals = sparse_poly.evaluate_over_domain_by_ref(domain);
578
579 let dense_poly: DensePolynomial<Fr> = sparse_poly.clone().into();
581 let dense_evals = dense_poly.clone().evaluate_over_domain(domain);
582 assert_eq!(
583 sparse_evals, dense_evals,
584 "poly_degree_dim = {}, domain_dim = {}",
585 poly_degree_dim, domain_dim
586 );
587
588 let (_q, r) = (dense_poly.clone() + -sparse_evals.interpolate())
590 .divide_by_vanishing_poly(domain);
591 assert_eq!(
592 r,
593 DensePolynomial::<Fr>::zero(),
594 "poly_degree_dim = {}, domain_dim = {}",
595 poly_degree_dim,
596 domain_dim
597 );
598
599 let (_q, r) = (dense_poly.clone() + -dense_evals.interpolate())
601 .divide_by_vanishing_poly(domain);
602 assert_eq!(
603 r,
604 DensePolynomial::<Fr>::zero(),
605 "poly_degree_dim = {}, domain_dim = {}",
606 poly_degree_dim,
607 domain_dim
608 );
609 }
610 }
611 }
612}