Skip to content

Commit

Permalink
pcs-change (#130)
Browse files Browse the repository at this point in the history
* add code distance test

* debug for code's matrix dimensions

* simplication of lagrange basis

* fmt

* debug for and change into directly computing optimal matrix dimensions

* debug for and change into directly computing optimal matrix dimensions

* matrix dimension opt considering extension field

* fmt

* handle special condition that polynomial is too small

* simplify

---------

Co-authored-by: Xiang Xie <[email protected]>
  • Loading branch information
tnyuzg and xiangxiecrypto authored Aug 21, 2024
1 parent eb8e633 commit 0ecee58
Show file tree
Hide file tree
Showing 7 changed files with 98 additions and 53 deletions.
2 changes: 1 addition & 1 deletion pcs/benches/brakedown_pcs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ type Hash = Sha256;
const BASE_FIELD_BITS: usize = 31;

pub fn criterion_benchmark(c: &mut Criterion) {
let num_vars = 20;
let num_vars = 24;
let evaluations: Vec<FF> = rand::thread_rng()
.sample_iter(FieldUniformSampler::new())
.take(1 << num_vars)
Expand Down
64 changes: 32 additions & 32 deletions pcs/src/multilinear/brakedown/data_structure.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use std::marker::PhantomData;

use algebra::{utils::Prg, AbstractExtensionField, Field};
use itertools::Itertools;
use serde::{Deserialize, Serialize};

use crate::utils::{
Expand All @@ -13,20 +12,19 @@ use crate::utils::{
use bincode::Result;

use crate::multilinear::brakedown::BRAKEDOWN_SECURITY_BIT;
use rayon::prelude::*;

/// Define the structure of Brakedown parameters.
#[derive(Serialize, Deserialize)]
pub struct BrakedownParams<F: Field, C: LinearCode<F>> {
pub struct BrakedownParams<F: Field, EF: AbstractExtensionField<F>, C: LinearCode<F>> {
security_bit: usize,
num_vars: usize,
num_rows: usize,
num_cols: usize,
code: C,
_marker: PhantomData<F>,
_marker: PhantomData<(F, EF)>,
}

impl<F: Field, C: LinearCode<F>> BrakedownParams<F, C> {
impl<F: Field, EF: AbstractExtensionField<F>, C: LinearCode<F>> BrakedownParams<F, EF, C> {
/// Create a new instance.
///
/// # Arguments
Expand All @@ -41,37 +39,34 @@ impl<F: Field, C: LinearCode<F>> BrakedownParams<F, C> {
let estimated_queries = |distance: f64, gap: f64| {
ceil(-(BRAKEDOWN_SECURITY_BIT as f64) / (1.0 - distance * gap).log2())
};
let num_queries = estimated_queries(
code_spec.distance().unwrap(),
code_spec.proximity_gap().unwrap(),
);

// Estimated proof size.
let estimated_proof_size = |codeword_len: usize, l: usize, msg_len: usize| {
codeword_len + l * (1 << num_vars) / msg_len
};
let estimated_proof_size =
|msg_len: usize| msg_len * EF::D + num_queries * (1 << num_vars) / msg_len;

// estimated proof size := num_cols + num_queries * num_rows = D * num_cols + (num_queries * (2 ^ num_vars)) / num_cols
// since message is on extension field
// optimal num_cols is the closest power of 2 to ((2 ^ num_vars) * num_queries / D) ^ (1/2)

let sqrt = (((1 << num_vars) * num_queries / EF::D) as f64).sqrt();
let lower = 1 << sqrt.log2().floor() as u32;
let upper = 1 << sqrt.log2().ceil() as u32;

let mut proof_size = vec![0usize; num_vars - 4];
let msg_lens: Vec<usize> = (4..num_vars).collect();

msg_lens
.par_iter()
.zip(proof_size.par_iter_mut())
.for_each(|(msg_len, size)| {
let mut rng = Prg::new();
let msg_len = 1 << msg_len;
let code = code_spec.code(msg_len, &mut rng);
*size = estimated_proof_size(
code.codeword_len(),
estimated_queries(code.distance(), code.proximity_gap()),
msg_len,
);
});

let index = proof_size
.iter()
.position_min()
.expect("can not find smallest proof size");

let num_cols = 1 << msg_lens[index];
let mut num_cols = if estimated_proof_size(lower) < estimated_proof_size(upper) {
lower
} else {
upper
};
if num_cols > (1 << num_vars) {
num_cols = 1 << num_vars;
}

let num_rows = (1 << num_vars) / num_cols;

let code = code_spec.code(num_cols, &mut Prg::new());

Self {
Expand Down Expand Up @@ -115,7 +110,12 @@ impl<F: Field, C: LinearCode<F>> BrakedownParams<F, C> {
}
}

impl<F: Field, C: LinearCode<F> + Serialize + for<'de> Deserialize<'de>> BrakedownParams<F, C> {
impl<
F: Field,
EF: AbstractExtensionField<F>,
C: LinearCode<F> + Serialize + for<'de> Deserialize<'de>,
> BrakedownParams<F, EF, C>
{
/// Convert into bytes.
pub fn to_bytes(&self) -> Result<Vec<u8>> {
bincode::serialize(&self)
Expand Down
20 changes: 10 additions & 10 deletions pcs/src/multilinear/brakedown/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ where
/// The computation of the product can be viewed as a linear combination of rows
/// of the matrix with challenge vector as the coefficients.
fn answer_challenge(
pp: &BrakedownParams<F, C>,
pp: &BrakedownParams<F, EF, C>,
challenge: &[EF],
state: &BrakedownCommitmentState<F, H>,
) -> Vec<EF> {
Expand Down Expand Up @@ -80,7 +80,7 @@ where
/// Prover answers the query of columns of given indices
/// and gives merkle paths as the proof of its consistency with the merkle root.
fn answer_queries(
pp: &BrakedownParams<F, C>,
pp: &BrakedownParams<F, EF, C>,
queries: &[usize],
state: &BrakedownCommitmentState<F, H>,
) -> (Vec<H::Output>, Vec<F>) {
Expand All @@ -107,7 +107,7 @@ where

/// Decompose an evaluation of point x into two tensors q1, q2 such that
/// f(x) = q1 * M * q2 where M is the committed matrix.
fn tensor_decompose(pp: &BrakedownParams<F, C>, point: &[EF]) -> (Vec<EF>, Vec<EF>) {
fn tensor_decompose(pp: &BrakedownParams<F, EF, C>, point: &[EF]) -> (Vec<EF>, Vec<EF>) {
let left_point_len = pp.num_rows().ilog2() as usize;
let right_point_len = pp.code().message_len().ilog2() as usize;
assert_eq!(left_point_len + right_point_len, point.len());
Expand All @@ -123,14 +123,14 @@ where
}

/// Generate tensor from points.
fn tensor_from_points(pp: &BrakedownParams<F, C>, points: &[EF]) -> Vec<EF> {
fn tensor_from_points(pp: &BrakedownParams<F, EF, C>, points: &[EF]) -> Vec<EF> {
let len = pp.num_vars() - pp.num_rows().ilog2() as usize;
lagrange_basis(&points[len..])
}

/// Check the merkle paths and consistency
fn check_query_answers(
pp: &BrakedownParams<F, C>,
pp: &BrakedownParams<F, EF, C>,
challenge: &[EF],
queries: &[usize],
encoded_rlc_msg: &[EF],
Expand All @@ -155,7 +155,7 @@ where
/// Check the hash of column is the same as the merkle leave.
/// Check the merkle paths are consistent with the merkle root.
fn check_merkle(
pp: &BrakedownParams<F, C>,
pp: &BrakedownParams<F, EF, C>,
queries: &[usize],
merkle_paths: &[H::Output],
columns: &[F],
Expand Down Expand Up @@ -186,7 +186,7 @@ where

/// Check the consistency of entries
fn check_consistency(
pp: &BrakedownParams<F, C>,
pp: &BrakedownParams<F, EF, C>,
queries: &[usize],
challenge: &[EF],
encoded_rlc_msg: &[EF],
Expand Down Expand Up @@ -227,7 +227,7 @@ where
EF: AbstractExtensionField<F>,
{
/// Generate random queries.
fn random_queries(pp: &BrakedownParams<F, C>, trans: &mut Transcript<F>) -> Vec<usize> {
fn random_queries(pp: &BrakedownParams<F, EF, C>, trans: &mut Transcript<F>) -> Vec<usize> {
let num_queries = pp.num_query();
let codeword_len = pp.code().codeword_len();

Expand All @@ -252,7 +252,7 @@ where
S: LinearCodeSpec<F, Code = C>,
EF: AbstractExtensionField<F> + Serialize + for<'de> Deserialize<'de>,
{
type Parameters = BrakedownParams<F, C>;
type Parameters = BrakedownParams<F, EF, C>;
type Polynomial = DenseMultilinearExtension<F>;
type Commitment = BrakedownPolyCommitment<H>;
type CommitmentState = BrakedownCommitmentState<F, H>;
Expand All @@ -261,7 +261,7 @@ where

fn setup(num_vars: usize, code_spec: Option<S>) -> Self::Parameters {
let code_spec = code_spec.expect("Need a code spec");
BrakedownParams::<F, C>::new(num_vars, code_spec)
BrakedownParams::<F, EF, C>::new(num_vars, code_spec)
}

fn commit(
Expand Down
13 changes: 5 additions & 8 deletions pcs/src/utils/arithmetic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,17 +168,14 @@ pub fn div_ceil(dividend: usize, divisor: usize) -> usize {
pub fn lagrange_basis<F: Field, EF: AbstractExtensionField<F>>(points: &[EF]) -> Vec<EF> {
let mut basis = vec![EF::one()];
points.iter().for_each(|point| {
basis.extend(
basis
.iter()
.map(|x| *x * (EF::one() - point))
.collect::<Vec<EF>>(),
);
basis.extend(basis.iter().map(|x| *x * point).collect::<Vec<EF>>());
let prev_len = basis.len() >> 1;
basis.iter_mut().take(prev_len).for_each(|x| *x *= point);
basis
.iter_mut()
.take(prev_len)
.for_each(|x| *x *= EF::one() - point);
});
assert!(basis.len() == 1 << points.len());

basis.reverse();
basis
}
11 changes: 10 additions & 1 deletion pcs/src/utils/code/expander.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ impl ExpanderCodeSpec {
.iter()
.map(|a| {
let n_prime = ceil(a.num_col as f64 * self.r);
let m_prime = ceil(a.num_row as f64 * self.r) - a.num_col - n_prime;
let m_prime = ceil(a.num_row as f64 * self.r) - a.num_row - n_prime;
SparseMatrixDimension::new(
n_prime,
m_prime,
Expand Down Expand Up @@ -196,9 +196,18 @@ impl ExpanderCodeSpec {

impl<F: Field> LinearCodeSpec<F> for ExpanderCodeSpec {
type Code = ExpanderCode<F>;

fn code(&self, message_len: usize, rng: &mut (impl Rng + CryptoRng)) -> Self::Code {
ExpanderCode::<F>::new(self.clone(), message_len, rng)
}

fn distance(&self) -> Result<f64, String> {
Ok(self.distance())
}

fn proximity_gap(&self) -> Result<f64, String> {
Ok(self.proximity_gap())
}
}

/// Define the struct of linear expander code.
Expand Down
4 changes: 4 additions & 0 deletions pcs/src/utils/code/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,8 @@ pub trait LinearCodeSpec<F>: Sync + Send + Default {
type Code: LinearCode<F>;
/// Generate the instance of linear code
fn code(&self, message_ln: usize, rng: &mut (impl Rng + CryptoRng)) -> Self::Code;
/// Distance of the linear code when avaible from linear code specification
fn distance(&self) -> Result<f64, String>;
/// Proximity gap of the linear code when avaible from linear code specification
fn proximity_gap(&self) -> Result<f64, String>;
}
37 changes: 36 additions & 1 deletion pcs/tests/test_code.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use algebra::{derive::*, FieldUniformSampler};
use num_traits::Zero;
use num_traits::{One, Zero};
use pcs::utils::code::{ExpanderCode, ExpanderCodeSpec, LinearCode};
use rand::Rng;

Expand Down Expand Up @@ -60,3 +60,38 @@ fn linearity_check() {
assert!(num_notequal == 0);
}
}

/// test the real code distance is larger or equal to the ideal minimum code distance
/// two random codewords have large distance to each other with high probability, making it inefficient to find two closed codewords
/// the test iteratively adds 1 to the first element of the message and compare its codeword to the original codeword
#[test]
fn code_distance_test() {
let mut rng = rand::thread_rng();
let field_distr = FieldUniformSampler::new();

let spec = ExpanderCodeSpec::new(0.1195, 0.0284, 1.420, 31, 30);
let brakedown_code: ExpanderCode<FF32> = ExpanderCode::new(spec, 5000, &mut rng);

let message_len = brakedown_code.message_len;
let codeword_len = brakedown_code.codeword_len;

let mut target_0 = vec![FF32::zero(); codeword_len];
target_0[..message_len]
.iter_mut()
.for_each(|x| *x = rng.sample(field_distr));

let mut target_1 = target_0.clone();
let check_times = 30;
for _ in 0..check_times {
target_1[0] += FF32::one();
brakedown_code.encode(&mut target_0);
brakedown_code.encode(&mut target_1);
let num_real: usize = target_0
.iter()
.zip(target_1.iter())
.map(|(x_0, x_1)| (x_0 != x_1) as usize)
.sum();
let num_expected = (brakedown_code.distance() * codeword_len as f64) as usize;
assert!(num_real >= num_expected);
}
}

0 comments on commit 0ecee58

Please sign in to comment.