Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

poly: small refactor and unit tests for SparsePolynomial #868

Merged
merged 2 commits into from
Nov 12, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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"
);
Comment on lines 140 to 144
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assertion is moved to the beginning of the loop because we must check this at the start of the loop before doing any calculation (this allows us to panic earlier if necessary)

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");
}
}
Loading