use crate::Field;
#[derive(Debug, Clone)]
pub struct MultilinearPolynomial {
num_vars: usize,
evals: Vec<u64>,
}
impl MultilinearPolynomial {
pub fn from_evaluations(num_vars: usize, evaluations: Vec<u64>) -> Self {
let expected_len = 1usize.checked_shl(num_vars as u32).unwrap_or(0);
assert_eq!(
evaluations.len(),
expected_len,
"expected 2^num_vars evaluations"
);
Self {
num_vars,
evals: evaluations,
}
}
pub fn num_vars(&self) -> usize {
self.num_vars
}
pub fn evaluations(&self) -> &[u64] {
&self.evals
}
pub fn evaluations_mod_p(&self, field: &Field) -> Vec<u64> {
self.evals.iter().map(|&v| v % field.modulus()).collect()
}
pub fn iter(&self) -> impl Iterator<Item = (usize, u64)> + '_ {
self.evals
.iter()
.enumerate()
.map(|(idx, &value)| (idx, value))
}
pub fn sum_over_hypercube(&self, field: &Field) -> u64 {
self.evals.iter().fold(0u64, |acc, &v| field.add(acc, v))
}
pub fn evaluate_boolean(&self, field: &Field, assignment: &[u64]) -> u64 {
assert_eq!(
assignment.len(),
self.num_vars,
"boolean assignment length mismatch"
);
let mut idx = 0usize;
for (i, &bit) in assignment.iter().enumerate() {
let bit_mod = bit % field.modulus();
assert!(
bit_mod == 0 || bit_mod == 1,
"boolean assignment must contain only 0/1"
);
if bit_mod == 1 {
idx |= 1 << i;
}
}
self.evals[idx] % field.modulus()
}
pub fn evaluate(&self, field: &Field, point: &[u64]) -> u64 {
assert_eq!(
point.len(),
self.num_vars,
"evaluation point length mismatch"
);
let mut layer = self.evaluations_mod_p(field);
for &coord in point {
let r = coord % field.modulus();
let mut next = Vec::with_capacity(layer.len() / 2);
for chunk in layer.chunks_exact(2) {
let v0 = chunk[0];
let v1 = chunk[1];
let diff = field.sub(v1, v0);
let eval = field.add(field.mul(diff, r), v0);
next.push(eval);
}
layer = next;
}
layer[0]
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_boolean_evaluation_ordering() {
let poly = MultilinearPolynomial::from_evaluations(2, vec![0, 1, 2, 3]);
let field = Field::new(101);
assert_eq!(poly.evaluate_boolean(&field, &[0, 0]), 0);
assert_eq!(poly.evaluate_boolean(&field, &[1, 0]), 1);
assert_eq!(poly.evaluate_boolean(&field, &[0, 1]), 2);
assert_eq!(poly.evaluate_boolean(&field, &[1, 1]), 3);
}
#[test]
fn test_arbitrary_evaluation() {
let poly = MultilinearPolynomial::from_evaluations(2, vec![0, 1, 2, 3]);
let field = Field::new(101);
let val = poly.evaluate(&field, &[5, 7]);
assert_eq!(val, 19);
}
}