// code forked from
// https://github.com/EspressoSystems/hyperplonk/blob/main/arithmetic/src/multilinear_polynomial.rs
//
// Copyright (c) 2023 Espresso Systems (espressosys.com)
// This file is part of the HyperPlonk library.
// You should have received a copy of the MIT License
// along with the HyperPlonk library. If not, see .
use ark_ff::Field;
#[cfg(feature = "parallel")]
use rayon::prelude::{IndexedParallelIterator, IntoParallelRefMutIterator, ParallelIterator};
pub use ark_poly::DenseMultilinearExtension;
pub fn fix_variables(
poly: &DenseMultilinearExtension,
partial_point: &[F],
) -> DenseMultilinearExtension {
assert!(
partial_point.len() <= poly.num_vars,
"invalid size of partial point"
);
let nv = poly.num_vars;
let mut poly = poly.evaluations.to_vec();
let dim = partial_point.len();
// evaluate single variable of partial point from left to right
for (i, point) in partial_point.iter().enumerate().take(dim) {
poly = fix_one_variable_helper(&poly, nv - i, point);
}
DenseMultilinearExtension::::from_evaluations_slice(nv - dim, &poly[..(1 << (nv - dim))])
}
fn fix_one_variable_helper(data: &[F], nv: usize, point: &F) -> Vec {
let mut res = vec![F::zero(); 1 << (nv - 1)];
// evaluate single variable of partial point from left to right
#[cfg(not(feature = "parallel"))]
for i in 0..(1 << (nv - 1)) {
res[i] = data[i << 1] + (data[(i << 1) + 1] - data[i << 1]) * point;
}
#[cfg(feature = "parallel")]
res.par_iter_mut().enumerate().for_each(|(i, x)| {
*x = data[i << 1] + (data[(i << 1) + 1] - data[i << 1]) * point;
});
res
}
pub fn evaluate_no_par(poly: &DenseMultilinearExtension, point: &[F]) -> F {
assert_eq!(poly.num_vars, point.len());
fix_variables_no_par(poly, point).evaluations[0]
}
fn fix_variables_no_par(
poly: &DenseMultilinearExtension,
partial_point: &[F],
) -> DenseMultilinearExtension {
assert!(
partial_point.len() <= poly.num_vars,
"invalid size of partial point"
);
let nv = poly.num_vars;
let mut poly = poly.evaluations.to_vec();
let dim = partial_point.len();
// evaluate single variable of partial point from left to right
for i in 1..dim + 1 {
let r = partial_point[i - 1];
for b in 0..(1 << (nv - i)) {
poly[b] = poly[b << 1] + (poly[(b << 1) + 1] - poly[b << 1]) * r;
}
}
DenseMultilinearExtension::from_evaluations_slice(nv - dim, &poly[..(1 << (nv - dim))])
}
/// Given multilinear polynomial `p(x)` and s `s`, compute `s*p(x)`
pub fn scalar_mul(
poly: &DenseMultilinearExtension,
s: &F,
) -> DenseMultilinearExtension {
DenseMultilinearExtension {
evaluations: poly.evaluations.iter().map(|e| *e * s).collect(),
num_vars: poly.num_vars,
}
}
/// Test-only methods used in virtual_polynomial.rs
#[cfg(test)]
pub mod tests {
use super::*;
use ark_ff::PrimeField;
use ark_std::rand::RngCore;
use ark_std::{end_timer, start_timer};
use std::sync::Arc;
pub fn fix_last_variables(
poly: &DenseMultilinearExtension,
partial_point: &[F],
) -> DenseMultilinearExtension {
assert!(
partial_point.len() <= poly.num_vars,
"invalid size of partial point"
);
let nv = poly.num_vars;
let mut poly = poly.evaluations.to_vec();
let dim = partial_point.len();
// evaluate single variable of partial point from left to right
for (i, point) in partial_point.iter().rev().enumerate().take(dim) {
poly = fix_last_variable_helper(&poly, nv - i, point);
}
DenseMultilinearExtension::::from_evaluations_slice(nv - dim, &poly[..(1 << (nv - dim))])
}
fn fix_last_variable_helper(data: &[F], nv: usize, point: &F) -> Vec {
let half_len = 1 << (nv - 1);
let mut res = vec![F::zero(); half_len];
// evaluate single variable of partial point from left to right
#[cfg(not(feature = "parallel"))]
for b in 0..half_len {
res[b] = data[b] + (data[b + half_len] - data[b]) * point;
}
#[cfg(feature = "parallel")]
res.par_iter_mut().enumerate().for_each(|(i, x)| {
*x = data[i] + (data[i + half_len] - data[i]) * point;
});
res
}
/// Sample a random list of multilinear polynomials.
/// Returns
/// - the list of polynomials,
/// - its sum of polynomial evaluations over the boolean hypercube.
#[cfg(test)]
pub fn random_mle_list(
nv: usize,
degree: usize,
rng: &mut R,
) -> (Vec>>, F) {
let start = start_timer!(|| "sample random mle list");
let mut multiplicands = Vec::with_capacity(degree);
for _ in 0..degree {
multiplicands.push(Vec::with_capacity(1 << nv))
}
let mut sum = F::zero();
for _ in 0..(1 << nv) {
let mut product = F::one();
for e in multiplicands.iter_mut() {
let val = F::rand(rng);
e.push(val);
product *= val;
}
sum += product;
}
let list = multiplicands
.into_iter()
.map(|x| Arc::new(DenseMultilinearExtension::from_evaluations_vec(nv, x)))
.collect();
end_timer!(start);
(list, sum)
}
// Build a randomize list of mle-s whose sum is zero.
#[cfg(test)]
pub fn random_zero_mle_list(
nv: usize,
degree: usize,
rng: &mut R,
) -> Vec>> {
let start = start_timer!(|| "sample random zero mle list");
let mut multiplicands = Vec::with_capacity(degree);
for _ in 0..degree {
multiplicands.push(Vec::with_capacity(1 << nv))
}
for _ in 0..(1 << nv) {
multiplicands[0].push(F::zero());
for e in multiplicands.iter_mut().skip(1) {
e.push(F::rand(rng));
}
}
let list = multiplicands
.into_iter()
.map(|x| Arc::new(DenseMultilinearExtension::from_evaluations_vec(nv, x)))
.collect();
end_timer!(start);
list
}
}