arnaucube
arnaucube/hyperplonkPublic Self-hosted repository
hyperplonk/arithmetic/src/multilinear_polynomial.rs
arithmetic/src/multilinear_polynomial.rsRaw
  1use crate::{util::get_batched_nv, ArithErrors};
  2use ark_ff::{Field, PrimeField};
  3use ark_poly::MultilinearExtension;
  4use ark_std::{end_timer, rand::RngCore, start_timer};
  5#[cfg(feature = "parallel")]
  6use rayon::prelude::{IndexedParallelIterator, IntoParallelRefMutIterator, ParallelIterator};
  7use std::rc::Rc;
  8
  9pub use ark_poly::DenseMultilinearExtension;
 10
 11/// Sample a random list of multilinear polynomials.
 12/// Returns
 13/// - the list of polynomials,
 14/// - its sum of polynomial evaluations over the boolean hypercube.
 15pub fn random_mle_list<F: PrimeField, R: RngCore>(
 16    nv: usize,
 17    degree: usize,
 18    rng: &mut R,
 19) -> (Vec<Rc<DenseMultilinearExtension<F>>>, F) {
 20    let start = start_timer!(|| "sample random mle list");
 21    let mut multiplicands = Vec::with_capacity(degree);
 22    for _ in 0..degree {
 23        multiplicands.push(Vec::with_capacity(1 << nv))
 24    }
 25    let mut sum = F::zero();
 26
 27    for _ in 0..(1 << nv) {
 28        let mut product = F::one();
 29
 30        for e in multiplicands.iter_mut() {
 31            let val = F::rand(rng);
 32            e.push(val);
 33            product *= val;
 34        }
 35        sum += product;
 36    }
 37
 38    let list = multiplicands
 39        .into_iter()
 40        .map(|x| Rc::new(DenseMultilinearExtension::from_evaluations_vec(nv, x)))
 41        .collect();
 42
 43    end_timer!(start);
 44    (list, sum)
 45}
 46
 47// Build a randomize list of mle-s whose sum is zero.
 48pub fn random_zero_mle_list<F: PrimeField, R: RngCore>(
 49    nv: usize,
 50    degree: usize,
 51    rng: &mut R,
 52) -> Vec<Rc<DenseMultilinearExtension<F>>> {
 53    let start = start_timer!(|| "sample random zero mle list");
 54
 55    let mut multiplicands = Vec::with_capacity(degree);
 56    for _ in 0..degree {
 57        multiplicands.push(Vec::with_capacity(1 << nv))
 58    }
 59    for _ in 0..(1 << nv) {
 60        multiplicands[0].push(F::zero());
 61        for e in multiplicands.iter_mut().skip(1) {
 62            e.push(F::rand(rng));
 63        }
 64    }
 65
 66    let list = multiplicands
 67        .into_iter()
 68        .map(|x| Rc::new(DenseMultilinearExtension::from_evaluations_vec(nv, x)))
 69        .collect();
 70
 71    end_timer!(start);
 72    list
 73}
 74
 75/// An MLE that represent an identity permutation: `f(index) \mapto index`
 76pub fn identity_permutation_mle<F: PrimeField>(
 77    num_vars: usize,
 78) -> Rc<DenseMultilinearExtension<F>> {
 79    let s_id_vec = (0..1u64 << num_vars).map(F::from).collect();
 80    Rc::new(DenseMultilinearExtension::from_evaluations_vec(
 81        num_vars, s_id_vec,
 82    ))
 83}
 84
 85/// An MLE that represent a random permutation
 86pub fn random_permutation_mle<F: PrimeField, R: RngCore>(
 87    num_vars: usize,
 88    rng: &mut R,
 89) -> Rc<DenseMultilinearExtension<F>> {
 90    let len = 1u64 << num_vars;
 91    let mut s_id_vec: Vec<F> = (0..len).map(F::from).collect();
 92    let mut s_perm_vec = vec![];
 93    for _ in 0..len {
 94        let index = rng.next_u64() as usize % s_id_vec.len();
 95        s_perm_vec.push(s_id_vec.remove(index));
 96    }
 97    Rc::new(DenseMultilinearExtension::from_evaluations_vec(
 98        num_vars, s_perm_vec,
 99    ))
100}
101
102pub fn evaluate_opt<F: Field>(poly: &DenseMultilinearExtension<F>, point: &[F]) -> F {
103    assert_eq!(poly.num_vars, point.len());
104    fix_variables(poly, point).evaluations[0]
105}
106
107pub fn fix_variables<F: Field>(
108    poly: &DenseMultilinearExtension<F>,
109    partial_point: &[F],
110) -> DenseMultilinearExtension<F> {
111    assert!(
112        partial_point.len() <= poly.num_vars,
113        "invalid size of partial point"
114    );
115    let nv = poly.num_vars;
116    let mut poly = poly.evaluations.to_vec();
117    let dim = partial_point.len();
118    // evaluate single variable of partial point from left to right
119    for (i, point) in partial_point.iter().enumerate().take(dim) {
120        poly = fix_one_variable_helper(&poly, nv - i, point);
121    }
122
123    DenseMultilinearExtension::<F>::from_evaluations_slice(nv - dim, &poly[..(1 << (nv - dim))])
124}
125
126pub fn fix_first_variable<F: Field>(
127    poly: &DenseMultilinearExtension<F>,
128    partial_point: &F,
129) -> DenseMultilinearExtension<F> {
130    assert!(poly.num_vars != 0, "invalid size of partial point");
131
132    let nv = poly.num_vars;
133    let res = fix_one_variable_helper(&poly.evaluations, nv, partial_point);
134    DenseMultilinearExtension::<F>::from_evaluations_slice(nv - 1, &res)
135}
136
137fn fix_one_variable_helper<F: Field>(data: &[F], nv: usize, point: &F) -> Vec<F> {
138    let mut res = vec![F::zero(); 1 << (nv - 1)];
139    let one_minus_p = F::one() - point;
140
141    // evaluate single variable of partial point from left to right
142    #[cfg(not(feature = "parallel"))]
143    for b in 0..(1 << (nv - 1)) {
144        res[b] = data[b << 1] * one_minus_p + data[(b << 1) + 1] * point;
145    }
146
147    #[cfg(feature = "parallel")]
148    if nv >= 13 {
149        // on my computer we parallelization doesn't help till nv >= 13
150        res.par_iter_mut().enumerate().for_each(|(i, x)| {
151            *x = data[i << 1] * one_minus_p + data[(i << 1) + 1] * point;
152        });
153    } else {
154        for b in 0..(1 << (nv - 1)) {
155            res[b] = data[b << 1] * one_minus_p + data[(b << 1) + 1] * point;
156        }
157    }
158
159    res
160}
161
162pub fn evaluate_no_par<F: Field>(poly: &DenseMultilinearExtension<F>, point: &[F]) -> F {
163    assert_eq!(poly.num_vars, point.len());
164    fix_variables_no_par(poly, point).evaluations[0]
165}
166
167fn fix_variables_no_par<F: Field>(
168    poly: &DenseMultilinearExtension<F>,
169    partial_point: &[F],
170) -> DenseMultilinearExtension<F> {
171    assert!(
172        partial_point.len() <= poly.num_vars,
173        "invalid size of partial point"
174    );
175    let nv = poly.num_vars;
176    let mut poly = poly.evaluations.to_vec();
177    let dim = partial_point.len();
178    // evaluate single variable of partial point from left to right
179    for i in 1..dim + 1 {
180        let r = partial_point[i - 1];
181        let one_minus_r = F::one() - r;
182        for b in 0..(1 << (nv - i)) {
183            poly[b] = poly[b << 1] * one_minus_r + poly[(b << 1) + 1] * r;
184        }
185    }
186    DenseMultilinearExtension::from_evaluations_slice(nv - dim, &poly[..(1 << (nv - dim))])
187}
188
189/// merge a set of polynomials. Returns an error if the
190/// polynomials do not share a same number of nvs.
191pub fn merge_polynomials<F: PrimeField>(
192    polynomials: &[Rc<DenseMultilinearExtension<F>>],
193) -> Result<Rc<DenseMultilinearExtension<F>>, ArithErrors> {
194    let nv = polynomials[0].num_vars();
195    for poly in polynomials.iter() {
196        if nv != poly.num_vars() {
197            return Err(ArithErrors::InvalidParameters(
198                "num_vars do not match for polynomials".to_string(),
199            ));
200        }
201    }
202
203    let merged_nv = get_batched_nv(nv, polynomials.len());
204    let mut scalars = vec![];
205    for poly in polynomials.iter() {
206        scalars.extend_from_slice(poly.to_evaluations().as_slice());
207    }
208    scalars.extend_from_slice(vec![F::zero(); (1 << merged_nv) - scalars.len()].as_ref());
209    Ok(Rc::new(DenseMultilinearExtension::from_evaluations_vec(
210        merged_nv, scalars,
211    )))
212}