use core::ops::Index; use ff::PrimeField; use rayon::prelude::*; pub struct EqPolynomial { r: Vec, } impl EqPolynomial { pub fn new(r: Vec) -> Self { EqPolynomial { r } } pub fn evaluate(&self, rx: &[Scalar]) -> Scalar { assert_eq!(self.r.len(), rx.len()); (0..rx.len()) .map(|i| rx[i] * self.r[i] + (Scalar::one() - rx[i]) * (Scalar::one() - self.r[i])) .fold(Scalar::one(), |acc, item| acc * item) } pub fn evals(&self) -> Vec { let ell = self.r.len(); let mut evals: Vec = vec![Scalar::zero(); (2_usize).pow(ell as u32) as usize]; let mut size = 1; evals[0] = Scalar::one(); for r in self.r.iter().rev() { let (evals_left, evals_right) = evals.split_at_mut(size); let (evals_right, _) = evals_right.split_at_mut(size); evals_left .par_iter_mut() .zip(evals_right.par_iter_mut()) .for_each(|(x, y)| { *y = *x * r; *x -= &*y; }); size *= 2; } evals } } #[derive(Debug)] pub struct MultilinearPolynomial { num_vars: usize, // the number of variables in the multilinear polynomial Z: Vec, // evaluations of the polynomial in all the 2^num_vars Boolean inputs } impl MultilinearPolynomial { pub fn new(Z: Vec) -> Self { assert_eq!(Z.len(), (2_usize).pow((Z.len() as f64).log2() as u32)); MultilinearPolynomial { num_vars: (Z.len() as f64).log2() as usize, Z, } } pub fn get_num_vars(&self) -> usize { self.num_vars } pub fn len(&self) -> usize { self.Z.len() } pub fn bound_poly_var_top(&mut self, r: &Scalar) { let n = self.len() / 2; let (left, right) = self.Z.split_at_mut(n); let (right, _) = right.split_at(n); left .par_iter_mut() .zip(right.par_iter()) .for_each(|(a, b)| { *a += *r * (*b - *a); }); self.Z.resize(n, Scalar::zero()); self.num_vars -= 1; } // returns Z(r) in O(n) time pub fn evaluate(&self, r: &[Scalar]) -> Scalar { // r must have a value for each variable assert_eq!(r.len(), self.get_num_vars()); let chis = EqPolynomial::new(r.to_vec()).evals(); assert_eq!(chis.len(), self.Z.len()); (0..chis.len()) .into_par_iter() .map(|i| chis[i] * self.Z[i]) .reduce(Scalar::zero, |x, y| x + y) } } impl Index for MultilinearPolynomial { type Output = Scalar; #[inline(always)] fn index(&self, _index: usize) -> &Scalar { &(self.Z[_index]) } } pub struct SparsePolynomial { num_vars: usize, Z: Vec<(usize, Scalar)>, } impl SparsePolynomial { pub fn new(num_vars: usize, Z: Vec<(usize, Scalar)>) -> Self { SparsePolynomial { num_vars, Z } } fn compute_chi(a: &[bool], r: &[Scalar]) -> Scalar { assert_eq!(a.len(), r.len()); let mut chi_i = Scalar::one(); for j in 0..r.len() { if a[j] { chi_i *= r[j]; } else { chi_i *= Scalar::one() - r[j]; } } chi_i } // Takes O(n log n). TODO: do this in O(n) where n is the number of entries in Z pub fn evaluate(&self, r: &[Scalar]) -> Scalar { assert_eq!(self.num_vars, r.len()); let get_bits = |num: usize, num_bits: usize| -> Vec { (0..num_bits) .into_par_iter() .map(|shift_amount| ((num & (1 << (num_bits - shift_amount - 1))) > 0)) .collect::>() }; (0..self.Z.len()) .into_par_iter() .map(|i| { let bits = get_bits(self.Z[i].0, r.len()); SparsePolynomial::compute_chi(&bits, r) * self.Z[i].1 }) .reduce(Scalar::zero, |x, y| x + y) } }