diff --git a/crates/prover/src/examples/xor/gkr_lookups/mle_eval.rs b/crates/prover/src/examples/xor/gkr_lookups/mle_eval.rs index a5ebb8bdb..0105e6313 100644 --- a/crates/prover/src/examples/xor/gkr_lookups/mle_eval.rs +++ b/crates/prover/src/examples/xor/gkr_lookups/mle_eval.rs @@ -39,7 +39,7 @@ use crate::core::poly::BitReversedOrder; use crate::core::utils::{self, bit_reverse_index, coset_index_to_circle_domain_index}; use crate::core::ColumnVec; -/// Component that carries out a univariate IOP for multilinear eval at point. +/// Prover component that carries out a univariate IOP for multilinear eval at point. /// /// See (Section 5.1). #[allow(dead_code)] @@ -105,7 +105,7 @@ impl<'twiddles, 'oracle, O: MleCoeffColumnOracle> MleEvalProverComponent<'twiddl } } - /// Size of this components trace columns. + /// Size of this component's trace columns. pub fn log_size(&self) -> u32 { self.mle_eval_point.n_variables() as u32 } @@ -253,6 +253,117 @@ impl<'twiddles, 'oracle, O: MleCoeffColumnOracle> ComponentProver } } +/// Verifier component that carries out a univariate IOP for multilinear eval at point. +/// +/// See (Section 5.1). +pub struct MleEvalVerifierComponent<'oracle, O: MleCoeffColumnOracle> { + /// Oracle for the polynomial encoding the multilinear Lagrange basis coefficients of the MLE. + mle_coeff_column_oracle: &'oracle O, + /// Multilinear evaluation point. + mle_eval_point: MleEvalPoint, + /// Equals `mle_claim / 2^mle_n_variables`. + mle_claim_shift: SecureField, + /// Commitment tree index for the trace. + evals_interaction: usize, + /// Commitment tree index for the constants trace. + const_interaction: usize, + /// Location in the trace for the this component. + trace_locations: Vec, +} + +impl<'oracle, O: MleCoeffColumnOracle> MleEvalVerifierComponent<'oracle, O> { + pub fn new( + provider: &mut TreeColumnSpanProvider, + mle_coeff_column_oracle: &'oracle O, + eval_point: &[SecureField], + claim: SecureField, + evals_interaction: usize, + const_interaction: usize, + ) -> Self { + let mle_eval_point = MleEvalPoint::new(eval_point); + let n_variables = mle_eval_point.n_variables(); + let mle_claim_shift = claim / BaseField::from(1 << n_variables); + + let trace_structure = + mle_eval_info(evals_interaction, const_interaction, n_variables).mask_offsets; + let trace_locations = provider.next_for_structure(&trace_structure); + + Self { + mle_coeff_column_oracle, + mle_eval_point, + mle_claim_shift, + evals_interaction, + const_interaction, + trace_locations, + } + } + + /// Size of this component's trace columns. + pub fn log_size(&self) -> u32 { + self.mle_eval_point.n_variables() as u32 + } + + pub fn eval_info(&self) -> InfoEvaluator { + let n_variables = self.mle_eval_point.n_variables(); + mle_eval_info(self.evals_interaction, self.const_interaction, n_variables) + } +} + +impl<'oracle, O: MleCoeffColumnOracle> Component for MleEvalVerifierComponent<'oracle, O> { + fn n_constraints(&self) -> usize { + self.eval_info().n_constraints + } + + fn max_constraint_log_degree_bound(&self) -> u32 { + self.log_size() + 1 + } + + fn trace_log_degree_bounds(&self) -> TreeVec> { + let log_size = self.log_size(); + let InfoEvaluator { mask_offsets, .. } = self.eval_info(); + mask_offsets.map(|tree_offsets| vec![log_size; tree_offsets.len()]) + } + + fn mask_points( + &self, + point: CirclePoint, + ) -> TreeVec>>> { + let trace_step = CanonicCoset::new(self.log_size()).step(); + let InfoEvaluator { mask_offsets, .. } = self.eval_info(); + mask_offsets.map_cols(|col_offsets| { + col_offsets + .iter() + .map(|offset| point + trace_step.mul_signed(*offset).into_ef()) + .collect() + }) + } + + fn evaluate_constraint_quotients_at_point( + &self, + point: CirclePoint, + mask: &TreeVec>>, + accumulator: &mut PointEvaluationAccumulator, + ) { + let component_mask = mask.sub_tree(&self.trace_locations); + let trace_coset = CanonicCoset::new(self.log_size()).coset; + let vanish_on_trace_eval_inv = coset_vanishing(trace_coset, point).inverse(); + let mut eval = PointEvaluator::new(component_mask, accumulator, vanish_on_trace_eval_inv); + + let mle_coeff_col_eval = self.mle_coeff_column_oracle.evaluate_at_point(point, mask); + let carry_quotients_col_eval = eval_carry_quotient_col(&self.mle_eval_point, point); + + eval_mle_eval_constraints( + self.evals_interaction, + self.const_interaction, + &mut eval, + mle_coeff_col_eval, + &self.mle_eval_point, + self.mle_claim_shift, + carry_quotients_col_eval, + ) + } +} + fn mle_eval_info( mle_interaction: usize, selector_interaction: usize, @@ -589,8 +700,9 @@ mod tests { use rand::{Rng, SeedableRng}; use super::{ - eval_carry_quotient_col, eval_eq_constraints, eval_mle_eval_constraints, - eval_prefix_sum_constraints, gen_carry_quotient_col, MleEvalPoint, + build_constant_trace, build_trace, eval_carry_quotient_col, eval_eq_constraints, + eval_mle_eval_constraints, eval_prefix_sum_constraints, gen_carry_quotient_col, + MleEvalPoint, MleEvalProverComponent, MleEvalVerifierComponent, }; use crate::constraint_framework::constant_columns::gen_is_step_with_offset; use crate::constraint_framework::{assert_constraints, EvalAtRow, TreeColumnSpanProvider}; @@ -611,9 +723,7 @@ mod tests { use crate::core::utils::{bit_reverse, coset_order_to_circle_domain_order}; use crate::core::vcs::blake2_merkle::Blake2sMerkleChannel; use crate::examples::xor::gkr_lookups::accumulation::MIN_LOG_BLOWUP_FACTOR; - use crate::examples::xor::gkr_lookups::mle_eval::{ - build_constant_trace, build_trace, eval_step_selector_with_offset, MleEvalProverComponent, - }; + use crate::examples::xor::gkr_lookups::mle_eval::eval_step_selector_with_offset; #[test] fn mle_eval_prover_component() -> Result<(), VerificationError> { @@ -684,6 +794,88 @@ mod tests { verify(&components.0, channel, commitment_scheme, proof) } + #[test] + fn mle_eval_verifier_component() -> Result<(), VerificationError> { + const N_VARIABLES: usize = 8; + const COEFFS_COL_TRACE: usize = 0; + const EVAL_TRACE: usize = 1; + const CONST_TRACE: usize = 2; + const LOG_EXPAND: u32 = 1; + // Create the test MLE. + let mut rng = SmallRng::seed_from_u64(0); + let log_size = N_VARIABLES as u32; + let size = 1 << log_size; + let mle_coeffs = (0..size).map(|_| rng.gen::()).collect(); + let mle = Mle::::new(mle_coeffs); + let eval_point: [SecureField; N_VARIABLES] = array::from_fn(|_| rng.gen()); + let claim = mle.eval_at_point(&eval_point); + // Setup protocol. + let twiddles = SimdBackend::precompute_twiddles( + CanonicCoset::new(log_size + LOG_EXPAND + MIN_LOG_BLOWUP_FACTOR) + .circle_domain() + .half_coset, + ); + let config = PcsConfig::default(); + let commitment_scheme = &mut CommitmentSchemeProver::new(config, &twiddles); + let channel = &mut Blake2sChannel::default(); + // Build trace. + // 1. MLE coeffs trace. + let mut tree_builder = commitment_scheme.tree_builder(); + tree_builder.extend_evals(mle_coeff_column::build_trace(&mle)); + tree_builder.commit(channel); + // 2. MLE eval trace (eq evals + prefix sum). + let mut tree_builder = commitment_scheme.tree_builder(); + tree_builder.extend_evals(build_trace(&mle, &eval_point, claim)); + tree_builder.commit(channel); + // 3. Constants trace. + let mut tree_builder = commitment_scheme.tree_builder(); + tree_builder.extend_evals(build_constant_trace(N_VARIABLES)); + tree_builder.commit(channel); + // Create components. + let provider = &mut TreeColumnSpanProvider::default(); + let mle_coeffs_col_component = MleCoeffColumnComponent::new( + provider, + MleCoeffColumnEval::new(COEFFS_COL_TRACE, mle.n_variables()), + ); + let mle_eval_component = MleEvalProverComponent::generate( + provider, + &mle_coeffs_col_component, + &eval_point, + mle, + claim, + &twiddles, + EVAL_TRACE, + CONST_TRACE, + ); + let components: &[&dyn ComponentProver] = + &[&mle_coeffs_col_component, &mle_eval_component]; + // Generate proof. + let proof = prove(components, channel, commitment_scheme).unwrap(); + + // Verify. + let provider = &mut TreeColumnSpanProvider::default(); + let mle_coeffs_col_component = MleCoeffColumnComponent::new( + provider, + MleCoeffColumnEval::new(COEFFS_COL_TRACE, N_VARIABLES), + ); + let mle_eval_component = MleEvalVerifierComponent::new( + provider, + &mle_coeffs_col_component, + &eval_point, + claim, + EVAL_TRACE, + CONST_TRACE, + ); + let components = Components(vec![&mle_coeffs_col_component, &mle_eval_component]); + let log_sizes = components.column_log_sizes(); + let channel = &mut Blake2sChannel::default(); + let commitment_scheme = &mut CommitmentSchemeVerifier::::new(config); + commitment_scheme.commit(proof.commitments[0], &log_sizes[0], channel); + commitment_scheme.commit(proof.commitments[1], &log_sizes[1], channel); + commitment_scheme.commit(proof.commitments[2], &log_sizes[2], channel); + verify(&components.0, channel, commitment_scheme, proof) + } + #[test] fn test_mle_eval_constraints_with_log_size_5() { const N_VARIABLES: usize = 5;