Skip to content

Commit

Permalink
MVPoly: add is_multilinear
Browse files Browse the repository at this point in the history
  • Loading branch information
dannywillems committed Sep 9, 2024
1 parent 1a72f3f commit 3238381
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 0 deletions.
4 changes: 4 additions & 0 deletions mvpoly/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,4 +110,8 @@ pub trait MVPoly<F: PrimeField, const N: usize, const D: usize>:
) -> HashMap<usize, F>;

fn modify_monomial_with_scalar(&mut self, scalar: F);

/// Return true if the multi-variate polynomial is multilinear, i.e. if each
/// variable in each monomial is of maximum degree 1.
fn is_multilinear(&self) -> bool;
}
6 changes: 6 additions & 0 deletions mvpoly/src/monomials.rs
Original file line number Diff line number Diff line change
Expand Up @@ -568,6 +568,12 @@ impl<const N: usize, const D: usize, F: PrimeField> MVPoly<F, N, D> for Sparse<F
fn modify_monomial_with_scalar(&mut self, scalar: F) {
self.modify_monomial([0; N], scalar);
}

fn is_multilinear(&self) -> bool {
self.monomials
.iter()
.all(|(exponents, _)| exponents.iter().all(|&d| d <= 1))
}
}

impl<const N: usize, const D: usize, F: PrimeField> From<prime::Dense<F, N, D>>
Expand Down
20 changes: 20 additions & 0 deletions mvpoly/src/prime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,26 @@ impl<F: PrimeField, const N: usize, const D: usize> MVPoly<F, N, D> for Dense<F,
fn modify_monomial_with_scalar(&mut self, scalar: F) {
self[0] = scalar;
}

fn is_multilinear(&self) -> bool {
if self.is_zero() {
return true;
}
let normalized_indices = self.normalized_indices.clone();
let mut prime_gen = PrimeNumberGenerator::new();
normalized_indices
.iter()
.zip(self.coeff.iter())
.all(|(idx, c)| {
if c.is_zero() {
true
} else {
let decomposition_of_i = naive_prime_factors(*idx, &mut prime_gen);
// Each prime number/variable should appear at most once
decomposition_of_i.iter().all(|(_p, d)| *d <= 1)
}
})
}
}

impl<F: PrimeField, const N: usize, const D: usize> Dense<F, N, D> {
Expand Down
36 changes: 36 additions & 0 deletions mvpoly/tests/monomials.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use ark_ff::{Field, One, UniformRand, Zero};
use mina_curves::pasta::Fp;
use mvpoly::{monomials::Sparse, MVPoly};
use rand::{seq::SliceRandom, Rng};

#[test]
fn test_mul_by_one() {
Expand Down Expand Up @@ -498,3 +499,38 @@ fn test_mvpoly_compute_cross_terms_degree_seven() {
};
assert_eq!(lhs, rhs);
}

#[test]
fn test_is_multilinear() {
let mut rng = o1_utils::tests::make_test_rng(None);
let p1 = Sparse::<Fp, 6, 2>::zero();
assert!(p1.is_multilinear());

let c = Fp::rand(&mut rng);
let p2 = Sparse::<Fp, 6, 2>::from(c);
assert!(p2.is_multilinear());

{
let mut p = Sparse::<Fp, 6, 3>::zero();
let c = Fp::rand(&mut rng);
let idx = rng.gen_range(0..6);
let monomials_exponents = std::array::from_fn(|i| if i == idx { 1 } else { 0 });
p.add_monomial(monomials_exponents, c);
assert!(p.is_multilinear());
}

{
let mut p = Sparse::<Fp, 6, 4>::zero();
let c = Fp::rand(&mut rng);
let nb_var = rng.gen_range(0..4);
let mut monomials_exponents: [usize; 6] =
std::array::from_fn(|i| if i <= nb_var { 1 } else { 0 });
monomials_exponents.shuffle(&mut rng);
p.add_monomial(monomials_exponents, c);
assert!(p.is_multilinear());
}

// Very unlikely to have a random polynomial being multilinear
let p3 = unsafe { Sparse::<Fp, 6, 4>::random(&mut rng, None) };
assert!(!p3.is_multilinear());
}
38 changes: 38 additions & 0 deletions mvpoly/tests/prime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use kimchi::circuits::{
};
use mina_curves::pasta::Fp;
use mvpoly::{prime::Dense, utils::PrimeNumberGenerator, MVPoly};
use rand::{seq::SliceRandom, Rng};

#[test]
fn test_vector_space_dimension() {
Expand Down Expand Up @@ -736,3 +737,40 @@ fn test_add_monomial() {
random_c1 * random_eval[0] * random_eval[0] + random_c2 * random_eval[1] * random_eval[1];
assert_eq!(eval_p4, exp_eval_p4);
}

#[test]
fn test_is_multilinear() {
let mut rng = o1_utils::tests::make_test_rng(None);
let p1 = Dense::<Fp, 6, 2>::zero();
assert!(p1.is_multilinear());

let c = Fp::rand(&mut rng);
let p2 = Dense::<Fp, 6, 2>::from(c);
assert!(p2.is_multilinear());

{
let mut p = Dense::<Fp, 6, 3>::zero();
let c = Fp::rand(&mut rng);
let idx = rng.gen_range(0..6);
let monomials_exponents = std::array::from_fn(|i| if i == idx { 1 } else { 0 });
p.add_monomial(monomials_exponents, c);
assert!(p.is_multilinear());
}

{
let mut p = Dense::<Fp, 6, 4>::zero();
let c = Fp::rand(&mut rng);
let nb_var = rng.gen_range(0..4);
let mut monomials_exponents: [usize; 6] =
std::array::from_fn(|i| if i <= nb_var { 1 } else { 0 });
monomials_exponents.shuffle(&mut rng);
p.add_monomial(monomials_exponents, c);
assert!(p.is_multilinear());
}

// Very unlikely to have a random polynomial being multilinear
{
let p = unsafe { Dense::<Fp, 6, 4>::random(&mut rng, None) };
assert!(!p.is_multilinear());
}
}

0 comments on commit 3238381

Please sign in to comment.