,
+ pub fn new(
+ context: &AirContext,
main_assertions: Vec>,
aux_assertions: Vec>,
composition_coefficients: &[E],
@@ -88,7 +88,7 @@ impl BoundaryConstraints {
);
let trace_length = context.trace_info.length();
- let main_trace_width = context.trace_info.main_trace_width();
+ let main_trace_width = context.trace_info.main_segment_width();
let aux_trace_width = context.trace_info.aux_segment_width();
// make sure the assertions are valid in the context of their respective trace segments;
@@ -151,9 +151,9 @@ impl BoundaryConstraints {
/// Translates the provided assertions into boundary constraints, groups the constraints by their
/// divisor, and sorts the resulting groups by the degree adjustment factor.
-fn group_constraints(
+fn group_constraints(
assertions: Vec>,
- context: &AirContext,
+ context: &AirContext,
composition_coefficients: &[E],
inv_g: F::BaseField,
twiddle_map: &mut BTreeMap>,
diff --git a/air/src/air/coefficients.rs b/air/src/air/coefficients.rs
index b82b2ac6b..ed6c3fa99 100644
--- a/air/src/air/coefficients.rs
+++ b/air/src/air/coefficients.rs
@@ -27,11 +27,19 @@ use math::FieldElement;
///
/// The coefficients are separated into two lists: one for transition constraints and another one
/// for boundary constraints. This separation is done for convenience only.
+///
+/// In addition to the above, and when LogUp-GKR is enabled, there are two extra sets of
+/// constraint composition coefficients that are used, namely for:
+///
+/// 1. Lagrange kernel constraints, which include both transition and boundary constraints.
+/// 2. S-column constraint, which is used in implementing the cohomological sum-check argument
+/// of https://eprint.iacr.org/2021/930
#[derive(Debug, Clone)]
pub struct ConstraintCompositionCoefficients {
pub transition: Vec,
pub boundary: Vec,
pub lagrange: Option>,
+ pub s_col: Option,
}
/// Stores the constraint composition coefficients for the Lagrange kernel transition and boundary
@@ -83,8 +91,9 @@ pub struct LagrangeConstraintsCompositionCoefficients {
/// negligible increase in soundness error. The formula for the updated error can be found in
/// Theorem 8 of https://eprint.iacr.org/2022/1216.
///
-/// 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
+/// In the case when LogUp-GKR is enabled, the trace polynomials contain an additional trace
+/// polynomial corresponding to a Lagrange kernel column and the above expression of $Y(x)$
+/// includes the additional term given by
///
/// $$
/// \gamma \cdot \frac{T_l(x) - p_S(x)}{Z_S(x)}
@@ -99,8 +108,13 @@ pub struct LagrangeConstraintsCompositionCoefficients {
/// 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 when LogUp-GKR is enabled, we also have to take into account an additional column,
+/// called s-column throughout, used in implementing the univariate IOP for multi-linear evaluation.
+/// This means that we need and additional random value, in addition to $\gamma$ above, when
+/// LogUp-GKR is enabled.
+///
+/// Note that, when LogUp-GKR is enabled, $\rho^{+}$ from above should be updated to be
+/// $\rho^{+} := \frac{\kappa + log_2(\nu) + 1}{\nu}$.
#[derive(Debug, Clone)]
pub struct DeepCompositionCoefficients {
/// Trace polynomial composition coefficients $\alpha_i$.
@@ -109,4 +123,6 @@ pub struct DeepCompositionCoefficients {
pub constraints: Vec,
/// Lagrange kernel trace polynomial composition coefficient $\gamma$.
pub lagrange: Option,
+ /// S-column trace polynomial composition coefficient.
+ pub s_col: Option,
}
diff --git a/air/src/air/context.rs b/air/src/air/context.rs
index 183f575fc..a4074036a 100644
--- a/air/src/air/context.rs
+++ b/air/src/air/context.rs
@@ -14,21 +14,21 @@ use crate::{air::TransitionConstraintDegree, ProofOptions, TraceInfo};
// ================================================================================================
/// STARK parameters and trace properties for a specific execution of a computation.
#[derive(Clone, PartialEq, Eq)]
-pub struct AirContext {
+pub struct AirContext {
pub(super) options: ProofOptions,
pub(super) trace_info: TraceInfo,
+ pub(super) pub_inputs: P,
pub(super) main_transition_constraint_degrees: Vec,
pub(super) aux_transition_constraint_degrees: Vec,
pub(super) num_main_assertions: usize,
pub(super) num_aux_assertions: usize,
- pub(super) lagrange_kernel_aux_column_idx: Option,
pub(super) ce_blowup_factor: usize,
pub(super) trace_domain_generator: B,
pub(super) lde_domain_generator: B,
pub(super) num_transition_exemptions: usize,
}
-impl AirContext {
+impl AirContext {
// CONSTRUCTORS
// --------------------------------------------------------------------------------------------
/// Returns a new instance of [AirContext] instantiated for computations which require a single
@@ -48,6 +48,7 @@ impl AirContext {
/// * `trace_info` describes a multi-segment execution trace.
pub fn new(
trace_info: TraceInfo,
+ pub_inputs: P,
transition_constraint_degrees: Vec,
num_assertions: usize,
options: ProofOptions,
@@ -58,11 +59,11 @@ impl AirContext {
);
Self::new_multi_segment(
trace_info,
+ pub_inputs,
transition_constraint_degrees,
Vec::new(),
num_assertions,
0,
- None,
options,
)
}
@@ -91,11 +92,11 @@ impl AirContext {
/// of the specified transition constraints.
pub fn new_multi_segment(
trace_info: TraceInfo,
+ pub_inputs: P,
main_transition_constraint_degrees: Vec,
aux_transition_constraint_degrees: Vec,
num_main_assertions: usize,
num_aux_assertions: usize,
- lagrange_kernel_aux_column_idx: Option,
options: ProofOptions,
) -> Self {
assert!(
@@ -105,14 +106,16 @@ impl AirContext {
assert!(num_main_assertions > 0, "at least one assertion must be specified");
if trace_info.is_multi_segment() {
- assert!(
- !aux_transition_constraint_degrees.is_empty(),
- "at least one transition constraint degree must be specified for the auxiliary trace segment"
+ if !trace_info.logup_gkr_enabled() {
+ assert!(
+ !aux_transition_constraint_degrees.is_empty(),
+ "at least one transition constraint degree must be specified for the auxiliary trace segment"
);
- assert!(
- num_aux_assertions > 0,
- "at least one assertion must be specified against the auxiliary trace segment"
- );
+ assert!(
+ num_aux_assertions > 0,
+ "at least one assertion must be specified against the auxiliary trace segment"
+ );
+ }
} else {
assert!(
aux_transition_constraint_degrees.is_empty(),
@@ -124,15 +127,6 @@ impl AirContext {
);
}
- // validate Lagrange kernel aux column, if any
- if let Some(lagrange_kernel_aux_column_idx) = lagrange_kernel_aux_column_idx {
- assert!(
- lagrange_kernel_aux_column_idx == trace_info.get_aux_segment_width() - 1,
- "Lagrange kernel column should be the last column of the auxiliary trace: index={}, but aux trace width is {}",
- lagrange_kernel_aux_column_idx, trace_info.get_aux_segment_width()
- );
- }
-
// determine minimum blowup factor needed to evaluate transition constraints by taking
// the blowup factor of the highest degree constraint
let mut ce_blowup_factor = 0;
@@ -161,11 +155,11 @@ impl AirContext {
AirContext {
options,
trace_info,
+ pub_inputs,
main_transition_constraint_degrees,
aux_transition_constraint_degrees,
num_main_assertions,
num_aux_assertions,
- lagrange_kernel_aux_column_idx,
ce_blowup_factor,
trace_domain_generator: B::get_root_of_unity(trace_length.ilog2()),
lde_domain_generator: B::get_root_of_unity(lde_domain_size.ilog2()),
@@ -209,6 +203,10 @@ impl AirContext {
self.trace_info.length() * self.options.blowup_factor()
}
+ pub fn public_inputs(&self) -> &P {
+ &self.pub_inputs
+ }
+
/// Returns the number of transition constraints for a computation, excluding the Lagrange
/// kernel transition constraints, which are managed separately.
///
@@ -230,14 +228,14 @@ impl AirContext {
self.aux_transition_constraint_degrees.len()
}
- /// Returns the index of the auxiliary column which implements the Lagrange kernel, if any
- pub fn lagrange_kernel_aux_column_idx(&self) -> Option {
- self.lagrange_kernel_aux_column_idx
+ /// Returns the index of the auxiliary column which implements the Lagrange kernel, if any.
+ pub fn lagrange_kernel_column_idx(&self) -> Option {
+ self.trace_info.lagrange_kernel_column_idx()
}
- /// Returns true if the auxiliary trace segment contains a Lagrange kernel column
- pub fn has_lagrange_kernel_aux_column(&self) -> bool {
- self.lagrange_kernel_aux_column_idx().is_some()
+ /// Returns true if LogUp-GKR is enabled.
+ pub fn logup_gkr_enabled(&self) -> bool {
+ self.trace_info.logup_gkr_enabled()
}
/// Returns the total number of assertions defined for a computation, excluding the Lagrange
@@ -307,10 +305,8 @@ impl AirContext {
let trace_length = self.trace_len();
let transition_divisior_degree = trace_length - self.num_transition_exemptions();
- // we use the identity: ceil(a/b) = (a + b - 1)/b
let num_constraint_col =
- (highest_constraint_degree - transition_divisior_degree + trace_length - 1)
- / trace_length;
+ (highest_constraint_degree - transition_divisior_degree).div_ceil(trace_length);
cmp::max(num_constraint_col, 1)
}
diff --git a/air/src/air/lagrange/boundary.rs b/air/src/air/logup_gkr/lagrange/boundary.rs
similarity index 84%
rename from air/src/air/lagrange/boundary.rs
rename to air/src/air/logup_gkr/lagrange/boundary.rs
index 5d1954615..3eaad9f5d 100644
--- a/air/src/air/lagrange/boundary.rs
+++ b/air/src/air/logup_gkr/lagrange/boundary.rs
@@ -5,7 +5,7 @@
use math::FieldElement;
-use crate::{LagrangeKernelEvaluationFrame, LagrangeKernelRandElements};
+use super::{LagrangeKernelEvaluationFrame, LagrangeKernelRandElements};
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct LagrangeKernelBoundaryConstraint
@@ -31,27 +31,28 @@ where
}
}
+ /// Returns the constraint composition coefficient for this boundary constraint.
+ pub fn constraint_composition_coefficient(&self) -> E {
+ self.composition_coefficient
+ }
+
/// Returns the evaluation of the boundary constraint at `x`, multiplied by the composition
/// coefficient.
///
/// `frame` is the evaluation frame of the Lagrange kernel column `c`, starting at `c(x)`
pub fn evaluate_at(&self, x: E, frame: &LagrangeKernelEvaluationFrame) -> E {
- let numerator = self.evaluate_numerator_at(frame);
+ let numerator = self.evaluate_numerator_at(frame) * self.composition_coefficient;
let denominator = self.evaluate_denominator_at(x);
numerator / denominator
}
- /// Returns the evaluation of the boundary constraint numerator, multiplied by the composition
- /// coefficient.
+ /// Returns the evaluation of the boundary constraint numerator.
///
/// `frame` is the evaluation frame of the Lagrange kernel column `c`, starting at `c(x)` for
/// some `x`
pub fn evaluate_numerator_at(&self, frame: &LagrangeKernelEvaluationFrame) -> E {
- let trace_value = frame.inner()[0];
- let constraint_evaluation = trace_value - self.assertion_value;
-
- constraint_evaluation * self.composition_coefficient
+ frame[0] - self.assertion_value
}
/// Returns the evaluation of the boundary constraint denominator at point `x`.
diff --git a/air/src/air/lagrange/frame.rs b/air/src/air/logup_gkr/lagrange/frame.rs
similarity index 81%
rename from air/src/air/lagrange/frame.rs
rename to air/src/air/logup_gkr/lagrange/frame.rs
index d0ffc4fa4..6dc0a64cc 100644
--- a/air/src/air/lagrange/frame.rs
+++ b/air/src/air/logup_gkr/lagrange/frame.rs
@@ -4,6 +4,7 @@
// LICENSE file in the root directory of this source tree.
use alloc::vec::Vec;
+use core::ops::{Index, IndexMut};
use math::{polynom, FieldElement, StarkField};
@@ -25,14 +26,15 @@ impl LagrangeKernelEvaluationFrame {
// --------------------------------------------------------------------------------------------
/// Constructs a Lagrange kernel evaluation frame from the raw column polynomial evaluations.
- pub fn new(frame: Vec) -> Self {
+ pub fn with_values(frame: Vec) -> Self {
Self { frame }
}
/// Constructs an empty Lagrange kernel evaluation frame from the raw column polynomial
/// evaluations. The frame can subsequently be filled using [`Self::frame_mut`].
- pub fn new_empty() -> Self {
- Self { frame: Vec::new() }
+ pub fn new(trace_len: usize) -> Self {
+ let frame_length = trace_len.ilog2() as usize + 1;
+ Self { frame: vec![E::ZERO; frame_length] }
}
/// Constructs the frame from the Lagrange kernel column trace polynomial coefficients for an
@@ -61,14 +63,6 @@ impl LagrangeKernelEvaluationFrame {
Self { frame }
}
- // MUTATORS
- // --------------------------------------------------------------------------------------------
-
- /// Returns a mutable reference to the inner frame.
- pub fn frame_mut(&mut self) -> &mut Vec {
- &mut self.frame
- }
-
// ACCESSORS
// --------------------------------------------------------------------------------------------
@@ -84,3 +78,17 @@ impl LagrangeKernelEvaluationFrame {
self.frame.len()
}
}
+
+impl Index for LagrangeKernelEvaluationFrame {
+ type Output = E;
+
+ fn index(&self, index: usize) -> &Self::Output {
+ &self.frame[index]
+ }
+}
+
+impl IndexMut for LagrangeKernelEvaluationFrame {
+ fn index_mut(&mut self, index: usize) -> &mut Self::Output {
+ &mut self.frame[index]
+ }
+}
diff --git a/air/src/air/lagrange/mod.rs b/air/src/air/logup_gkr/lagrange/mod.rs
similarity index 95%
rename from air/src/air/lagrange/mod.rs
rename to air/src/air/logup_gkr/lagrange/mod.rs
index fed5897f3..9d80b4437 100644
--- a/air/src/air/lagrange/mod.rs
+++ b/air/src/air/logup_gkr/lagrange/mod.rs
@@ -3,17 +3,18 @@
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree.
-mod boundary;
use alloc::vec::Vec;
use core::ops::Deref;
+use math::FieldElement;
+
+mod boundary;
pub use boundary::LagrangeKernelBoundaryConstraint;
mod frame;
pub use frame::LagrangeKernelEvaluationFrame;
mod transition;
-use math::FieldElement;
pub use transition::LagrangeKernelTransitionConstraints;
use crate::LagrangeConstraintsCompositionCoefficients;
@@ -22,7 +23,6 @@ use crate::LagrangeConstraintsCompositionCoefficients;
pub struct LagrangeKernelConstraints {
pub transition: LagrangeKernelTransitionConstraints,
pub boundary: LagrangeKernelBoundaryConstraint,
- pub lagrange_kernel_col_idx: usize,
}
impl LagrangeKernelConstraints {
@@ -30,7 +30,6 @@ impl LagrangeKernelConstraints {
pub fn new(
lagrange_composition_coefficients: LagrangeConstraintsCompositionCoefficients,
lagrange_kernel_rand_elements: &LagrangeKernelRandElements,
- lagrange_kernel_col_idx: usize,
) -> Self {
Self {
transition: LagrangeKernelTransitionConstraints::new(
@@ -40,7 +39,6 @@ impl LagrangeKernelConstraints {
lagrange_composition_coefficients.boundary,
lagrange_kernel_rand_elements,
),
- lagrange_kernel_col_idx,
}
}
}
diff --git a/air/src/air/lagrange/transition.rs b/air/src/air/logup_gkr/lagrange/transition.rs
similarity index 91%
rename from air/src/air/lagrange/transition.rs
rename to air/src/air/logup_gkr/lagrange/transition.rs
index 18bdfa9be..5f5b110e6 100644
--- a/air/src/air/lagrange/transition.rs
+++ b/air/src/air/logup_gkr/lagrange/transition.rs
@@ -43,6 +43,11 @@ impl LagrangeKernelTransitionConstraints {
}
}
+ /// Returns the constraint composition coefficients for the Lagrange kernel transition constraints.
+ pub fn lagrange_constraint_coefficients(&self) -> &[E] {
+ &self.lagrange_constraint_coefficients
+ }
+
/// Evaluates the numerator of the `constraint_idx`th transition constraint.
pub fn evaluate_ith_numerator(
&self,
@@ -54,14 +59,12 @@ impl LagrangeKernelTransitionConstraints {
F: FieldElement,
E: ExtensionOf,
{
- let c = lagrange_kernel_column_frame.inner();
- let v = c.len() - 1;
+ let c = lagrange_kernel_column_frame;
+ let v = c.num_rows() - 1;
let r = lagrange_kernel_rand_elements;
let k = constraint_idx + 1;
- let eval = (r[v - k] * c[0]) - ((E::ONE - r[v - k]) * c[v - k + 1]);
-
- self.lagrange_constraint_coefficients[constraint_idx].mul_base(eval)
+ (r[v - k] * c[0]) - ((E::ONE - r[v - k]) * c[v - k + 1])
}
/// Evaluates the divisor of the `constraint_idx`th transition constraint.
@@ -124,8 +127,8 @@ impl LagrangeKernelTransitionConstraints {
let log2_trace_len = lagrange_kernel_column_frame.num_rows() - 1;
let mut transition_evals = vec![E::ZERO; log2_trace_len];
- let c = lagrange_kernel_column_frame.inner();
- let v = c.len() - 1;
+ let c = lagrange_kernel_column_frame;
+ let v = c.num_rows() - 1;
let r = lagrange_kernel_rand_elements;
for k in 1..v + 1 {
diff --git a/air/src/air/logup_gkr/mod.rs b/air/src/air/logup_gkr/mod.rs
new file mode 100644
index 000000000..d3e198912
--- /dev/null
+++ b/air/src/air/logup_gkr/mod.rs
@@ -0,0 +1,305 @@
+// Copyright (c) Facebook, Inc. and its affiliates.
+//
+// This source code is licensed under the MIT license found in the
+// LICENSE file in the root directory of this source tree.
+
+use alloc::vec::Vec;
+use core::marker::PhantomData;
+
+use crypto::{ElementHasher, RandomCoin};
+use math::{ExtensionOf, FieldElement, StarkField, ToElements};
+
+use super::{EvaluationFrame, GkrData, LagrangeConstraintsCompositionCoefficients};
+mod s_column;
+use s_column::SColumnConstraint;
+
+mod lagrange;
+pub use lagrange::{
+ LagrangeKernelBoundaryConstraint, LagrangeKernelConstraints, LagrangeKernelEvaluationFrame,
+ LagrangeKernelRandElements, LagrangeKernelTransitionConstraints,
+};
+
+/// A trait containing the necessary information in order to run the LogUp-GKR protocol of [1].
+///
+/// The trait contains useful information for running the GKR protocol as well as for implementing
+/// the univariate IOP for multi-linear evaluation of Section 5 in [1] for the final evaluation
+/// check resulting from GKR.
+///
+/// [1]: https://eprint.iacr.org/2023/1284
+pub trait LogUpGkrEvaluator: Clone + Sync {
+ /// Defines the base field of the evaluator.
+ type BaseField: StarkField;
+
+ /// Public inputs need to compute the final claim.
+ type PublicInputs: ToElements + Send;
+
+ /// Gets a list of all oracles involved in LogUp-GKR; this is intended to be used in construction of
+ /// MLEs.
+ fn get_oracles(&self) -> &[LogUpGkrOracle];
+
+ /// A vector of virtual periodic columns defined by their values in some given cycle.
+ /// Note that the cycle lengths must be powers of 2.
+ fn get_periodic_column_values(&self) -> Vec> {
+ vec![]
+ }
+
+ /// Returns the number of random values needed to evaluate a query.
+ fn get_num_rand_values(&self) -> usize;
+
+ /// Returns the number of fractions in the LogUp-GKR statement.
+ fn get_num_fractions(&self) -> usize;
+
+ /// Returns the maximal degree of the multi-variate associated to the input layer.
+ ///
+ /// This is equal to the max of $1 + deg_k(\text{numerator}_i) * deg_k(\text{denominator}_j)$ where
+ /// $i$ and $j$ range over the number of numerators and denominators, respectively, and $deg_k$
+ /// is the degree of a multi-variate polynomial in its $k$-th variable.
+ fn max_degree(&self) -> usize;
+
+ /// Builds a query from the provided main trace frame and periodic values.
+ ///
+ /// Note: it should be possible to provide an implementation of this method based on the
+ /// information returned from `get_oracles()`. However, this implementation is likely to be
+ /// expensive compared to the hand-written implementation. However, we could provide a test
+ /// which verifies that `get_oracles()` and `build_query()` methods are consistent.
+ fn build_query(&self, frame: &EvaluationFrame, query: &mut [E])
+ where
+ E: FieldElement;
+
+ /// Evaluates the provided query and writes the results into the numerators and denominators.
+ ///
+ /// Note: it is also possible to combine `build_query()` and `evaluate_query()` into a single
+ /// method to avoid the need to first build the query struct and then evaluate it. However:
+ /// - We assume that the compiler will be able to optimize this away.
+ /// - Merging the methods will make it more difficult avoid inconsistencies between
+ /// `evaluate_query()` and `get_oracles()` methods.
+ fn evaluate_query(
+ &self,
+ query: &[F],
+ periodic_values: &[F],
+ logup_randomness: &[E],
+ numerators: &mut [E],
+ denominators: &mut [E],
+ ) where
+ F: FieldElement,
+ E: FieldElement + ExtensionOf;
+
+ /// Computes the final claim for the LogUp-GKR circuit.
+ ///
+ /// The default implementation of this method returns E::ZERO as it is expected that the
+ /// fractional sums will cancel out. However, in cases when some boundary conditions need to
+ /// be imposed on the LogUp-GKR relations, this method can be overridden to compute the final
+ /// expected claim.
+ fn compute_claim(&self, _inputs: &Self::PublicInputs, _rand_values: &[E]) -> E
+ where
+ E: FieldElement,
+ {
+ E::ZERO
+ }
+
+ /// Generates the data needed for running the univariate IOP for multi-linear evaluation of [1].
+ ///
+ /// This mainly generates the batching randomness used to batch a number of multi-linear
+ /// evaluation claims and includes some additional data that is needed for building/verifying
+ /// the univariate IOP for multi-linear evaluation of [1].
+ ///
+ /// This is the $\lambda$ randomness in section 5.2 in [1] but using different random values for
+ /// each term instead of powers of a single random element.
+ ///
+ /// [1]: https://eprint.iacr.org/2023/1284
+ fn generate_univariate_iop_for_multi_linear_opening_data(
+ &self,
+ openings: Vec,
+ eval_point: Vec,
+ public_coin: &mut impl RandomCoin,
+ ) -> GkrData
+ where
+ E: FieldElement,
+ H: ElementHasher,
+ {
+ public_coin.reseed(H::hash_elements(&openings));
+
+ let mut batching_randomness = Vec::with_capacity(openings.len() - 1);
+ for _ in 0..openings.len() - 1 {
+ batching_randomness.push(public_coin.draw().expect("failed to generate randomness"))
+ }
+
+ GkrData::new(
+ LagrangeKernelRandElements::new(eval_point),
+ batching_randomness,
+ openings,
+ self.get_oracles().to_vec(),
+ )
+ }
+
+ /// Returns a new [`LagrangeKernelConstraints`].
+ fn get_lagrange_kernel_constraints>(
+ &self,
+ lagrange_composition_coefficients: LagrangeConstraintsCompositionCoefficients,
+ lagrange_kernel_rand_elements: &LagrangeKernelRandElements,
+ ) -> LagrangeKernelConstraints {
+ LagrangeKernelConstraints::new(
+ lagrange_composition_coefficients,
+ lagrange_kernel_rand_elements,
+ )
+ }
+
+ /// Returns a new [`SColumnConstraints`].
+ fn get_s_column_constraints>(
+ &self,
+ gkr_data: GkrData,
+ composition_coefficient: E,
+ ) -> SColumnConstraint {
+ SColumnConstraint::new(gkr_data, composition_coefficient)
+ }
+
+ /// Returns the periodic values used in the LogUp-GKR statement, either as base field element
+ /// during circuit evaluation or as extension field element during the run of sum-check for
+ /// the input layer.
+ fn build_periodic_values(&self) -> PeriodicTable
+ where
+ E: FieldElement,
+ {
+ let table = self
+ .get_periodic_column_values()
+ .iter()
+ .map(|values| values.iter().map(|x| E::from(*x)).collect())
+ .collect();
+
+ PeriodicTable { table }
+ }
+}
+
+#[derive(Clone, Default)]
+pub(crate) struct PhantomLogUpGkrEval> {
+ _field: PhantomData,
+ _public_inputs: PhantomData,
+}
+
+impl PhantomLogUpGkrEval
+where
+ B: StarkField,
+ P: Clone + Send + Sync + ToElements,
+{
+ pub fn new() -> Self {
+ Self {
+ _field: PhantomData,
+ _public_inputs: PhantomData,
+ }
+ }
+}
+
+impl LogUpGkrEvaluator for PhantomLogUpGkrEval
+where
+ B: StarkField,
+ P: Clone + Send + Sync + ToElements,
+{
+ type BaseField = B;
+
+ type PublicInputs = P;
+
+ fn get_oracles(&self) -> &[LogUpGkrOracle] {
+ panic!("LogUpGkrEvaluator method called but LogUp-GKR is not implemented")
+ }
+
+ fn get_num_rand_values(&self) -> usize {
+ panic!("LogUpGkrEvaluator method called but LogUp-GKR is not implemented")
+ }
+
+ fn get_num_fractions(&self) -> usize {
+ panic!("LogUpGkrEvaluator method called but LogUp-GKR is not implemented")
+ }
+
+ fn max_degree(&self) -> usize {
+ panic!("LogUpGkrEvaluator method called but LogUp-GKR is not implemented")
+ }
+
+ fn build_query(&self, _frame: &EvaluationFrame, _query: &mut [E])
+ where
+ E: FieldElement,
+ {
+ panic!("LogUpGkrEvaluator method called but LogUp-GKR is not implemented")
+ }
+
+ fn evaluate_query(
+ &self,
+ _query: &[F],
+ _periodic_values: &[F],
+ _rand_values: &[E],
+ _numerator: &mut [E],
+ _denominator: &mut [E],
+ ) where
+ F: FieldElement,
+ E: FieldElement + ExtensionOf,
+ {
+ panic!("LogUpGkrEvaluator method called but LogUp-GKR is not implemented")
+ }
+
+ fn compute_claim(&self, _inputs: &Self::PublicInputs, _rand_values: &[E]) -> E
+ where
+ E: FieldElement,
+ {
+ panic!("LogUpGkrEvaluator method called but LogUp-GKR is not implemented")
+ }
+}
+
+#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord)]
+pub enum LogUpGkrOracle {
+ /// A column with a given index in the main trace segment.
+ CurrentRow(usize),
+ /// A column with a given index in the main trace segment but shifted upwards.
+ NextRow(usize),
+}
+
+// PERIODIC COLUMNS FOR LOGUP
+// =================================================================================================
+
+/// Stores the periodic columns used in a LogUp-GKR statement.
+///
+/// Each stored periodic column is interpreted as a multi-linear extension polynomial of the column
+/// with the given periodic values. Due to the periodic nature of the values, storing, binding of
+/// an argument and evaluating the said multi-linear extension can be all done linearly in the size
+/// of the smallest cycle defining the periodic values. Hence we only store the values of this
+/// smallest cycle. The cycle is assumed throughout to be a power of 2.
+#[derive(Clone, Debug, Default, PartialEq, PartialOrd, Eq, Ord)]
+pub struct PeriodicTable {
+ pub table: Vec>,
+}
+
+impl PeriodicTable
+where
+ E: FieldElement,
+{
+ pub fn new(table: Vec>) -> Self {
+ let table = table.iter().map(|col| col.iter().map(|x| E::from(*x)).collect()).collect();
+
+ Self { table }
+ }
+
+ pub fn num_columns(&self) -> usize {
+ self.table.len()
+ }
+
+ pub fn table(&self) -> &[Vec] {
+ &self.table
+ }
+
+ pub fn fill_periodic_values_at(&self, row: usize, values: &mut [E]) {
+ self.table
+ .iter()
+ .zip(values.iter_mut())
+ .for_each(|(col, value)| *value = col[row % col.len()])
+ }
+
+ pub fn bind_least_significant_variable(&mut self, round_challenge: E) {
+ for col in self.table.iter_mut() {
+ if col.len() > 1 {
+ let num_evals = col.len() >> 1;
+ for i in 0..num_evals {
+ col[i] = col[i << 1] + round_challenge * (col[(i << 1) + 1] - col[i << 1]);
+ }
+ col.truncate(num_evals)
+ }
+ }
+ }
+}
diff --git a/air/src/air/logup_gkr/s_column.rs b/air/src/air/logup_gkr/s_column.rs
new file mode 100644
index 000000000..685c6e026
--- /dev/null
+++ b/air/src/air/logup_gkr/s_column.rs
@@ -0,0 +1,56 @@
+// Copyright (c) Facebook, Inc. and its affiliates.
+//
+// This source code is licensed under the MIT license found in the
+// LICENSE file in the root directory of this source tree.
+
+use math::FieldElement;
+
+use super::{super::Air, EvaluationFrame, GkrData};
+use crate::LogUpGkrEvaluator;
+
+/// Represents the transition constraint for the s-column, as well as the random coefficient used
+/// to linearly combine the constraint into the constraint composition polynomial.
+///
+/// The s-column implements the cohomological sum-check argument of [1] and the constraint in
+/// [`SColumnConstraint`] is exactly Eq (4) in Lemma 1 in [1].
+///
+///
+/// [1]: https://eprint.iacr.org/2021/930
+pub struct SColumnConstraint {
+ gkr_data: GkrData,
+ composition_coefficient: E,
+}
+
+impl SColumnConstraint {
+ pub fn new(gkr_data: GkrData, composition_coefficient: E) -> Self {
+ Self { gkr_data, composition_coefficient }
+ }
+
+ /// Evaluates the transition constraint over the specificed main trace segment, s-column,
+ /// and Lagrange kernel evaluation frames.
+ pub fn evaluate(
+ &self,
+ air: &A,
+ main_trace_frame: &EvaluationFrame,
+ s_cur: E,
+ s_nxt: E,
+ l_cur: E,
+ x: E,
+ ) -> E
+ where
+ A: Air,
+ {
+ let batched_claim = self.gkr_data.compute_batched_claim();
+ let mean = batched_claim
+ .mul_base(E::BaseField::ONE / E::BaseField::from(air.trace_length() as u32));
+
+ let mut query = vec![E::ZERO; air.get_logup_gkr_evaluator().get_oracles().len()];
+ air.get_logup_gkr_evaluator().build_query(main_trace_frame, &mut query);
+ let batched_claim_at_query = self.gkr_data.compute_batched_query::(&query);
+ let rhs = s_cur - mean + batched_claim_at_query * l_cur;
+ let lhs = s_nxt;
+
+ let divisor = x.exp((air.trace_length() as u32).into()) - E::ONE;
+ self.composition_coefficient * (rhs - lhs) / divisor
+ }
+}
diff --git a/air/src/air/mod.rs b/air/src/air/mod.rs
index 53a59fa5a..cc2e82d2b 100644
--- a/air/src/air/mod.rs
+++ b/air/src/air/mod.rs
@@ -11,7 +11,7 @@ use math::{fft, ExtensibleField, ExtensionOf, FieldElement, StarkField, ToElemen
use crate::ProofOptions;
mod aux;
-pub use aux::{AuxRandElements, GkrRandElements, GkrVerifier};
+pub use aux::{AuxRandElements, GkrData};
mod trace_info;
pub use trace_info::TraceInfo;
@@ -28,10 +28,12 @@ pub use boundary::{BoundaryConstraint, BoundaryConstraintGroup, BoundaryConstrai
mod transition;
pub use transition::{EvaluationFrame, TransitionConstraintDegree, TransitionConstraints};
-mod lagrange;
-pub use lagrange::{
+mod logup_gkr;
+use logup_gkr::PhantomLogUpGkrEval;
+pub use logup_gkr::{
LagrangeKernelBoundaryConstraint, LagrangeKernelConstraints, LagrangeKernelEvaluationFrame,
- LagrangeKernelRandElements, LagrangeKernelTransitionConstraints,
+ LagrangeKernelRandElements, LagrangeKernelTransitionConstraints, LogUpGkrEvaluator,
+ LogUpGkrOracle, PeriodicTable,
};
mod coefficients;
@@ -42,7 +44,6 @@ pub use coefficients::{
mod divisor;
pub use divisor::ConstraintDivisor;
-use utils::{Deserializable, Serializable};
#[cfg(test)]
mod tests;
@@ -192,13 +193,7 @@ pub trait Air: Send + Sync {
/// A type defining shape of public inputs for the computation described by this protocol.
/// This could be any type as long as it can be serialized into a sequence of field elements.
- type PublicInputs: ToElements + Send;
-
- /// An GKR proof object. If not needed, set to `()`.
- type GkrProof: Serializable + Deserializable + Send;
-
- /// A verifier for verifying GKR proofs. If not needed, set to `()`.
- type GkrVerifier: GkrVerifier;
+ type PublicInputs: ToElements + Clone + Send + Sync;
// REQUIRED METHODS
// --------------------------------------------------------------------------------------------
@@ -214,7 +209,7 @@ pub trait Air: Send + Sync {
fn new(trace_info: TraceInfo, pub_inputs: Self::PublicInputs, options: ProofOptions) -> Self;
/// Returns context for this instance of the computation.
- fn context(&self) -> &AirContext;
+ fn context(&self) -> &AirContext;
/// Evaluates transition constraints over the specified evaluation frame.
///
@@ -303,16 +298,15 @@ pub trait Air: Send + Sync {
Vec::new()
}
- // AUXILIARY PROOF VERIFIER
+ // LOGUP-GKR EVALUATOR
// --------------------------------------------------------------------------------------------
- /// Returns the [`GkrVerifier`] to be used to verify the GKR proof.
- ///
- /// Leave unimplemented if the `Air` doesn't use a GKR proof.
- fn get_gkr_proof_verifier>(
+ /// Returns the object needed for the LogUp-GKR argument.
+ fn get_logup_gkr_evaluator(
&self,
- ) -> Self::GkrVerifier {
- unimplemented!("`get_auxiliary_proof_verifier()` must be implemented when the proof contains a GKR proof");
+ ) -> impl LogUpGkrEvaluator
+ {
+ PhantomLogUpGkrEval::new()
}
// PROVIDED METHODS
@@ -335,22 +329,6 @@ pub trait Air: Send + Sync {
Ok(rand_elements)
}
- /// Returns a new [`LagrangeKernelConstraints`] if a Lagrange kernel auxiliary column is present
- /// in the trace, or `None` otherwise.
- fn get_lagrange_kernel_constraints>(
- &self,
- lagrange_composition_coefficients: LagrangeConstraintsCompositionCoefficients,
- lagrange_kernel_rand_elements: &LagrangeKernelRandElements,
- ) -> Option> {
- self.context().lagrange_kernel_aux_column_idx().map(|col_idx| {
- LagrangeKernelConstraints::new(
- lagrange_composition_coefficients,
- lagrange_kernel_rand_elements,
- col_idx,
- )
- })
- }
-
/// Returns values for all periodic columns used in the computation.
///
/// These values will be used to compute column values at specific states of the computation
@@ -545,7 +523,7 @@ pub trait Air: Send + Sync {
b_coefficients.push(public_coin.draw()?);
}
- let lagrange = if self.context().has_lagrange_kernel_aux_column() {
+ let lagrange = if self.context().logup_gkr_enabled() {
let mut lagrange_kernel_t_coefficients = Vec::new();
for _ in 0..self.context().trace_len().ilog2() {
lagrange_kernel_t_coefficients.push(public_coin.draw()?);
@@ -561,10 +539,17 @@ pub trait Air: Send + Sync {
None
};
+ let s_col = if self.context().logup_gkr_enabled() {
+ Some(public_coin.draw()?)
+ } else {
+ None
+ };
+
Ok(ConstraintCompositionCoefficients {
transition: t_coefficients,
boundary: b_coefficients,
lagrange,
+ s_col,
})
}
@@ -588,7 +573,13 @@ pub trait Air: Send + Sync {
c_coefficients.push(public_coin.draw()?);
}
- let lagrange_cc = if self.context().has_lagrange_kernel_aux_column() {
+ let lagrange_cc = if self.context().logup_gkr_enabled() {
+ Some(public_coin.draw()?)
+ } else {
+ None
+ };
+
+ let s_col_cc = if self.context().logup_gkr_enabled() {
Some(public_coin.draw()?)
} else {
None
@@ -598,6 +589,7 @@ pub trait Air: Send + Sync {
trace: t_coefficients,
constraints: c_coefficients,
lagrange: lagrange_cc,
+ s_col: s_col_cc,
})
}
}
diff --git a/air/src/air/tests.rs b/air/src/air/tests.rs
index e0063ed3b..2400cb883 100644
--- a/air/src/air/tests.rs
+++ b/air/src/air/tests.rs
@@ -9,8 +9,8 @@ use crypto::{hashers::Blake3_256, DefaultRandomCoin, RandomCoin};
use math::{fields::f64::BaseElement, get_power_series, polynom, FieldElement, StarkField};
use super::{
- Air, AirContext, Assertion, EvaluationFrame, ProofOptions, TraceInfo,
- TransitionConstraintDegree,
+ logup_gkr::PhantomLogUpGkrEval, Air, AirContext, Assertion, EvaluationFrame, ProofOptions,
+ TraceInfo, TransitionConstraintDegree,
};
use crate::FieldExtension;
@@ -192,7 +192,7 @@ fn get_boundary_constraints() {
// ================================================================================================
struct MockAir {
- context: AirContext,
+ context: AirContext,
assertions: Vec>,
periodic_columns: Vec>,
}
@@ -225,8 +225,6 @@ impl MockAir {
impl Air for MockAir {
type BaseField = BaseElement;
type PublicInputs = ();
- type GkrProof = ();
- type GkrVerifier = ();
fn new(trace_info: TraceInfo, _pub_inputs: (), _options: ProofOptions) -> Self {
let num_assertions = trace_info.meta()[0] as usize;
@@ -238,7 +236,7 @@ impl Air for MockAir {
}
}
- fn context(&self) -> &AirContext {
+ fn context(&self) -> &AirContext {
&self.context
}
@@ -257,6 +255,13 @@ impl Air for MockAir {
_result: &mut [E],
) {
}
+
+ fn get_logup_gkr_evaluator(
+ &self,
+ ) -> impl super::LogUpGkrEvaluator
+ {
+ PhantomLogUpGkrEval::default()
+ }
}
// UTILITY FUNCTIONS
@@ -266,11 +271,11 @@ pub fn build_context(
trace_length: usize,
trace_width: usize,
num_assertions: usize,
-) -> AirContext {
+) -> AirContext {
let options = ProofOptions::new(32, 8, 0, FieldExtension::None, 4, 31);
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)
+ AirContext::new(trace_info, (), t_degrees, num_assertions, options)
}
pub fn build_prng() -> DefaultRandomCoin> {
diff --git a/air/src/air/trace_info.rs b/air/src/air/trace_info.rs
index 99ff4aa6d..29cf4726b 100644
--- a/air/src/air/trace_info.rs
+++ b/air/src/air/trace_info.rs
@@ -27,6 +27,7 @@ pub struct TraceInfo {
num_aux_segment_rands: usize,
trace_length: usize,
trace_meta: Vec,
+ logup_gkr: bool,
}
impl TraceInfo {
@@ -38,6 +39,10 @@ impl TraceInfo {
pub const MAX_META_LENGTH: usize = 65535;
/// Maximum number of random elements in the auxiliary trace segment; currently set to 255.
pub const MAX_RAND_SEGMENT_ELEMENTS: usize = 255;
+ /// The Lagrange kernel, if present, is the last column of the auxiliary trace.
+ pub const LAGRANGE_KERNEL_OFFSET: usize = 1;
+ /// The s-column, if present, is the second to last column of the auxiliary trace.
+ pub const S_COLUMN_OFFSET: usize = 2;
// CONSTRUCTORS
// --------------------------------------------------------------------------------------------
@@ -65,7 +70,7 @@ impl TraceInfo {
/// * Length of `meta` is greater than 65535;
pub fn with_meta(width: usize, length: usize, meta: Vec) -> Self {
assert!(width > 0, "trace width must be greater than 0");
- Self::new_multi_segment(width, 0, 0, length, meta)
+ Self::new_multi_segment(width, 0, 0, length, meta, false)
}
/// Creates a new [TraceInfo] with main and auxiliary segments.
@@ -90,6 +95,7 @@ impl TraceInfo {
num_aux_segment_rands: usize,
trace_length: usize,
trace_meta: Vec,
+ logup_gkr: bool,
) -> Self {
assert!(
trace_length >= Self::MIN_TRACE_LENGTH,
@@ -110,7 +116,7 @@ impl TraceInfo {
// validate trace segment widths
assert!(main_segment_width > 0, "main trace segment must consist of at least one column");
- let full_width = main_segment_width + aux_segment_width;
+ let full_width = main_segment_width + aux_segment_width + 2 * logup_gkr as usize;
assert!(
full_width <= TraceInfo::MAX_TRACE_WIDTH,
"total number of columns in the trace cannot be greater than {}, but was {}",
@@ -138,6 +144,7 @@ impl TraceInfo {
num_aux_segment_rands,
trace_length,
trace_meta,
+ logup_gkr,
}
}
@@ -146,9 +153,13 @@ impl TraceInfo {
/// Returns the total number of columns in an execution trace.
///
+ /// When LogUp-GKR is enabled, we also account for two extra columns, in the auxiliary segment,
+ /// which are needed for implementing the univariate IOP for multi-linear evaluation in
+ /// https://eprint.iacr.org/2023/1284.
+ ///
/// This is guaranteed to be between 1 and 255.
pub fn width(&self) -> usize {
- self.main_segment_width + self.aux_segment_width
+ self.main_segment_width + self.aux_segment_width + 2 * self.logup_gkr as usize
}
/// Returns execution trace length.
@@ -163,21 +174,27 @@ impl TraceInfo {
&self.trace_meta
}
- /// Returns true if an execution trace contains the auxiliary trace segment.
+ /// Returns true if an execution trace contains an auxiliary trace segment.
+ ///
+ /// This includes either the case when the auxiliary trace segment is user defined or the case
+ /// when the segment is created as part of LogUp-GKR.
pub fn is_multi_segment(&self) -> bool {
- self.aux_segment_width > 0
+ self.aux_segment_width > 0 || self.logup_gkr
}
/// Returns the number of columns in the main segment of an execution trace.
///
/// This is guaranteed to be between 1 and 255.
- pub fn main_trace_width(&self) -> usize {
+ pub fn main_segment_width(&self) -> usize {
self.main_segment_width
}
/// Returns the number of columns in the auxiliary segment of an execution trace.
+ ///
+ /// This includes both the columns that are user defined as well as the two columns defined
+ /// as part of LogUp-GKR when the latter is enabled.
pub fn aux_segment_width(&self) -> usize {
- self.aux_segment_width
+ self.aux_segment_width + 2 * self.logup_gkr as usize
}
/// Returns the total number of segments in an execution trace.
@@ -198,9 +215,27 @@ impl TraceInfo {
}
}
- /// Returns the number of columns in the auxiliary trace segment.
- pub fn get_aux_segment_width(&self) -> usize {
- self.aux_segment_width
+ /// Returns a boolean indicating whether LogUp-GKR is enabled.
+ pub fn logup_gkr_enabled(&self) -> bool {
+ self.logup_gkr
+ }
+
+ /// Returns the index of the auxiliary column which implements the Lagrange kernel, if any.
+ pub fn lagrange_kernel_column_idx(&self) -> Option {
+ if self.logup_gkr_enabled() {
+ Some(self.aux_segment_width() - TraceInfo::LAGRANGE_KERNEL_OFFSET)
+ } else {
+ None
+ }
+ }
+
+ /// Returns the index of the auxiliary column which implements the s-column, if any.
+ pub fn s_column_idx(&self) -> Option {
+ if self.logup_gkr_enabled() {
+ Some(self.aux_segment_width() - TraceInfo::S_COLUMN_OFFSET)
+ } else {
+ None
+ }
}
/// Returns the number of random elements needed to build all auxiliary columns, except for the
@@ -264,6 +299,9 @@ impl Serializable for TraceInfo {
// store trace meta
target.write_u16(self.trace_meta.len() as u16);
target.write_bytes(&self.trace_meta);
+
+ // write bool indicating if LogUp-GKR is used
+ target.write_bool(self.logup_gkr);
}
}
@@ -326,12 +364,16 @@ impl Deserializable for TraceInfo {
vec![]
};
+ // read `logup_gkr`
+ let logup_gkr = source.read_bool()?;
+
Ok(Self::new_multi_segment(
main_segment_width,
aux_segment_width,
num_aux_segment_rands,
trace_length,
trace_meta,
+ logup_gkr,
))
}
}
@@ -387,6 +429,7 @@ mod tests {
aux_rands,
trace_length as usize,
trace_meta,
+ false,
);
assert_eq!(expected, info.to_elements());
diff --git a/air/src/air/transition/mod.rs b/air/src/air/transition/mod.rs
index 60e641817..d29cbbb8b 100644
--- a/air/src/air/transition/mod.rs
+++ b/air/src/air/transition/mod.rs
@@ -46,7 +46,7 @@ impl TransitionConstraints {
/// # Panics
/// Panics if the number of transition constraints in the context does not match the number of
/// provided composition coefficients.
- pub fn new(context: &AirContext, composition_coefficients: &[E]) -> Self {
+ pub fn new(context: &AirContext, composition_coefficients: &[E]) -> Self {
assert_eq!(
context.num_transition_constraints(),
composition_coefficients.len(),
diff --git a/air/src/lib.rs b/air/src/lib.rs
index 539a812d9..39ef44d18 100644
--- a/air/src/lib.rs
+++ b/air/src/lib.rs
@@ -44,9 +44,9 @@ mod air;
pub use air::{
Air, AirContext, Assertion, AuxRandElements, BoundaryConstraint, BoundaryConstraintGroup,
BoundaryConstraints, ConstraintCompositionCoefficients, ConstraintDivisor,
- DeepCompositionCoefficients, EvaluationFrame, GkrRandElements, GkrVerifier,
+ DeepCompositionCoefficients, EvaluationFrame, GkrData,
LagrangeConstraintsCompositionCoefficients, LagrangeKernelBoundaryConstraint,
LagrangeKernelConstraints, LagrangeKernelEvaluationFrame, LagrangeKernelRandElements,
- LagrangeKernelTransitionConstraints, TraceInfo, TransitionConstraintDegree,
- TransitionConstraints,
+ LagrangeKernelTransitionConstraints, LogUpGkrEvaluator, LogUpGkrOracle, PeriodicTable,
+ TraceInfo, TransitionConstraintDegree, TransitionConstraints,
};
diff --git a/air/src/proof/context.rs b/air/src/proof/context.rs
index 83c2beece..73152709a 100644
--- a/air/src/proof/context.rs
+++ b/air/src/proof/context.rs
@@ -190,6 +190,7 @@ mod tests {
aux_rands,
trace_length,
vec![],
+ false,
);
let mut expected = trace_info.to_elements();
@@ -213,8 +214,14 @@ mod tests {
fri_folding_factor as usize,
fri_remainder_max_degree as usize,
);
- let trace_info =
- TraceInfo::new_multi_segment(main_width, aux_width, aux_rands, trace_length, vec![]);
+ let trace_info = TraceInfo::new_multi_segment(
+ main_width,
+ aux_width,
+ aux_rands,
+ trace_length,
+ vec![],
+ false,
+ );
let context = Context::new::(trace_info, options);
assert_eq!(expected, context.to_elements());
}
diff --git a/air/src/proof/ood_frame.rs b/air/src/proof/ood_frame.rs
index d4b3f14ec..9ae017094 100644
--- a/air/src/proof/ood_frame.rs
+++ b/air/src/proof/ood_frame.rs
@@ -131,7 +131,7 @@ impl OodFrame {
let lagrange_kernel_frame = if lagrange_kernel_frame_size > 0 {
let lagrange_kernel_trace = reader.read_many(lagrange_kernel_frame_size)?;
- Some(LagrangeKernelEvaluationFrame::new(lagrange_kernel_trace))
+ Some(LagrangeKernelEvaluationFrame::with_values(lagrange_kernel_trace))
} else {
None
};
@@ -229,6 +229,8 @@ impl Deserializable for OodFrame {
// OOD FRAME TRACE STATES
// ================================================================================================
+/// Stores trace evaluations at an OOD point.
+///
/// Stores the trace evaluations at `z` and `gz`, where `z` is a random Field element in
/// `current_row` and `next_row`, respectively. If the Air contains a Lagrange kernel auxiliary
/// column, then that column interpolated polynomial will be evaluated at `z`, `gz`, `g^2 z`, ...
diff --git a/crypto/src/hash/mds/mds_f64_12x12.rs b/crypto/src/hash/mds/mds_f64_12x12.rs
index 44f5660b9..ddf79f4a2 100644
--- a/crypto/src/hash/mds/mds_f64_12x12.rs
+++ b/crypto/src/hash/mds/mds_f64_12x12.rs
@@ -12,19 +12,19 @@ use math::{
FieldElement,
};
-/// This module contains helper functions as well as constants used to perform a 12x12 vector-matrix
-/// multiplication. The special form of our MDS matrix i.e. being circulant, allows us to reduce
-/// the vector-matrix multiplication to a Hadamard product of two vectors in "frequency domain".
-/// This follows from the simple fact that every circulant matrix has the columns of the discrete
-/// Fourier transform matrix as orthogonal eigenvectors.
-/// The implementation also avoids the use of 3-point FFTs, and 3-point iFFTs, and substitutes that
-/// with explicit expressions. It also avoids, due to the form of our matrix in the frequency domain,
-/// divisions by 2 and repeated modular reductions. This is because of our explicit choice of
-/// an MDS matrix that has small powers of 2 entries in frequency domain.
-/// The following implementation has benefited greatly from the discussions and insights of
-/// Hamish Ivey-Law and Jacqueline Nabaglo of Polygon Zero and is based on Nabaglo's implementation
-/// in [Plonky2](https://github.com/mir-protocol/plonky2).
-/// The circulant matrix is identified by its first row: [7, 23, 8, 26, 13, 10, 9, 7, 6, 22, 21, 8].
+// This module contains helper functions as well as constants used to perform a 12x12 vector-matrix
+// multiplication. The special form of our MDS matrix i.e. being circulant, allows us to reduce
+// the vector-matrix multiplication to a Hadamard product of two vectors in "frequency domain".
+// This follows from the simple fact that every circulant matrix has the columns of the discrete
+// Fourier transform matrix as orthogonal eigenvectors.
+// The implementation also avoids the use of 3-point FFTs, and 3-point iFFTs, and substitutes that
+// with explicit expressions. It also avoids, due to the form of our matrix in the frequency domain,
+// divisions by 2 and repeated modular reductions. This is because of our explicit choice of
+// an MDS matrix that has small powers of 2 entries in frequency domain.
+// The following implementation has benefited greatly from the discussions and insights of
+// Hamish Ivey-Law and Jacqueline Nabaglo of Polygon Zero and is based on Nabaglo's implementation
+// in [Plonky2](https://github.com/mir-protocol/plonky2).
+// The circulant matrix is identified by its first row: [7, 23, 8, 26, 13, 10, 9, 7, 6, 22, 21, 8].
// MDS matrix in frequency domain.
// More precisely, this is the output of the three 4-point (real) FFTs of the first column of
@@ -33,6 +33,7 @@ use math::{
// The entries have been scaled appropriately in order to avoid divisions by 2 in iFFT2 and iFFT4.
// The code to generate the matrix in frequency domain is based on an adaptation of a code, to generate
// MDS matrices efficiently in original domain, that was developed by the Polygon Zero team.
+
const MDS_FREQ_BLOCK_ONE: [i64; 3] = [16, 8, 16];
const MDS_FREQ_BLOCK_TWO: [(i64, i64); 3] = [(-1, 2), (-1, 1), (4, 8)];
const MDS_FREQ_BLOCK_THREE: [i64; 3] = [-8, 1, 1];
diff --git a/crypto/src/hash/mds/mds_f64_8x8.rs b/crypto/src/hash/mds/mds_f64_8x8.rs
index 037dee721..4e7818357 100644
--- a/crypto/src/hash/mds/mds_f64_8x8.rs
+++ b/crypto/src/hash/mds/mds_f64_8x8.rs
@@ -12,25 +12,26 @@ use math::{
FieldElement,
};
-/// This module contains helper functions as well as constants used to perform a 8x8 vector-matrix
-/// multiplication. The special form of our MDS matrix i.e. being circulant, allows us to reduce
-/// the vector-matrix multiplication to a Hadamard product of two vectors in "frequency domain".
-/// This follows from the simple fact that every circulant matrix has the columns of the discrete
-/// Fourier transform matrix as orthogonal eigenvectors.
-/// The implementation also avoids the use of internal 2-point FFTs, and 2-point iFFTs, and substitutes
-/// them with explicit expressions. It also avoids, due to the form of our matrix in the frequency domain,
-/// divisions by 2 and repeated modular reductions. This is because of our explicit choice of
-/// an MDS matrix that has small powers of 2 entries in frequency domain.
-/// The following implementation has benefited greatly from the discussions and insights of
-/// Hamish Ivey-Law and Jacqueline Nabaglo of Polygon Zero is based on Nabaglo's implementation
-/// in [Plonky2](https://github.com/mir-protocol/plonky2).
-/// The circulant matrix is identified by its first row: [23, 8, 13, 10, 7, 6, 21, 8].
+// This module contains helper functions as well as constants used to perform a 8x8 vector-matrix
+// multiplication. The special form of our MDS matrix i.e. being circulant, allows us to reduce
+// the vector-matrix multiplication to a Hadamard product of two vectors in "frequency domain".
+// This follows from the simple fact that every circulant matrix has the columns of the discrete
+// Fourier transform matrix as orthogonal eigenvectors.
+// The implementation also avoids the use of internal 2-point FFTs, and 2-point iFFTs, and substitutes
+// them with explicit expressions. It also avoids, due to the form of our matrix in the frequency domain,
+// divisions by 2 and repeated modular reductions. This is because of our explicit choice of
+// an MDS matrix that has small powers of 2 entries in frequency domain.
+// The following implementation has benefited greatly from the discussions and insights of
+// Hamish Ivey-Law and Jacqueline Nabaglo of Polygon Zero is based on Nabaglo's implementation
+// in [Plonky2](https://github.com/mir-protocol/plonky2).
+// The circulant matrix is identified by its first row: [23, 8, 13, 10, 7, 6, 21, 8].
// MDS matrix in frequency domain.
// More precisely, this is the output of the two 4-point (real) FFTs of the first column of
// the MDS matrix i.e. just before the multiplication with the appropriate twiddle factors
// and application of the final four 2-point FFT in order to get the full 8-point FFT.
// The entries have been scaled appropriately in order to avoid divisions by 2 in iFFT2 and iFFT4.
+
const MDS_FREQ_BLOCK_ONE: [i64; 2] = [16, 8];
const MDS_FREQ_BLOCK_TWO: [(i64, i64); 2] = [(8, -4), (-1, 1)];
const MDS_FREQ_BLOCK_THREE: [i64; 2] = [-1, 1];
diff --git a/crypto/src/merkle/concurrent.rs b/crypto/src/merkle/concurrent.rs
index 637bd51b5..7a3ba077f 100644
--- a/crypto/src/merkle/concurrent.rs
+++ b/crypto/src/merkle/concurrent.rs
@@ -18,9 +18,10 @@ pub const MIN_CONCURRENT_LEAVES: usize = 1024;
// PUBLIC FUNCTIONS
// ================================================================================================
-/// Builds all internal nodes of the Merkle using all available threads and stores the
-/// results in a single vector such that root of the tree is at position 1, nodes immediately
-/// under the root is at positions 2 and 3 etc.
+/// Builds all internal nodes of the Merkle tree.
+///
+/// This uses all available threads and stores the results in a single vector such that root of
+/// the tree is at position 1, nodes immediately under the root is at positions 2 and 3 etc.
pub fn build_merkle_nodes(leaves: &[H::Digest]) -> Vec {
let n = leaves.len() / 2;
diff --git a/examples/src/fibonacci/fib2/air.rs b/examples/src/fibonacci/fib2/air.rs
index 9e5d75a48..4019ddcae 100644
--- a/examples/src/fibonacci/fib2/air.rs
+++ b/examples/src/fibonacci/fib2/air.rs
@@ -14,15 +14,13 @@ use crate::utils::are_equal;
// ================================================================================================
pub struct FibAir {
- context: AirContext,
+ context: AirContext,
result: BaseElement,
}
impl Air for FibAir {
type BaseField = BaseElement;
type PublicInputs = BaseElement;
- type GkrProof = ();
- type GkrVerifier = ();
// CONSTRUCTOR
// --------------------------------------------------------------------------------------------
@@ -30,12 +28,12 @@ impl Air for FibAir {
let degrees = vec![TransitionConstraintDegree::new(1), TransitionConstraintDegree::new(1)];
assert_eq!(TRACE_WIDTH, trace_info.width());
FibAir {
- context: AirContext::new(trace_info, degrees, 3, options),
+ context: AirContext::new(trace_info, pub_inputs, degrees, 3, options),
result: pub_inputs,
}
}
- fn context(&self) -> &AirContext {
+ fn context(&self) -> &AirContext {
&self.context
}
diff --git a/examples/src/fibonacci/fib8/air.rs b/examples/src/fibonacci/fib8/air.rs
index 4d7aef9ba..17edc7970 100644
--- a/examples/src/fibonacci/fib8/air.rs
+++ b/examples/src/fibonacci/fib8/air.rs
@@ -15,15 +15,13 @@ use crate::utils::are_equal;
// ================================================================================================
pub struct Fib8Air {
- context: AirContext,
+ context: AirContext,
result: BaseElement,
}
impl Air for Fib8Air {
type BaseField = BaseElement;
type PublicInputs = BaseElement;
- type GkrProof = ();
- type GkrVerifier = ();
// CONSTRUCTOR
// --------------------------------------------------------------------------------------------
@@ -31,12 +29,12 @@ impl Air for Fib8Air {
let degrees = vec![TransitionConstraintDegree::new(1), TransitionConstraintDegree::new(1)];
assert_eq!(TRACE_WIDTH, trace_info.width());
Fib8Air {
- context: AirContext::new(trace_info, degrees, 3, options),
+ context: AirContext::new(trace_info, pub_inputs, degrees, 3, options),
result: pub_inputs,
}
}
- fn context(&self) -> &AirContext {
+ fn context(&self) -> &AirContext {
&self.context
}
diff --git a/examples/src/fibonacci/fib_small/air.rs b/examples/src/fibonacci/fib_small/air.rs
index 66580c872..b48eb734b 100644
--- a/examples/src/fibonacci/fib_small/air.rs
+++ b/examples/src/fibonacci/fib_small/air.rs
@@ -14,15 +14,13 @@ use crate::utils::are_equal;
// ================================================================================================
pub struct FibSmall {
- context: AirContext,
+ context: AirContext,
result: BaseElement,
}
impl Air for FibSmall {
type BaseField = BaseElement;
type PublicInputs = BaseElement;
- type GkrProof = ();
- type GkrVerifier = ();
// CONSTRUCTOR
// --------------------------------------------------------------------------------------------
@@ -30,12 +28,12 @@ impl Air for FibSmall {
let degrees = vec![TransitionConstraintDegree::new(1), TransitionConstraintDegree::new(1)];
assert_eq!(TRACE_WIDTH, trace_info.width());
FibSmall {
- context: AirContext::new(trace_info, degrees, 3, options),
+ context: AirContext::new(trace_info, pub_inputs, degrees, 3, options),
result: pub_inputs,
}
}
- fn context(&self) -> &AirContext