mirror of
https://github.com/arnaucube/hyperplonk.git
synced 2026-01-10 16:11:29 +01:00
initial integration of hyperplonk snark(#39)
This commit is contained in:
34
arithmetic/Cargo.toml
Normal file
34
arithmetic/Cargo.toml
Normal file
@@ -0,0 +1,34 @@
|
||||
[package]
|
||||
name = "arithmetic"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
|
||||
ark-ff = { version = "^0.3.0", default-features = false }
|
||||
ark-std = { version = "^0.3.0", default-features = false }
|
||||
ark-poly = { version = "^0.3.0", default-features = false }
|
||||
ark-serialize = { version = "^0.3.0", default-features = false }
|
||||
ark-bls12-381 = { version = "0.3.0", default-features = false, features = [ "curve" ] }
|
||||
|
||||
rand_chacha = { version = "0.3.0", default-features = false }
|
||||
displaydoc = { version = "0.2.3", default-features = false }
|
||||
rayon = { version = "1.5.2", default-features = false, optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
ark-ec = { version = "^0.3.0", default-features = false }
|
||||
|
||||
[features]
|
||||
# default = [ "parallel", "print-trace" ]
|
||||
default = [ "parallel" ]
|
||||
parallel = [
|
||||
"rayon",
|
||||
"ark-std/parallel",
|
||||
"ark-ff/parallel",
|
||||
"ark-poly/parallel"
|
||||
]
|
||||
print-trace = [
|
||||
"ark-std/print-trace"
|
||||
]
|
||||
21
arithmetic/src/errors.rs
Normal file
21
arithmetic/src/errors.rs
Normal file
@@ -0,0 +1,21 @@
|
||||
//! Error module.
|
||||
|
||||
use ark_std::string::String;
|
||||
use displaydoc::Display;
|
||||
|
||||
/// A `enum` specifying the possible failure modes of the arithmetics.
|
||||
#[derive(Display, Debug)]
|
||||
pub enum ArithErrors {
|
||||
/// Invalid parameters: {0}
|
||||
InvalidParameters(String),
|
||||
/// Should not arrive to this point
|
||||
ShouldNotArrive,
|
||||
/// An error during (de)serialization: {0}
|
||||
SerializationErrors(ark_serialize::SerializationError),
|
||||
}
|
||||
|
||||
impl From<ark_serialize::SerializationError> for ArithErrors {
|
||||
fn from(e: ark_serialize::SerializationError) -> Self {
|
||||
Self::SerializationErrors(e)
|
||||
}
|
||||
}
|
||||
7
arithmetic/src/lib.rs
Normal file
7
arithmetic/src/lib.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
mod errors;
|
||||
mod multilinear_polynomial;
|
||||
mod virtual_polynomial;
|
||||
|
||||
pub use errors::ArithErrors;
|
||||
pub use multilinear_polynomial::{random_zero_mle_list, DenseMultilinearExtension};
|
||||
pub use virtual_polynomial::{build_eq_x_r, VPAuxInfo, VirtualPolynomial};
|
||||
33
arithmetic/src/multilinear_polynomial.rs
Normal file
33
arithmetic/src/multilinear_polynomial.rs
Normal file
@@ -0,0 +1,33 @@
|
||||
use ark_ff::PrimeField;
|
||||
use ark_std::{end_timer, rand::RngCore, start_timer};
|
||||
use std::rc::Rc;
|
||||
|
||||
pub use ark_poly::DenseMultilinearExtension;
|
||||
|
||||
// Build a randomize list of mle-s whose sum is zero.
|
||||
pub fn random_zero_mle_list<F: PrimeField, R: RngCore>(
|
||||
nv: usize,
|
||||
degree: usize,
|
||||
rng: &mut R,
|
||||
) -> Vec<Rc<DenseMultilinearExtension<F>>> {
|
||||
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| Rc::new(DenseMultilinearExtension::from_evaluations_vec(nv, x)))
|
||||
.collect();
|
||||
|
||||
end_timer!(start);
|
||||
list
|
||||
}
|
||||
549
arithmetic/src/virtual_polynomial.rs
Normal file
549
arithmetic/src/virtual_polynomial.rs
Normal file
@@ -0,0 +1,549 @@
|
||||
//! This module defines our main mathematical object `VirtualPolynomial`; and
|
||||
//! various functions associated with it.
|
||||
|
||||
use crate::{errors::ArithErrors, multilinear_polynomial::random_zero_mle_list};
|
||||
use ark_ff::PrimeField;
|
||||
use ark_poly::{DenseMultilinearExtension, MultilinearExtension};
|
||||
use ark_serialize::{CanonicalSerialize, SerializationError, Write};
|
||||
use ark_std::{
|
||||
end_timer,
|
||||
rand::{Rng, RngCore},
|
||||
start_timer,
|
||||
};
|
||||
use std::{cmp::max, collections::HashMap, marker::PhantomData, ops::Add, rc::Rc};
|
||||
|
||||
#[rustfmt::skip]
|
||||
/// A virtual polynomial is a sum of products of multilinear polynomials;
|
||||
/// where the multilinear polynomials are stored via their multilinear
|
||||
/// extensions: `(coefficient, DenseMultilinearExtension)`
|
||||
///
|
||||
/// * Number of products n = `polynomial.products.len()`,
|
||||
/// * Number of multiplicands of ith product m_i =
|
||||
/// `polynomial.products[i].1.len()`,
|
||||
/// * Coefficient of ith product c_i = `polynomial.products[i].0`
|
||||
///
|
||||
/// The resulting polynomial is
|
||||
///
|
||||
/// $$ \sum_{i=0}^{n} c_i \cdot \prod_{j=0}^{m_i} P_{ij} $$
|
||||
///
|
||||
/// Example:
|
||||
/// f = c0 * f0 * f1 * f2 + c1 * f3 * f4
|
||||
/// where f0 ... f4 are multilinear polynomials
|
||||
///
|
||||
/// - flattened_ml_extensions stores the multilinear extension representation of
|
||||
/// f0, f1, f2, f3 and f4
|
||||
/// - products is
|
||||
/// \[
|
||||
/// (c0, \[0, 1, 2\]),
|
||||
/// (c1, \[3, 4\])
|
||||
/// \]
|
||||
/// - raw_pointers_lookup_table maps fi to i
|
||||
///
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
pub struct VirtualPolynomial<F: PrimeField> {
|
||||
/// Aux information about the multilinear polynomial
|
||||
pub aux_info: VPAuxInfo<F>,
|
||||
/// list of reference to products (as usize) of multilinear extension
|
||||
pub products: Vec<(F, Vec<usize>)>,
|
||||
/// Stores multilinear extensions in which product multiplicand can refer
|
||||
/// to.
|
||||
pub flattened_ml_extensions: Vec<Rc<DenseMultilinearExtension<F>>>,
|
||||
/// Pointers to the above poly extensions
|
||||
raw_pointers_lookup_table: HashMap<*const DenseMultilinearExtension<F>, usize>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, CanonicalSerialize)]
|
||||
/// Auxiliary information about the multilinear polynomial
|
||||
pub struct VPAuxInfo<F: PrimeField> {
|
||||
/// max number of multiplicands in each product
|
||||
pub max_degree: usize,
|
||||
/// number of variables of the polynomial
|
||||
pub num_variables: usize,
|
||||
/// Associated field
|
||||
#[doc(hidden)]
|
||||
pub phantom: PhantomData<F>,
|
||||
}
|
||||
|
||||
impl<F: PrimeField> Add for &VirtualPolynomial<F> {
|
||||
type Output = VirtualPolynomial<F>;
|
||||
fn add(self, other: &VirtualPolynomial<F>) -> Self::Output {
|
||||
let start = start_timer!(|| "virtual poly add");
|
||||
let mut res = self.clone();
|
||||
for products in other.products.iter() {
|
||||
let cur: Vec<Rc<DenseMultilinearExtension<F>>> = products
|
||||
.1
|
||||
.iter()
|
||||
.map(|&x| other.flattened_ml_extensions[x].clone())
|
||||
.collect();
|
||||
|
||||
res.add_mle_list(cur, products.0)
|
||||
.expect("add product failed");
|
||||
}
|
||||
end_timer!(start);
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: convert this into a trait
|
||||
impl<F: PrimeField> VirtualPolynomial<F> {
|
||||
/// Creates an empty virtual polynomial with `num_variables`.
|
||||
pub fn new(num_variables: usize) -> Self {
|
||||
VirtualPolynomial {
|
||||
aux_info: VPAuxInfo {
|
||||
max_degree: 0,
|
||||
num_variables,
|
||||
phantom: PhantomData::default(),
|
||||
},
|
||||
products: Vec::new(),
|
||||
flattened_ml_extensions: Vec::new(),
|
||||
raw_pointers_lookup_table: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates an new virtual polynomial from a MLE and its coefficient.
|
||||
pub fn new_from_mle(mle: Rc<DenseMultilinearExtension<F>>, coefficient: F) -> Self {
|
||||
let mle_ptr: *const DenseMultilinearExtension<F> = Rc::as_ptr(&mle);
|
||||
let mut hm = HashMap::new();
|
||||
hm.insert(mle_ptr, 0);
|
||||
|
||||
VirtualPolynomial {
|
||||
aux_info: VPAuxInfo {
|
||||
// The max degree is the max degree of any individual variable
|
||||
max_degree: 1,
|
||||
num_variables: mle.num_vars,
|
||||
phantom: PhantomData::default(),
|
||||
},
|
||||
// here `0` points to the first polynomial of `flattened_ml_extensions`
|
||||
products: vec![(coefficient, vec![0])],
|
||||
flattened_ml_extensions: vec![mle],
|
||||
raw_pointers_lookup_table: hm,
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a product of list of multilinear extensions to self
|
||||
/// Returns an error if the list is empty, or the MLE has a different
|
||||
/// `num_vars` from self.
|
||||
///
|
||||
/// The MLEs will be multiplied together, and then multiplied by the scalar
|
||||
/// `coefficient`.
|
||||
pub fn add_mle_list(
|
||||
&mut self,
|
||||
mle_list: impl IntoIterator<Item = Rc<DenseMultilinearExtension<F>>>,
|
||||
coefficient: F,
|
||||
) -> Result<(), ArithErrors> {
|
||||
let mle_list: Vec<Rc<DenseMultilinearExtension<F>>> = mle_list.into_iter().collect();
|
||||
let mut indexed_product = Vec::with_capacity(mle_list.len());
|
||||
|
||||
if mle_list.is_empty() {
|
||||
return Err(ArithErrors::InvalidParameters(
|
||||
"input mle_list is empty".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
self.aux_info.max_degree = max(self.aux_info.max_degree, mle_list.len());
|
||||
|
||||
for mle in mle_list {
|
||||
if mle.num_vars != self.aux_info.num_variables {
|
||||
return Err(ArithErrors::InvalidParameters(format!(
|
||||
"product has a multiplicand with wrong number of variables {} vs {}",
|
||||
mle.num_vars, self.aux_info.num_variables
|
||||
)));
|
||||
}
|
||||
|
||||
let mle_ptr: *const DenseMultilinearExtension<F> = Rc::as_ptr(&mle);
|
||||
if let Some(index) = self.raw_pointers_lookup_table.get(&mle_ptr) {
|
||||
indexed_product.push(*index)
|
||||
} else {
|
||||
let curr_index = self.flattened_ml_extensions.len();
|
||||
self.flattened_ml_extensions.push(mle.clone());
|
||||
self.raw_pointers_lookup_table.insert(mle_ptr, curr_index);
|
||||
indexed_product.push(curr_index);
|
||||
}
|
||||
}
|
||||
self.products.push((coefficient, indexed_product));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Multiple the current VirtualPolynomial by an MLE:
|
||||
/// - add the MLE to the MLE list;
|
||||
/// - multiple each product by MLE and its coefficient.
|
||||
/// Returns an error if the MLE has a different `num_vars` from self.
|
||||
pub fn mul_by_mle(
|
||||
&mut self,
|
||||
mle: Rc<DenseMultilinearExtension<F>>,
|
||||
coefficient: F,
|
||||
) -> Result<(), ArithErrors> {
|
||||
let start = start_timer!(|| "mul by mle");
|
||||
|
||||
if mle.num_vars != self.aux_info.num_variables {
|
||||
return Err(ArithErrors::InvalidParameters(format!(
|
||||
"product has a multiplicand with wrong number of variables {} vs {}",
|
||||
mle.num_vars, self.aux_info.num_variables
|
||||
)));
|
||||
}
|
||||
|
||||
let mle_ptr: *const DenseMultilinearExtension<F> = Rc::as_ptr(&mle);
|
||||
|
||||
// check if this mle already exists in the virtual polynomial
|
||||
let mle_index = match self.raw_pointers_lookup_table.get(&mle_ptr) {
|
||||
Some(&p) => p,
|
||||
None => {
|
||||
self.raw_pointers_lookup_table
|
||||
.insert(mle_ptr, self.flattened_ml_extensions.len());
|
||||
self.flattened_ml_extensions.push(mle);
|
||||
self.flattened_ml_extensions.len() - 1
|
||||
},
|
||||
};
|
||||
|
||||
for (prod_coef, indices) in self.products.iter_mut() {
|
||||
// - add the MLE to the MLE list;
|
||||
// - multiple each product by MLE and its coefficient.
|
||||
indices.push(mle_index);
|
||||
*prod_coef *= coefficient;
|
||||
}
|
||||
|
||||
// increase the max degree by one as the MLE has degree 1.
|
||||
self.aux_info.max_degree += 1;
|
||||
end_timer!(start);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Evaluate the virtual polynomial at point `point`.
|
||||
/// Returns an error is point.len() does not match `num_variables`.
|
||||
pub fn evaluate(&self, point: &[F]) -> Result<F, ArithErrors> {
|
||||
let start = start_timer!(|| "evaluation");
|
||||
|
||||
if self.aux_info.num_variables != point.len() {
|
||||
return Err(ArithErrors::InvalidParameters(format!(
|
||||
"wrong number of variables {} vs {}",
|
||||
self.aux_info.num_variables,
|
||||
point.len()
|
||||
)));
|
||||
}
|
||||
|
||||
let evals: Vec<F> = self
|
||||
.flattened_ml_extensions
|
||||
.iter()
|
||||
.map(|x| {
|
||||
x.evaluate(point).unwrap() // safe unwrap here since we have
|
||||
// already checked that num_var
|
||||
// matches
|
||||
})
|
||||
.collect();
|
||||
|
||||
let res = self
|
||||
.products
|
||||
.iter()
|
||||
.map(|(c, p)| *c * p.iter().map(|&i| evals[i]).product::<F>())
|
||||
.sum();
|
||||
|
||||
end_timer!(start);
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
/// Sample a random virtual polynomial, return the polynomial and its sum.
|
||||
pub fn rand<R: RngCore>(
|
||||
nv: usize,
|
||||
num_multiplicands_range: (usize, usize),
|
||||
num_products: usize,
|
||||
rng: &mut R,
|
||||
) -> Result<(Self, F), ArithErrors> {
|
||||
let start = start_timer!(|| "sample random virtual polynomial");
|
||||
|
||||
let mut sum = F::zero();
|
||||
let mut poly = VirtualPolynomial::new(nv);
|
||||
for _ in 0..num_products {
|
||||
let num_multiplicands =
|
||||
rng.gen_range(num_multiplicands_range.0..num_multiplicands_range.1);
|
||||
let (product, product_sum) = random_mle_list(nv, num_multiplicands, rng);
|
||||
let coefficient = F::rand(rng);
|
||||
poly.add_mle_list(product.into_iter(), coefficient)?;
|
||||
sum += product_sum * coefficient;
|
||||
}
|
||||
|
||||
end_timer!(start);
|
||||
Ok((poly, sum))
|
||||
}
|
||||
|
||||
/// Sample a random virtual polynomial that evaluates to zero everywhere
|
||||
/// over the boolean hypercube.
|
||||
pub fn rand_zero<R: RngCore>(
|
||||
nv: usize,
|
||||
num_multiplicands_range: (usize, usize),
|
||||
num_products: usize,
|
||||
rng: &mut R,
|
||||
) -> Result<Self, ArithErrors> {
|
||||
let mut poly = VirtualPolynomial::new(nv);
|
||||
for _ in 0..num_products {
|
||||
let num_multiplicands =
|
||||
rng.gen_range(num_multiplicands_range.0..num_multiplicands_range.1);
|
||||
let product = random_zero_mle_list(nv, num_multiplicands, rng);
|
||||
let coefficient = F::rand(rng);
|
||||
poly.add_mle_list(product.into_iter(), coefficient)?;
|
||||
}
|
||||
|
||||
Ok(poly)
|
||||
}
|
||||
|
||||
// Input poly f(x) and a random vector r, output
|
||||
// \hat f(x) = \sum_{x_i \in eval_x} f(x_i) eq(x, r)
|
||||
// where
|
||||
// eq(x,y) = \prod_i=1^num_var (x_i * y_i + (1-x_i)*(1-y_i))
|
||||
//
|
||||
// This function is used in ZeroCheck.
|
||||
pub fn build_f_hat(&self, r: &[F]) -> Result<Self, ArithErrors> {
|
||||
let start = start_timer!(|| "zero check build hat f");
|
||||
|
||||
if self.aux_info.num_variables != r.len() {
|
||||
return Err(ArithErrors::InvalidParameters(format!(
|
||||
"r.len() is different from number of variables: {} vs {}",
|
||||
r.len(),
|
||||
self.aux_info.num_variables
|
||||
)));
|
||||
}
|
||||
|
||||
let eq_x_r = build_eq_x_r(r)?;
|
||||
let mut res = self.clone();
|
||||
res.mul_by_mle(eq_x_r, F::one())?;
|
||||
|
||||
end_timer!(start);
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
/// Print out the evaluation map for testing. Panic if the num_vars > 5.
|
||||
pub fn print_evals(&self) {
|
||||
if self.aux_info.num_variables > 5 {
|
||||
panic!("this function is used for testing only. cannot print more than 5 num_vars")
|
||||
}
|
||||
for i in 0..1 << self.aux_info.num_variables {
|
||||
let point = bit_decompose(i, self.aux_info.num_variables);
|
||||
let point_fr: Vec<F> = point.iter().map(|&x| F::from(x)).collect();
|
||||
println!("{} {}", i, self.evaluate(point_fr.as_ref()).unwrap())
|
||||
}
|
||||
println!()
|
||||
}
|
||||
}
|
||||
|
||||
/// Sample a random list of multilinear polynomials.
|
||||
/// Returns
|
||||
/// - the list of polynomials,
|
||||
/// - its sum of polynomial evaluations over the boolean hypercube.
|
||||
fn random_mle_list<F: PrimeField, R: RngCore>(
|
||||
nv: usize,
|
||||
degree: usize,
|
||||
rng: &mut R,
|
||||
) -> (Vec<Rc<DenseMultilinearExtension<F>>>, 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| Rc::new(DenseMultilinearExtension::from_evaluations_vec(nv, x)))
|
||||
.collect();
|
||||
|
||||
end_timer!(start);
|
||||
(list, sum)
|
||||
}
|
||||
|
||||
// This function build the eq(x, r) polynomial for any given r.
|
||||
//
|
||||
// Evaluate
|
||||
// eq(x,y) = \prod_i=1^num_var (x_i * y_i + (1-x_i)*(1-y_i))
|
||||
// over r, which is
|
||||
// eq(x,y) = \prod_i=1^num_var (x_i * r_i + (1-x_i)*(1-r_i))
|
||||
pub fn build_eq_x_r<F: PrimeField>(
|
||||
r: &[F],
|
||||
) -> Result<Rc<DenseMultilinearExtension<F>>, ArithErrors> {
|
||||
let start = start_timer!(|| "zero check build eq_x_r");
|
||||
|
||||
// we build eq(x,r) from its evaluations
|
||||
// we want to evaluate eq(x,r) over x \in {0, 1}^num_vars
|
||||
// for example, with num_vars = 4, x is a binary vector of 4, then
|
||||
// 0 0 0 0 -> (1-r0) * (1-r1) * (1-r2) * (1-r3)
|
||||
// 1 0 0 0 -> r0 * (1-r1) * (1-r2) * (1-r3)
|
||||
// 0 1 0 0 -> (1-r0) * r1 * (1-r2) * (1-r3)
|
||||
// 1 1 0 0 -> r0 * r1 * (1-r2) * (1-r3)
|
||||
// ....
|
||||
// 1 1 1 1 -> r0 * r1 * r2 * r3
|
||||
// we will need 2^num_var evaluations
|
||||
|
||||
let mut eval = Vec::new();
|
||||
build_eq_x_r_helper(r, &mut eval)?;
|
||||
|
||||
let mle = DenseMultilinearExtension::from_evaluations_vec(r.len(), eval);
|
||||
|
||||
let res = Rc::new(mle);
|
||||
end_timer!(start);
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
/// A helper function to build eq(x, r) recursively.
|
||||
/// This function takes `r.len()` steps, and for each step it requires a maximum
|
||||
/// `r.len()-1` multiplications.
|
||||
fn build_eq_x_r_helper<F: PrimeField>(r: &[F], buf: &mut Vec<F>) -> Result<(), ArithErrors> {
|
||||
if r.is_empty() {
|
||||
return Err(ArithErrors::InvalidParameters("r length is 0".to_string()));
|
||||
} else if r.len() == 1 {
|
||||
// initializing the buffer with [1-r_0, r_0]
|
||||
buf.push(F::one() - r[0]);
|
||||
buf.push(r[0]);
|
||||
} else {
|
||||
build_eq_x_r_helper(&r[1..], buf)?;
|
||||
|
||||
// suppose at the previous step we received [b_1, ..., b_k]
|
||||
// for the current step we will need
|
||||
// if x_0 = 0: (1-r0) * [b_1, ..., b_k]
|
||||
// if x_0 = 1: r0 * [b_1, ..., b_k]
|
||||
|
||||
let mut res = vec![];
|
||||
for &b_i in buf.iter() {
|
||||
let tmp = r[0] * b_i;
|
||||
res.push(b_i - tmp);
|
||||
res.push(tmp);
|
||||
}
|
||||
*buf = res;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Decompose an integer into a binary vector in little endian.
|
||||
pub fn bit_decompose(input: u64, num_var: usize) -> Vec<bool> {
|
||||
let mut res = Vec::with_capacity(num_var);
|
||||
let mut i = input;
|
||||
for _ in 0..num_var {
|
||||
res.push(i & 1 == 1);
|
||||
i >>= 1;
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use ark_bls12_381::Fr;
|
||||
use ark_ff::UniformRand;
|
||||
use ark_std::test_rng;
|
||||
|
||||
#[test]
|
||||
fn test_virtual_polynomial_additions() -> Result<(), ArithErrors> {
|
||||
let mut rng = test_rng();
|
||||
for nv in 2..5 {
|
||||
for num_products in 2..5 {
|
||||
let base: Vec<Fr> = (0..nv).map(|_| Fr::rand(&mut rng)).collect();
|
||||
|
||||
let (a, _a_sum) =
|
||||
VirtualPolynomial::<Fr>::rand(nv, (2, 3), num_products, &mut rng)?;
|
||||
let (b, _b_sum) =
|
||||
VirtualPolynomial::<Fr>::rand(nv, (2, 3), num_products, &mut rng)?;
|
||||
let c = &a + &b;
|
||||
|
||||
assert_eq!(
|
||||
a.evaluate(base.as_ref())? + b.evaluate(base.as_ref())?,
|
||||
c.evaluate(base.as_ref())?
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_virtual_polynomial_mul_by_mle() -> Result<(), ArithErrors> {
|
||||
let mut rng = test_rng();
|
||||
for nv in 2..5 {
|
||||
for num_products in 2..5 {
|
||||
let base: Vec<Fr> = (0..nv).map(|_| Fr::rand(&mut rng)).collect();
|
||||
|
||||
let (a, _a_sum) =
|
||||
VirtualPolynomial::<Fr>::rand(nv, (2, 3), num_products, &mut rng)?;
|
||||
let (b, _b_sum) = random_mle_list(nv, 1, &mut rng);
|
||||
let b_mle = b[0].clone();
|
||||
let coeff = Fr::rand(&mut rng);
|
||||
let b_vp = VirtualPolynomial::new_from_mle(b_mle.clone(), coeff);
|
||||
|
||||
let mut c = a.clone();
|
||||
|
||||
c.mul_by_mle(b_mle, coeff)?;
|
||||
|
||||
assert_eq!(
|
||||
a.evaluate(base.as_ref())? * b_vp.evaluate(base.as_ref())?,
|
||||
c.evaluate(base.as_ref())?
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_eq_xr() {
|
||||
let mut rng = test_rng();
|
||||
for nv in 4..10 {
|
||||
let r: Vec<Fr> = (0..nv).map(|_| Fr::rand(&mut rng)).collect();
|
||||
let eq_x_r = build_eq_x_r(r.as_ref()).unwrap();
|
||||
let eq_x_r2 = build_eq_x_r_for_test(r.as_ref());
|
||||
assert_eq!(eq_x_r, eq_x_r2);
|
||||
}
|
||||
}
|
||||
|
||||
/// Naive method to build eq(x, r).
|
||||
/// Only used for testing purpose.
|
||||
// Evaluate
|
||||
// eq(x,y) = \prod_i=1^num_var (x_i * y_i + (1-x_i)*(1-y_i))
|
||||
// over r, which is
|
||||
// eq(x,y) = \prod_i=1^num_var (x_i * r_i + (1-x_i)*(1-r_i))
|
||||
fn build_eq_x_r_for_test<F: PrimeField>(r: &[F]) -> Rc<DenseMultilinearExtension<F>> {
|
||||
let start = start_timer!(|| "zero check naive build eq_x_r");
|
||||
|
||||
// we build eq(x,r) from its evaluations
|
||||
// we want to evaluate eq(x,r) over x \in {0, 1}^num_vars
|
||||
// for example, with num_vars = 4, x is a binary vector of 4, then
|
||||
// 0 0 0 0 -> (1-r0) * (1-r1) * (1-r2) * (1-r3)
|
||||
// 1 0 0 0 -> r0 * (1-r1) * (1-r2) * (1-r3)
|
||||
// 0 1 0 0 -> (1-r0) * r1 * (1-r2) * (1-r3)
|
||||
// 1 1 0 0 -> r0 * r1 * (1-r2) * (1-r3)
|
||||
// ....
|
||||
// 1 1 1 1 -> r0 * r1 * r2 * r3
|
||||
// we will need 2^num_var evaluations
|
||||
|
||||
// First, we build array for {1 - r_i}
|
||||
let one_minus_r: Vec<F> = r.iter().map(|ri| F::one() - ri).collect();
|
||||
|
||||
let num_var = r.len();
|
||||
let mut eval = vec![];
|
||||
|
||||
for i in 0..1 << num_var {
|
||||
let mut current_eval = F::one();
|
||||
let bit_sequence = bit_decompose(i, num_var);
|
||||
|
||||
for (&bit, (ri, one_minus_ri)) in
|
||||
bit_sequence.iter().zip(r.iter().zip(one_minus_r.iter()))
|
||||
{
|
||||
current_eval *= if bit { *ri } else { *one_minus_ri };
|
||||
}
|
||||
eval.push(current_eval);
|
||||
}
|
||||
|
||||
let mle = DenseMultilinearExtension::from_evaluations_vec(num_var, eval);
|
||||
|
||||
let res = Rc::new(mle);
|
||||
end_timer!(start);
|
||||
res
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user