mirror of
https://github.com/arnaucube/hyperplonk.git
synced 2026-01-11 16:41:28 +01:00
30 implement prod1 x (#31)
This commit is contained in:
@@ -16,6 +16,8 @@ pub enum PolyIOPErrors {
|
|||||||
InvalidParameters(String),
|
InvalidParameters(String),
|
||||||
/// Invalid Transcript: {0}
|
/// Invalid Transcript: {0}
|
||||||
InvalidTranscript(String),
|
InvalidTranscript(String),
|
||||||
|
/// Should not arrive to this point
|
||||||
|
ShouldNotArrive,
|
||||||
/// An error during (de)serialization: {0}
|
/// An error during (de)serialization: {0}
|
||||||
SerializationError(ark_serialize::SerializationError),
|
SerializationError(ark_serialize::SerializationError),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
//! This module implements the permutation check protocol.
|
//! This module implements the permutation check protocol.
|
||||||
|
|
||||||
|
use crate::{utils::get_index, PolyIOPErrors};
|
||||||
use ark_ff::PrimeField;
|
use ark_ff::PrimeField;
|
||||||
use ark_poly::DenseMultilinearExtension;
|
use ark_poly::DenseMultilinearExtension;
|
||||||
|
use ark_std::{end_timer, start_timer};
|
||||||
use crate::PolyIOPErrors;
|
|
||||||
|
|
||||||
/// Compute `prod(0,x) := prod(0, x1, …, xn)` which is the MLE over the
|
/// Compute `prod(0,x) := prod(0, x1, …, xn)` which is the MLE over the
|
||||||
/// evaluations of the following polynomial on the boolean hypercube {0,1}^n:
|
/// evaluations of the following polynomial on the boolean hypercube {0,1}^n:
|
||||||
@@ -13,6 +13,9 @@ use crate::PolyIOPErrors;
|
|||||||
/// where
|
/// where
|
||||||
/// - beta and gamma are challenges
|
/// - beta and gamma are challenges
|
||||||
/// - w(x), s_id(x), s_perm(x) are mle-s
|
/// - w(x), s_id(x), s_perm(x) are mle-s
|
||||||
|
///
|
||||||
|
/// The caller needs to check num_vars matches in w/s_id/s_perm
|
||||||
|
/// Cost: linear in N.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
// TODO: remove
|
// TODO: remove
|
||||||
fn compute_prod_0<F: PrimeField>(
|
fn compute_prod_0<F: PrimeField>(
|
||||||
@@ -22,13 +25,8 @@ fn compute_prod_0<F: PrimeField>(
|
|||||||
s_id: &DenseMultilinearExtension<F>,
|
s_id: &DenseMultilinearExtension<F>,
|
||||||
s_perm: &DenseMultilinearExtension<F>,
|
s_perm: &DenseMultilinearExtension<F>,
|
||||||
) -> Result<DenseMultilinearExtension<F>, PolyIOPErrors> {
|
) -> Result<DenseMultilinearExtension<F>, PolyIOPErrors> {
|
||||||
|
let start = start_timer!(|| "compute prod(1,x)");
|
||||||
let num_vars = w.num_vars;
|
let num_vars = w.num_vars;
|
||||||
|
|
||||||
if num_vars != s_id.num_vars || num_vars != s_perm.num_vars {
|
|
||||||
return Err(PolyIOPErrors::InvalidParameters(
|
|
||||||
"num of variables do not match".to_string(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
let eval: Vec<F> = w
|
let eval: Vec<F> = w
|
||||||
.iter()
|
.iter()
|
||||||
.zip(s_id.iter().zip(s_perm.iter()))
|
.zip(s_id.iter().zip(s_perm.iter()))
|
||||||
@@ -39,10 +37,119 @@ fn compute_prod_0<F: PrimeField>(
|
|||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let res = DenseMultilinearExtension::from_evaluations_vec(num_vars, eval);
|
let res = DenseMultilinearExtension::from_evaluations_vec(num_vars, eval);
|
||||||
|
end_timer!(start);
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Compute the following 5 polynomials
|
||||||
|
/// - prod(x)
|
||||||
|
/// - prod(0, x)
|
||||||
|
/// - prod(1, x)
|
||||||
|
/// - prod(x, 0)
|
||||||
|
/// - prod(x, 1)
|
||||||
|
///
|
||||||
|
/// where
|
||||||
|
/// - `prod(0,x) := prod(0, x0, x1, …, xn)` which is the MLE over the
|
||||||
|
/// evaluations of the following polynomial on the boolean hypercube {0,1}^n:
|
||||||
|
///
|
||||||
|
/// (w(x) + \beta s_id(x) + \gamma)/(w(x) + \beta s_perm(x) + \gamma)
|
||||||
|
///
|
||||||
|
/// where
|
||||||
|
/// - beta and gamma are challenges
|
||||||
|
/// - w(x), s_id(x), s_perm(x) are mle-s
|
||||||
|
///
|
||||||
|
/// - `prod(1,x) := prod(x, 0) * prod(x, 1)`
|
||||||
|
///
|
||||||
|
/// Returns an error when the num_vars in w/s_id/s_perm does not match
|
||||||
|
/// Cost: linear in N.
|
||||||
|
#[allow(dead_code)]
|
||||||
|
// TODO: remove
|
||||||
|
fn compute_products<F: PrimeField>(
|
||||||
|
beta: &F,
|
||||||
|
gamma: &F,
|
||||||
|
w: &DenseMultilinearExtension<F>,
|
||||||
|
s_id: &DenseMultilinearExtension<F>,
|
||||||
|
s_perm: &DenseMultilinearExtension<F>,
|
||||||
|
) -> Result<[DenseMultilinearExtension<F>; 5], PolyIOPErrors> {
|
||||||
|
let start = start_timer!(|| "compute all prod polynomial");
|
||||||
|
|
||||||
|
let num_vars = w.num_vars;
|
||||||
|
|
||||||
|
if num_vars != s_id.num_vars || num_vars != s_perm.num_vars {
|
||||||
|
return Err(PolyIOPErrors::InvalidParameters(
|
||||||
|
"num of variables do not match".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
// ===================================
|
||||||
|
// prod(0, x)
|
||||||
|
// ===================================
|
||||||
|
let prod_0x = compute_prod_0(beta, gamma, w, s_id, s_perm)?;
|
||||||
|
|
||||||
|
// ===================================
|
||||||
|
// prod(1, x)
|
||||||
|
// ===================================
|
||||||
|
//
|
||||||
|
// `prod(1, x)` can be computed via recursing the following formula for 2^n-1
|
||||||
|
// times
|
||||||
|
//
|
||||||
|
// `prod(1, x_1, ..., x_n) :=
|
||||||
|
// prod(x_1, x_2, ..., x_n, 0) * prod(x_1, x_2, ..., x_n, 1)`
|
||||||
|
//
|
||||||
|
// At any given step, the right hand side of the equation
|
||||||
|
// is available via either eval_0x or the current view of eval_1x
|
||||||
|
let eval_0x = &prod_0x.evaluations;
|
||||||
|
let mut eval_1x = vec![];
|
||||||
|
for x in 0..(1 << num_vars) - 1 {
|
||||||
|
// sign will decide if the evaluation should be looked up from eval_0x or
|
||||||
|
// eval_1x; x_zero_index is the index for the evaluation (x_2, ..., x_n,
|
||||||
|
// 0); x_one_index is the index for the evaluation (x_2, ..., x_n, 1);
|
||||||
|
let (x_zero_index, x_one_index, sign) = get_index(x, num_vars);
|
||||||
|
if !sign {
|
||||||
|
eval_1x.push(eval_0x[x_zero_index] * eval_0x[x_one_index]);
|
||||||
|
} else {
|
||||||
|
// sanity check: if we are trying to look up from the eval_1x table,
|
||||||
|
// then the target index must already exist
|
||||||
|
if x_zero_index >= eval_1x.len() || x_one_index >= eval_1x.len() {
|
||||||
|
return Err(PolyIOPErrors::ShouldNotArrive);
|
||||||
|
}
|
||||||
|
eval_1x.push(eval_1x[x_zero_index] * eval_1x[x_one_index]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// prod(1, 1, ..., 1) := 0
|
||||||
|
eval_1x.push(F::zero());
|
||||||
|
|
||||||
|
// ===================================
|
||||||
|
// prod(x)
|
||||||
|
// ===================================
|
||||||
|
// prod(x)'s evaluation is indeed `e := [eval_0x[..], eval_1x[..]].concat()`
|
||||||
|
let eval = [eval_0x.as_slice(), eval_1x.as_slice()].concat();
|
||||||
|
|
||||||
|
// ===================================
|
||||||
|
// prod(x, 0) and prod(x, 1)
|
||||||
|
// ===================================
|
||||||
|
//
|
||||||
|
// now we compute eval_x0 and eval_x1
|
||||||
|
// eval_0x will be the odd coefficients of eval
|
||||||
|
// and eval_1x will be the even coefficients of eval
|
||||||
|
let mut eval_x0 = vec![];
|
||||||
|
let mut eval_x1 = vec![];
|
||||||
|
for (x, &prod_x) in eval.iter().enumerate() {
|
||||||
|
if x & 1 == 0 {
|
||||||
|
eval_x0.push(prod_x);
|
||||||
|
} else {
|
||||||
|
eval_x1.push(prod_x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let prod_1x = DenseMultilinearExtension::from_evaluations_vec(num_vars, eval_1x);
|
||||||
|
let prod_x0 = DenseMultilinearExtension::from_evaluations_vec(num_vars, eval_x0);
|
||||||
|
let prod_x1 = DenseMultilinearExtension::from_evaluations_vec(num_vars, eval_x1);
|
||||||
|
let prod = DenseMultilinearExtension::from_evaluations_vec(num_vars + 1, eval);
|
||||||
|
|
||||||
|
end_timer!(start);
|
||||||
|
Ok([prod, prod_0x, prod_1x, prod_x0, prod_x1])
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
@@ -52,6 +159,12 @@ mod test {
|
|||||||
use ark_poly::MultilinearExtension;
|
use ark_poly::MultilinearExtension;
|
||||||
use ark_std::test_rng;
|
use ark_std::test_rng;
|
||||||
|
|
||||||
|
/// An MLE that represent an identity permutation: `f(index) \mapto index`
|
||||||
|
fn identity_permutation_mle<F: PrimeField>(num_vars: usize) -> DenseMultilinearExtension<F> {
|
||||||
|
let s_id_vec = (0..1u64 << num_vars).map(|index| F::from(index)).collect();
|
||||||
|
DenseMultilinearExtension::from_evaluations_vec(num_vars, s_id_vec)
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_compute_prod_0() -> Result<(), PolyIOPErrors> {
|
fn test_compute_prod_0() -> Result<(), PolyIOPErrors> {
|
||||||
let mut rng = test_rng();
|
let mut rng = test_rng();
|
||||||
@@ -60,22 +173,22 @@ mod test {
|
|||||||
let w_vec: Vec<Fr> = (0..(1 << num_vars)).map(|_| Fr::rand(&mut rng)).collect();
|
let w_vec: Vec<Fr> = (0..(1 << num_vars)).map(|_| Fr::rand(&mut rng)).collect();
|
||||||
let w = DenseMultilinearExtension::from_evaluations_vec(num_vars, w_vec);
|
let w = DenseMultilinearExtension::from_evaluations_vec(num_vars, w_vec);
|
||||||
|
|
||||||
let s_id_vec: Vec<Fr> = (0..(1 << num_vars)).map(|_| Fr::rand(&mut rng)).collect();
|
let s_id = identity_permutation_mle::<Fr>(num_vars);
|
||||||
let s_id = DenseMultilinearExtension::from_evaluations_vec(num_vars, s_id_vec);
|
|
||||||
|
|
||||||
let s_perm_vec: Vec<Fr> = (0..(1 << num_vars)).map(|_| Fr::rand(&mut rng)).collect();
|
let s_perm_vec: Vec<Fr> = (0..(1 << num_vars)).map(|_| Fr::rand(&mut rng)).collect();
|
||||||
let s_perm = DenseMultilinearExtension::from_evaluations_vec(num_vars, s_perm_vec);
|
let s_perm = DenseMultilinearExtension::from_evaluations_vec(num_vars, s_perm_vec);
|
||||||
|
|
||||||
let beta = Fr::rand(&mut rng);
|
let beta = Fr::rand(&mut rng);
|
||||||
let gamma = Fr::rand(&mut rng);
|
let gamma = Fr::rand(&mut rng);
|
||||||
|
|
||||||
|
let prod_0 = compute_prod_0(&beta, &gamma, &w, &s_id, &s_perm)?;
|
||||||
|
|
||||||
for i in 0..1 << num_vars {
|
for i in 0..1 << num_vars {
|
||||||
let r: Vec<Fr> = bit_decompose(i, num_vars)
|
let r: Vec<Fr> = bit_decompose(i, num_vars)
|
||||||
.iter()
|
.iter()
|
||||||
.map(|&x| Fr::from(x))
|
.map(|&x| Fr::from(x))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let prod_0 = compute_prod_0(&beta, &gamma, &w, &s_id, &s_perm)?;
|
|
||||||
|
|
||||||
let eval = prod_0.evaluate(&r).unwrap();
|
let eval = prod_0.evaluate(&r).unwrap();
|
||||||
|
|
||||||
let w_eval = w.evaluate(&r).unwrap();
|
let w_eval = w.evaluate(&r).unwrap();
|
||||||
@@ -89,4 +202,43 @@ mod test {
|
|||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_compute_prod() -> Result<(), PolyIOPErrors> {
|
||||||
|
let mut rng = test_rng();
|
||||||
|
|
||||||
|
for num_vars in 2..6 {
|
||||||
|
let w_vec: Vec<Fr> = (0..(1 << num_vars)).map(|_| Fr::rand(&mut rng)).collect();
|
||||||
|
let w = DenseMultilinearExtension::from_evaluations_vec(num_vars, w_vec);
|
||||||
|
|
||||||
|
let s_id = identity_permutation_mle::<Fr>(num_vars);
|
||||||
|
|
||||||
|
let s_perm_vec: Vec<Fr> = (0..(1 << num_vars)).map(|_| Fr::rand(&mut rng)).collect();
|
||||||
|
let s_perm = DenseMultilinearExtension::from_evaluations_vec(num_vars, s_perm_vec);
|
||||||
|
|
||||||
|
let beta = Fr::rand(&mut rng);
|
||||||
|
let gamma = Fr::rand(&mut rng);
|
||||||
|
|
||||||
|
// TODO: also test other 4 polynomials
|
||||||
|
let res = compute_products(&beta, &gamma, &w, &s_id, &s_perm)?;
|
||||||
|
|
||||||
|
for i in 0..1 << num_vars {
|
||||||
|
let r: Vec<Fr> = bit_decompose(i, num_vars)
|
||||||
|
.iter()
|
||||||
|
.map(|&x| Fr::from(x))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let eval = res[1].evaluate(&r).unwrap();
|
||||||
|
|
||||||
|
let w_eval = w.evaluate(&r).unwrap();
|
||||||
|
let s_id_eval = s_id.evaluate(&r).unwrap();
|
||||||
|
let s_perm_eval = s_perm.evaluate(&r).unwrap();
|
||||||
|
let eval_rec =
|
||||||
|
(w_eval + beta * s_id_eval + gamma) / (w_eval + beta * s_perm_eval + gamma);
|
||||||
|
|
||||||
|
assert_eq!(eval, eval_rec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,8 @@ macro_rules! to_bytes {
|
|||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
/// Decompose an integer into a binary vector in little endian.
|
||||||
|
#[allow(dead_code)]
|
||||||
pub(crate) fn bit_decompose(input: u64, num_var: usize) -> Vec<bool> {
|
pub(crate) fn bit_decompose(input: u64, num_var: usize) -> Vec<bool> {
|
||||||
let mut res = Vec::with_capacity(num_var);
|
let mut res = Vec::with_capacity(num_var);
|
||||||
let mut i = input;
|
let mut i = input;
|
||||||
@@ -22,14 +23,79 @@ pub(crate) fn bit_decompose(input: u64, num_var: usize) -> Vec<bool> {
|
|||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
/// Project a little endian binary vector into an integer.
|
||||||
fn test_to_bytes() {
|
#[allow(dead_code)]
|
||||||
|
pub(crate) fn project(input: &[bool]) -> u64 {
|
||||||
|
let mut res = 0;
|
||||||
|
for &e in input.iter().rev() {
|
||||||
|
res <<= 1;
|
||||||
|
res += e as u64;
|
||||||
|
}
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
// Input index
|
||||||
|
// - `i := (i_0, ...i_{n-1})`,
|
||||||
|
// - `num_vars := n`
|
||||||
|
// return three elements:
|
||||||
|
// - `x0 := (i_1, ..., i_{n-1}, 0)`
|
||||||
|
// - `x1 := (i_1, ..., i_{n-1}, 1)`
|
||||||
|
// - `sign := i_0`
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn get_index(i: usize, num_vars: usize) -> (usize, usize, bool) {
|
||||||
|
let bit_sequence = bit_decompose(i as u64, num_vars);
|
||||||
|
|
||||||
|
// the last bit comes first here because of LE encoding
|
||||||
|
let x0 = project(&[[false].as_ref(), bit_sequence[..num_vars - 1].as_ref()].concat()) as usize;
|
||||||
|
let x1 = project(&[[true].as_ref(), bit_sequence[..num_vars - 1].as_ref()].concat()) as usize;
|
||||||
|
|
||||||
|
(x0, x1, bit_sequence[num_vars - 1])
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::{bit_decompose, get_index, project};
|
||||||
use ark_bls12_381::Fr;
|
use ark_bls12_381::Fr;
|
||||||
use ark_serialize::CanonicalSerialize;
|
use ark_serialize::CanonicalSerialize;
|
||||||
use ark_std::One;
|
use ark_std::{rand::RngCore, test_rng, One};
|
||||||
let f1 = Fr::one();
|
|
||||||
|
|
||||||
let mut bytes = ark_std::vec![];
|
#[test]
|
||||||
f1.serialize(&mut bytes).unwrap();
|
fn test_to_bytes() {
|
||||||
assert_eq!(bytes, to_bytes!(&f1).unwrap());
|
let f1 = Fr::one();
|
||||||
|
|
||||||
|
let mut bytes = ark_std::vec![];
|
||||||
|
f1.serialize(&mut bytes).unwrap();
|
||||||
|
assert_eq!(bytes, to_bytes!(&f1).unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_decomposition() {
|
||||||
|
let mut rng = test_rng();
|
||||||
|
for _ in 0..100 {
|
||||||
|
let t = rng.next_u64();
|
||||||
|
let b = bit_decompose(t, 64);
|
||||||
|
let r = project(&b);
|
||||||
|
assert_eq!(t, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_index() {
|
||||||
|
let a = 0b1010;
|
||||||
|
let (x0, x1, sign) = get_index(a, 4);
|
||||||
|
assert_eq!(x0, 0b0100);
|
||||||
|
assert_eq!(x1, 0b0101);
|
||||||
|
assert!(sign);
|
||||||
|
|
||||||
|
let (x0, x1, sign) = get_index(a, 5);
|
||||||
|
assert_eq!(x0, 0b10100);
|
||||||
|
assert_eq!(x1, 0b10101);
|
||||||
|
assert!(!sign);
|
||||||
|
|
||||||
|
let a = 0b1111;
|
||||||
|
let (x0, x1, sign) = get_index(a, 4);
|
||||||
|
assert_eq!(x0, 0b1110);
|
||||||
|
assert_eq!(x1, 0b1111);
|
||||||
|
assert!(sign);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user