use crate::{util::get_batched_nv, ArithErrors}; use ark_ff::{Field, PrimeField}; use ark_poly::MultilinearExtension; use ark_std::{end_timer, rand::RngCore, start_timer}; #[cfg(feature = "parallel")] use rayon::prelude::{IndexedParallelIterator, IntoParallelRefMutIterator, ParallelIterator}; use std::sync::Arc; pub use ark_poly::DenseMultilinearExtension; /// Sample a random list of multilinear polynomials. /// Returns /// - the list of polynomials, /// - its sum of polynomial evaluations over the boolean hypeArcube. 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. 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 } pub fn identity_permutation(num_vars: usize, num_chunks: usize) -> Vec { let len = (num_chunks as u64) * (1u64 << num_vars); (0..len).map(F::from).collect() } /// A list of MLEs that represents an identity permutation pub fn identity_permutation_mles( num_vars: usize, num_chunks: usize, ) -> Vec>> { let mut res = vec![]; for i in 0..num_chunks { let shift = (i * (1 << num_vars)) as u64; let s_id_vec = (shift..shift + (1u64 << num_vars)).map(F::from).collect(); res.push(Arc::new(DenseMultilinearExtension::from_evaluations_vec( num_vars, s_id_vec, ))); } res } pub fn random_permutation( num_vars: usize, num_chunks: usize, rng: &mut R, ) -> Vec { let len = (num_chunks as u64) * (1u64 << num_vars); let mut s_id_vec: Vec = (0..len).map(F::from).collect(); let mut s_perm_vec = vec![]; for _ in 0..len { let index = rng.next_u64() as usize % s_id_vec.len(); s_perm_vec.push(s_id_vec.remove(index)); } s_perm_vec } /// A list of MLEs that represent a random permutation pub fn random_permutation_mles( num_vars: usize, num_chunks: usize, rng: &mut R, ) -> Vec>> { let s_perm_vec = random_permutation(num_vars, num_chunks, rng); let mut res = vec![]; let n = 1 << num_vars; for i in 0..num_chunks { res.push(Arc::new(DenseMultilinearExtension::from_evaluations_vec( num_vars, s_perm_vec[i * n..i * n + n].to_vec(), ))); } res } pub fn evaluate_opt(poly: &DenseMultilinearExtension, point: &[F]) -> F { assert_eq!(poly.num_vars, point.len()); fix_variables(poly, point).evaluations[0] } 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] + (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))]) } /// merge a set of polynomials. Returns an error if the /// polynomials do not share a same number of nvs. pub fn merge_polynomials( polynomials: &[Arc>], ) -> Result>, ArithErrors> { let nv = polynomials[0].num_vars(); for poly in polynomials.iter() { if nv != poly.num_vars() { return Err(ArithErrors::InvalidParameters( "num_vars do not match for polynomials".to_string(), )); } } let merged_nv = get_batched_nv(nv, polynomials.len()); let mut scalars = vec![]; for poly in polynomials.iter() { scalars.extend_from_slice(poly.to_evaluations().as_slice()); } scalars.extend_from_slice(vec![F::zero(); (1 << merged_nv) - scalars.len()].as_ref()); Ok(Arc::new(DenseMultilinearExtension::from_evaluations_vec( merged_nv, scalars, ))) } pub fn fix_last_variables_no_par( poly: &DenseMultilinearExtension, partial_point: &[F], ) -> DenseMultilinearExtension { let mut res = fix_last_variable_no_par(poly, partial_point.last().unwrap()); for p in partial_point.iter().rev().skip(1) { res = fix_last_variable_no_par(&res, p); } res } fn fix_last_variable_no_par( poly: &DenseMultilinearExtension, partial_point: &F, ) -> DenseMultilinearExtension { let nv = poly.num_vars(); let half_len = 1 << (nv - 1); let mut res = vec![F::zero(); half_len]; for (i, e) in res.iter_mut().enumerate().take(half_len) { *e = poly.evaluations[i] + *partial_point * (poly.evaluations[i + half_len] - poly.evaluations[i]); } DenseMultilinearExtension::from_evaluations_vec(nv - 1, res) } 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 }