Skip to content

Commit

Permalink
poly: small refactor and unit tests for SparsePolynomial (#868)
Browse files Browse the repository at this point in the history
* poly: small refactor and unit tests for SparsePolynomial

* Update poly/src/lib.rs
  • Loading branch information
tcoratger authored Nov 12, 2024
1 parent 6af88cb commit b6dc180
Showing 1 changed file with 115 additions and 14 deletions.
129 changes: 115 additions & 14 deletions poly/src/polynomial/multivariate/sparse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ impl<F: Field> Polynomial<F> for SparsePolynomial<F, SparseTerm> {
.iter()
.map(|(_, term)| term.degree())
.max()
.unwrap_or(0)
.unwrap_or_default()
}

/// Evaluates `self` at the given `point` in `Self::Point`.
Expand Down Expand Up @@ -136,19 +136,22 @@ impl<F: Field> DenseMVPolynomial<F> for SparsePolynomial<F, SparseTerm> {
terms.sort_by(|(_, t1), (_, t2)| t1.cmp(t2));
// If any terms are duplicated, add them together
let mut terms_dedup: Vec<(F, SparseTerm)> = Vec::new();
for term in terms {
if let Some(prev) = terms_dedup.last_mut() {
if prev.1 == term.1 {
*prev = (prev.0 + term.0, prev.1.clone());
continue;
}
};
for (coeff, term) in terms {
// Assert correct number of indeterminates
assert!(
term.1.iter().all(|(var, _)| *var < num_vars),
term.iter().all(|(var, _)| *var < num_vars),
"Invalid number of indeterminates"
);
terms_dedup.push(term);

if let Some((prev_coeff, prev_term)) = terms_dedup.last_mut() {
// If terms match, add the coefficients.
if prev_term == &term {
*prev_coeff += coeff;
continue;
}
}

terms_dedup.push((coeff, term));
}
let mut result = Self {
num_vars,
Expand Down Expand Up @@ -278,10 +281,7 @@ impl<F: Field, T: Term> fmt::Debug for SparsePolynomial<F, T> {
impl<F: Field, T: Term> Zero for SparsePolynomial<F, T> {
/// Returns the zero polynomial.
fn zero() -> Self {
Self {
num_vars: 0,
terms: Vec::new(),
}
Self::default()
}

/// Checks if the given polynomial is zero.
Expand Down Expand Up @@ -464,4 +464,105 @@ mod tests {
);
assert_eq!(expected, result);
}

#[test]
fn test_polynomial_with_zero_coefficients() {
let rng = &mut test_rng();
let max_degree = 10;
let p1 = rand_poly(3, max_degree, rng);

let p2 = SparsePolynomial::from_coefficients_vec(
3,
vec![
(Fr::zero(), SparseTerm::new(vec![(0, 1)])), // A zero coefficient term
(Fr::from(2), SparseTerm::new(vec![(1, 1)])),
],
);

let sum = &p1 + &p2;

// Ensure that the zero coefficient term is ignored in the evaluation.
let point = vec![Fr::from(1), Fr::from(2), Fr::from(3)];
let result = sum.evaluate(&point);

assert_eq!(result, p1.evaluate(&point) + p2.evaluate(&point)); // Should be the sum of the evaluations
}

#[test]
fn test_constant_polynomial() {
let constant_term = SparsePolynomial::from_coefficients_vec(
3,
vec![(Fr::from(5), SparseTerm::new(vec![]))],
);

let point = vec![Fr::from(1), Fr::from(2), Fr::from(3)];
assert_eq!(constant_term.evaluate(&point), Fr::from(5));
}

#[test]
fn test_polynomial_addition_with_overlapping_terms() {
let poly1 = SparsePolynomial::from_coefficients_vec(
3,
vec![
(Fr::from(2), SparseTerm::new(vec![(0, 1)])),
(Fr::from(3), SparseTerm::new(vec![(1, 1)])),
],
);

let poly2 = SparsePolynomial::from_coefficients_vec(
3,
vec![
(Fr::from(4), SparseTerm::new(vec![(0, 1)])),
(Fr::from(1), SparseTerm::new(vec![(2, 1)])),
],
);

let expected = SparsePolynomial::from_coefficients_vec(
3,
vec![
(Fr::from(6), SparseTerm::new(vec![(0, 1)])),
(Fr::from(3), SparseTerm::new(vec![(1, 1)])),
(Fr::from(1), SparseTerm::new(vec![(2, 1)])),
],
);

let result = &poly1 + &poly2;

assert_eq!(expected, result);
}

#[test]
fn test_polynomial_degree() {
// Polynomial: 2*x_0^3 + x_1*x_2
let poly1 = SparsePolynomial::<Fr, SparseTerm>::from_coefficients_vec(
3,
vec![
(Fr::from(2), SparseTerm::new(vec![(0, 3)])), // term with degree 3
(Fr::from(1), SparseTerm::new(vec![(1, 1), (2, 1)])), // term with degree 2
],
);

// Polynomial: x_0^2 + x_1^2 + 1
let poly2 = SparsePolynomial::<Fr, SparseTerm>::from_coefficients_vec(
3,
vec![
(Fr::from(1), SparseTerm::new(vec![(0, 2)])), // term with degree 2
(Fr::from(1), SparseTerm::new(vec![(1, 2)])), // term with degree 2
(Fr::from(1), SparseTerm::new(vec![])), // constant term (degree 0)
],
);

// Polynomial: 3
let poly3 = SparsePolynomial::<Fr, SparseTerm>::from_coefficients_vec(
3,
vec![
(Fr::from(3), SparseTerm::new(vec![])), // constant term (degree 0)
],
);

// Test the degree method
assert_eq!(poly1.degree(), 3, "Degree of poly1 should be 3");
assert_eq!(poly2.degree(), 2, "Degree of poly2 should be 2");
assert_eq!(poly3.degree(), 0, "Degree of poly3 should be 0");
}
}

0 comments on commit b6dc180

Please sign in to comment.