Skip to content

Commit

Permalink
Add option for algebraic batching to build DEEP polynomial (#357)
Browse files Browse the repository at this point in the history
  • Loading branch information
Al-Kindi-0 authored Jan 31, 2025
1 parent 02345d7 commit 89b6d60
Show file tree
Hide file tree
Showing 24 changed files with 227 additions and 420 deletions.
74 changes: 65 additions & 9 deletions air/src/air/coefficients.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@

use alloc::vec::Vec;

use math::FieldElement;
use crypto::{RandomCoin, RandomCoinError};
use math::{get_power_series, get_power_series_with_offset, FieldElement};

// CONSTRAINT COMPOSITION COEFFICIENTS
// ================================================================================================
Expand Down Expand Up @@ -69,19 +70,19 @@ pub struct LagrangeConstraintsCompositionCoefficients<E: FieldElement> {
/// * $\alpha_i$ is a composition coefficient for the $i$th trace polynomial.
/// * $\beta_j$ is a composition coefficient for the $j$th constraint column polynomial.
///
/// The soundness of the resulting protocol with batching as above is given in Theorem 8 in
/// https://eprint.iacr.org/2022/1216 and it relies on two points:
/// The soundness of the resulting protocol is given in Theorem 8 in https://eprint.iacr.org/2022/1216
/// and it relies on the following points:
///
///
/// 1. The evaluation proofs for each trace polynomial at $z$ and $g \cdot z$ can be batched using
/// the non-normalized Lagrange kernel over the set $\{z, g \cdot z\}$. This, however, requires
/// that the FRI protocol is run with a larger agreement parameter
/// $\alpha^{+} = (1 + 1/2m)\cdot\sqrt{\rho^{+}}$ where $\rho^{+} := \frac{\kappa + 2}{\nu}$,
/// $\kappa$ and $\nu$ are the length of the execution trace and the LDE domain size,
/// respectively.
/// that the FRI protocol is run with a larger agreement parameter.
/// 2. The resulting $Y(x)$ do not need to be degree adjusted but the soundness error of the
/// protocol needs to be updated. For most combinations of batching parameters, this leads to a
/// negligible increase in soundness error. The formula for the updated error can be found in
/// Theorem 8 of https://eprint.iacr.org/2022/1216.
/// 3. The error will depend on the batching used in building the DEEP polynomial. More precisely,
/// when using algebraic batching there is a loss of log_2(k + m - 1) bits of soundness.
///
/// In the case when the trace polynomials contain a trace polynomial corresponding to a Lagrange
/// kernel column, the above expression of $Y(x)$ includes the additional term given by
Expand All @@ -99,8 +100,8 @@ pub struct LagrangeConstraintsCompositionCoefficients<E: FieldElement> {
/// 4. $p_S(X)$ is the polynomial of minimal degree interpolating the set ${(a, T_l(a)): a \in S}$.
/// 5. $Z_S(X)$ is the polynomial of minimal degree vanishing over the set $S$.
///
/// Note that, if a Lagrange kernel trace polynomial is present, then $\rho^{+}$ from above should
/// be updated to be $\rho^{+} := \frac{\kappa + log_2(\nu) + 1}{\nu}$.
/// Note that, if a Lagrange kernel trace polynomial is present, then the agreement parameter needs
/// to be adapted accordingly.
#[derive(Debug, Clone)]
pub struct DeepCompositionCoefficients<E: FieldElement> {
/// Trace polynomial composition coefficients $\alpha_i$.
Expand All @@ -110,3 +111,58 @@ pub struct DeepCompositionCoefficients<E: FieldElement> {
/// Lagrange kernel trace polynomial composition coefficient $\gamma$.
pub lagrange: Option<E>,
}

impl<E: FieldElement> DeepCompositionCoefficients<E> {
/// Generates the random values used in the construction of the DEEP polynomial when linear
/// batching is used.
pub fn draw_linear(
public_coin: &mut impl RandomCoin<BaseField = E::BaseField>,
trace_width: usize,
num_constraint_composition_columns: usize,
) -> Result<Self, RandomCoinError> {
let mut t_coefficients = Vec::new();
for _ in 0..trace_width {
t_coefficients.push(public_coin.draw()?);
}

let mut c_coefficients = Vec::new();
for _ in 0..num_constraint_composition_columns {
c_coefficients.push(public_coin.draw()?);
}

Ok(DeepCompositionCoefficients {
trace: t_coefficients,
constraints: c_coefficients,
lagrange: None,
})
}

/// Generates the random values used in the construction of the DEEP polynomial when algebraic
/// batching is used.
pub fn draw_algebraic(
public_coin: &mut impl RandomCoin<BaseField = E::BaseField>,
trace_width: usize,
num_constraint_composition_columns: usize,
) -> Result<Self, RandomCoinError> {
let mut t_coefficients = Vec::new();
let alpha: E = public_coin.draw()?;
t_coefficients.extend_from_slice(&get_power_series(alpha, trace_width));

let mut c_coefficients = Vec::new();

let alpha_pow_trace_width = alpha.exp((trace_width as u32).into());
c_coefficients.extend_from_slice(&get_power_series_with_offset(
alpha,
alpha_pow_trace_width,
num_constraint_composition_columns,
));

assert_eq!(t_coefficients[trace_width - 1] * alpha, c_coefficients[0]);

Ok(DeepCompositionCoefficients {
trace: t_coefficients,
constraints: c_coefficients,
lagrange: None,
})
}
}
33 changes: 12 additions & 21 deletions air/src/air/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use alloc::{collections::BTreeMap, vec::Vec};
use crypto::{RandomCoin, RandomCoinError};
use math::{fft, ExtensibleField, ExtensionOf, FieldElement, StarkField, ToElements};

use crate::ProofOptions;
use crate::{BatchingMethod, ProofOptions};

mod aux;
pub use aux::{AuxRandElements, GkrRandElements, GkrVerifier};
Expand Down Expand Up @@ -578,26 +578,17 @@ pub trait Air: Send + Sync {
E: FieldElement<BaseField = Self::BaseField>,
R: RandomCoin<BaseField = Self::BaseField>,
{
let mut t_coefficients = Vec::new();
for _ in 0..self.trace_info().width() {
t_coefficients.push(public_coin.draw()?);
match self.context().options.deep_poly_batching_method() {
BatchingMethod::Linear => DeepCompositionCoefficients::draw_linear(
public_coin,
self.trace_info().width(),
self.context().num_constraint_composition_columns(),
),
BatchingMethod::Algebraic => DeepCompositionCoefficients::draw_algebraic(
public_coin,
self.trace_info().width(),
self.context().num_constraint_composition_columns(),
),
}

let mut c_coefficients = Vec::new();
for _ in 0..self.context().num_constraint_composition_columns() {
c_coefficients.push(public_coin.draw()?);
}

let lagrange_cc = if self.context().has_lagrange_kernel_aux_column() {
Some(public_coin.draw()?)
} else {
None
};

Ok(DeepCompositionCoefficients {
trace: t_coefficients,
constraints: c_coefficients,
lagrange: lagrange_cc,
})
}
}
8 changes: 4 additions & 4 deletions air/src/air/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use super::{
Air, AirContext, Assertion, EvaluationFrame, ProofOptions, TraceInfo,
TransitionConstraintDegree,
};
use crate::FieldExtension;
use crate::{options::BatchingMethod, FieldExtension};

// PERIODIC COLUMNS
// ================================================================================================
Expand Down Expand Up @@ -205,7 +205,7 @@ impl MockAir {
let mut result = Self::new(
TraceInfo::with_meta(4, trace_length, vec![1]),
(),
ProofOptions::new(32, 8, 0, FieldExtension::None, 4, 31),
ProofOptions::new(32, 8, 0, FieldExtension::None, 4, 31, BatchingMethod::Linear),
);
result.periodic_columns = column_values;
result
Expand All @@ -215,7 +215,7 @@ impl MockAir {
let mut result = Self::new(
TraceInfo::with_meta(4, trace_length, vec![assertions.len() as u8]),
(),
ProofOptions::new(32, 8, 0, FieldExtension::None, 4, 31),
ProofOptions::new(32, 8, 0, FieldExtension::None, 4, 31, BatchingMethod::Linear),
);
result.assertions = assertions;
result
Expand Down Expand Up @@ -267,7 +267,7 @@ pub fn build_context<B: StarkField>(
trace_width: usize,
num_assertions: usize,
) -> AirContext<B> {
let options = ProofOptions::new(32, 8, 0, FieldExtension::None, 4, 31);
let options = ProofOptions::new(32, 8, 0, FieldExtension::None, 4, 31, BatchingMethod::Linear);
let t_degrees = vec![TransitionConstraintDegree::new(2)];
let trace_info = TraceInfo::new(trace_width, trace_length);
AirContext::new(trace_info, t_degrees, num_assertions, options)
Expand Down
2 changes: 1 addition & 1 deletion air/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ mod errors;
pub use errors::AssertionError;

mod options;
pub use options::{FieldExtension, PartitionOptions, ProofOptions};
pub use options::{BatchingMethod, FieldExtension, PartitionOptions, ProofOptions};

mod air;
pub use air::{
Expand Down
72 changes: 72 additions & 0 deletions air/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ pub enum FieldExtension {
/// 4. Grinding factor - higher values increase proof soundness, but also may increase proof
/// generation time. More precisely, conjectured proof soundness is bounded by
/// `num_queries * log2(blowup_factor) + grinding_factor`.
/// 5. Batching type - either independent random values per multi-point quotient are used in
/// the computation of the DEEP polynomial or powers of a single random value are used
/// instead. The first type of batching is called `Linear` while the second is called `Algebraic`.
///
/// Another important parameter in defining STARK security level, which is not a part of [ProofOptions]
/// is the hash function used in the protocol. The soundness of a STARK proof is limited by the
Expand All @@ -91,6 +94,7 @@ pub struct ProofOptions {
field_extension: FieldExtension,
fri_folding_factor: u8,
fri_remainder_max_degree: u8,
batching_deep: BatchingMethod,
partition_options: PartitionOptions,
}

Expand Down Expand Up @@ -125,6 +129,7 @@ impl ProofOptions {
field_extension: FieldExtension,
fri_folding_factor: usize,
fri_remainder_max_degree: usize,
batching_deep: BatchingMethod,
) -> ProofOptions {
// TODO: return errors instead of panicking
assert!(num_queries > 0, "number of queries must be greater than 0");
Expand Down Expand Up @@ -166,6 +171,7 @@ impl ProofOptions {
fri_folding_factor: fri_folding_factor as u8,
fri_remainder_max_degree: fri_remainder_max_degree as u8,
partition_options: PartitionOptions::new(1, 1),
batching_deep,
}
}

Expand Down Expand Up @@ -246,6 +252,19 @@ impl ProofOptions {
pub fn partition_options(&self) -> PartitionOptions {
self.partition_options
}

/// Returns the `[BatchingMethod]` defining the method used for batching the multi-point quotients
/// defining the DEEP polynomial.
///
/// Linear batching implies that independently drawn random values per multi-point quotient
/// will be used to do the batching, while Algebraic batching implies that powers of a single
/// random value are used.
///
/// Depending on other parameters, Algebraic batching may lead to a small reduction in the security
/// level of the generated proofs, but avoids extra calls to the random oracle (i.e., hash function).
pub fn deep_poly_batching_method(&self) -> BatchingMethod {
self.batching_deep
}
}

impl<E: StarkField> ToElements<E> for ProofOptions {
Expand Down Expand Up @@ -275,6 +294,7 @@ impl Serializable for ProofOptions {
target.write_u8(self.fri_remainder_max_degree);
target.write_u8(self.partition_options.num_partitions);
target.write_u8(self.partition_options.hash_rate);
target.write(self.batching_deep);
}
}

Expand All @@ -291,6 +311,7 @@ impl Deserializable for ProofOptions {
FieldExtension::read_from(source)?,
source.read_u8()? as usize,
source.read_u8()? as usize,
BatchingMethod::read_from(source)?,
);
Ok(result.with_partitions(source.read_u8()? as usize, source.read_u8()? as usize))
}
Expand Down Expand Up @@ -406,6 +427,55 @@ impl Default for PartitionOptions {
}
}

// BATCHING METHOD
// ================================================================================================

/// Represents the type of batching, using randomness, used in the construction of the DEEP
/// composition polynomial.
///
/// There are currently two types of batching supported:
///
/// 1. Linear, also called affine, where the resulting expression is a multivariate polynomial of
/// total degree 1 in each of the random values.
/// 2. Algebraic, also called parametric or curve batching, where the resulting expression is
/// a univariate polynomial in one random value.
///
/// The main difference between the two types is that algebraic batching has low verifier randomness
/// complexity and hence is light on the number of calls to the random oracle. However, this comes
/// at the cost of a slight degradation in the soundness of the FRI protocol, on the order of
/// log2(N - 1) where N is the number of code words being batched. Linear batching does not suffer
/// from such a degradation but has linear verifier randomness complexity in the number of terms
/// being batched.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(u8)]
pub enum BatchingMethod {
Linear = 0,
Algebraic = 1,
}

impl Serializable for BatchingMethod {
/// Serializes `self` and writes the resulting bytes into the `target`.
fn write_into<W: ByteWriter>(&self, target: &mut W) {
target.write_u8(*self as u8);
}
}

impl Deserializable for BatchingMethod {
/// Reads [BatchingMethod] from the specified `source` and returns the result.
///
/// # Errors
/// Returns an error if the value does not correspond to a valid [BatchingMethod].
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
match source.read_u8()? {
0 => Ok(BatchingMethod::Linear),
1 => Ok(BatchingMethod::Algebraic),
n => Err(DeserializationError::InvalidValue(format!(
"value {n} cannot be deserialized as a BatchingMethod enum"
))),
}
}
}

// TESTS
// ================================================================================================

Expand All @@ -414,6 +484,7 @@ mod tests {
use math::fields::{f64::BaseElement, CubeExtension};

use super::{FieldExtension, PartitionOptions, ProofOptions, ToElements};
use crate::options::BatchingMethod;

#[test]
fn proof_options_to_elements() {
Expand Down Expand Up @@ -444,6 +515,7 @@ mod tests {
field_extension,
fri_folding_factor as usize,
fri_remainder_max_degree as usize,
BatchingMethod::Linear,
);
assert_eq!(expected, options.to_elements());
}
Expand Down
4 changes: 3 additions & 1 deletion air/src/proof/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ mod tests {
use math::fields::f64::BaseElement;

use super::{Context, ProofOptions, ToElements, TraceInfo};
use crate::FieldExtension;
use crate::{options::BatchingMethod, FieldExtension};

#[test]
fn context_to_elements() {
Expand All @@ -170,6 +170,7 @@ mod tests {
let grinding_factor = 20;
let blowup_factor = 8;
let num_queries = 30;
let batching_deep = BatchingMethod::Linear;

let main_width = 20;
let aux_width = 9;
Expand Down Expand Up @@ -212,6 +213,7 @@ mod tests {
field_extension,
fri_folding_factor as usize,
fri_remainder_max_degree as usize,
batching_deep,
);
let trace_info =
TraceInfo::new_multi_segment(main_width, aux_width, aux_rands, trace_length, vec![]);
Expand Down
4 changes: 2 additions & 2 deletions air/src/proof/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use math::FieldElement;
use security::{ConjecturedSecurity, ProvenSecurity};
use utils::{ByteReader, Deserializable, DeserializationError, Serializable, SliceReader};

use crate::{ProofOptions, TraceInfo};
use crate::{options::BatchingMethod, ProofOptions, TraceInfo};

mod context;
pub use context::Context;
Expand Down Expand Up @@ -146,7 +146,7 @@ impl Proof {
Self {
context: Context::new::<DummyField>(
TraceInfo::new(1, 8),
ProofOptions::new(1, 2, 2, FieldExtension::None, 8, 1),
ProofOptions::new(1, 2, 2, FieldExtension::None, 8, 1, BatchingMethod::Linear),
),
num_unique_queries: 0,
commitments: Commitments::default(),
Expand Down
Loading

0 comments on commit 89b6d60

Please sign in to comment.