From 1bc2dccf3fe06b588dda945f42ff878690200c00 Mon Sep 17 00:00:00 2001 From: Shahar Papini Date: Tue, 13 Feb 2024 12:02:35 +0200 Subject: [PATCH] Fibonacci component --- src/core/oods.rs | 7 +- src/fibonacci/component.rs | 151 +++++++++++++++++++++++ src/{fibonacci.rs => fibonacci/mod.rs} | 162 +++++++++++-------------- 3 files changed, 224 insertions(+), 96 deletions(-) create mode 100644 src/fibonacci/component.rs rename src/{fibonacci.rs => fibonacci/mod.rs} (84%) diff --git a/src/core/oods.rs b/src/core/oods.rs index 416987e24..d5c52d8af 100644 --- a/src/core/oods.rs +++ b/src/core/oods.rs @@ -80,7 +80,6 @@ pub fn get_pair_oods_quotient( pub fn get_oods_values( mask: &Mask, oods_point: CirclePoint, - trace_domains: &[CanonicCoset], trace_polys: &[CirclePoly], ) -> PointMapping { let mut oods_evals = Vec::with_capacity(trace_polys.len()); @@ -90,7 +89,11 @@ pub fn get_oods_values( poly, }); } - mask.get_evaluation(trace_domains, &oods_evals[..]) + let trace_domains = trace_polys + .iter() + .map(|p| CanonicCoset::new(p.log_size())) + .collect::>(); + mask.get_evaluation(&trace_domains, &oods_evals[..]) } // TODO(AlonH): Consider refactoring and using this function in `get_oods_values`. diff --git a/src/fibonacci/component.rs b/src/fibonacci/component.rs new file mode 100644 index 000000000..08197dfe4 --- /dev/null +++ b/src/fibonacci/component.rs @@ -0,0 +1,151 @@ +use num_traits::One; + +use crate::core::air::evaluation::{DomainEvaluationAccumulator, PointEvaluationAccumulator}; +use crate::core::air::{Component, ComponentTrace}; +use crate::core::circle::{CirclePoint, Coset}; +use crate::core::constraints::{coset_vanishing, pair_excluder}; +use crate::core::fields::m31::BaseField; +use crate::core::fields::qm31::SecureField; +use crate::core::fields::{ExtensionOf, Field}; +use crate::core::poly::circle::{CanonicCoset, CircleDomain}; + +pub struct FibonacciComponent { + pub log_size: u32, + pub claim: BaseField, +} + +impl FibonacciComponent { + pub fn new(log_size: u32, claim: BaseField) -> Self { + Self { log_size, claim } + } + + /// Evaluates the step constraint quotient polynomial on a single point. + /// The step constraint is defined as: + /// mask[0]^2 + mask[1]^2 - mask[2] + fn step_constraint_eval_quotient_by_mask>( + &self, + point: CirclePoint, + mask: &[F; 3], + ) -> F { + let constraint_zero_domain = Coset::subgroup(self.log_size); + let constraint_value = mask[0].square() + mask[1].square() - mask[2]; + let selector = pair_excluder( + constraint_zero_domain + .at(constraint_zero_domain.size() - 2) + .into_ef(), + constraint_zero_domain + .at(constraint_zero_domain.size() - 1) + .into_ef(), + point, + ); + let num = constraint_value * selector; + let denom = coset_vanishing(constraint_zero_domain, point); + num / denom + } + + /// Evaluates the boundary constraint quotient polynomial on a single point. + fn boundary_constraint_eval_quotient_by_mask>( + &self, + point: CirclePoint, + mask: &[F; 1], + ) -> F { + let constraint_zero_domain = Coset::subgroup(self.log_size); + let p = constraint_zero_domain.at(constraint_zero_domain.size() - 1); + // On (1,0), we should get 1. + // On p, we should get self.claim. + // 1 + y * (1 - self.claim) * p.y^-1 + // TODO(spapini): Cache the constant. + let linear = F::one() + point.y * (self.claim - BaseField::one()) * p.y.inverse(); + + let num = mask[0] - linear; + let denom = pair_excluder(p.into_ef(), CirclePoint::zero(), point); + num / denom + } +} + +impl Component for FibonacciComponent { + fn max_constraint_log_degree_bound(&self) -> u32 { + self.log_size + 1 + } + + fn evaluate_constraint_quotients_on_domain( + &self, + trace: &ComponentTrace, + evaluation_accumulator: &mut DomainEvaluationAccumulator, + ) { + let poly = &trace.0[0]; + let trace_domain = CanonicCoset::new(self.log_size); + let trace_eval_domain = trace_domain.evaluation_domain(self.log_size + 1); + let trace_eval = poly.evaluate(trace_eval_domain); + + // Step constraint. + let constraint_log_degree_bound = trace_domain.log_size() + 1; + let [mut accum] = evaluation_accumulator.columns([(constraint_log_degree_bound, 1)]); + let constraint_eval_domain = + CircleDomain::constraint_evaluation_domain(constraint_log_degree_bound); + for (off, point_coset, mul) in [ + (0, constraint_eval_domain.half_coset, 1isize), + ( + constraint_eval_domain.half_coset.size(), + constraint_eval_domain.half_coset.conjugate(), + -1, + ), + ] { + let eval = trace_eval.fetch_eval_on_coset(point_coset.shift(trace_domain.index_at(0))); + for (i, point) in point_coset.iter().enumerate() { + let mask = [eval[i], eval[i as isize + mul], eval[i as isize + 2 * mul]]; + let res = self.step_constraint_eval_quotient_by_mask(point, &mask); + accum.accumulate(i + off, res); + } + } + + // Boundary constraint. + let constraint_log_degree_bound = trace_domain.log_size(); + let [mut accum] = evaluation_accumulator.columns([(constraint_log_degree_bound, 1)]); + let constraint_eval_domain = + CircleDomain::constraint_evaluation_domain(constraint_log_degree_bound); + for (off, point_coset) in [ + (0, constraint_eval_domain.half_coset), + ( + constraint_eval_domain.half_coset.size(), + constraint_eval_domain.half_coset.conjugate(), + ), + ] { + let eval = trace_eval.fetch_eval_on_coset(point_coset.shift(trace_domain.index_at(0))); + for (i, point) in point_coset.iter().enumerate() { + let mask = [eval[i]]; + let res = self.boundary_constraint_eval_quotient_by_mask(point, &mask); + accum.accumulate(i + off, res); + } + } + } + + fn mask_values_at_point( + &self, + point: CirclePoint, + trace: &ComponentTrace, + ) -> Vec { + let poly = &trace.0[0]; + let trace_domain = CanonicCoset::new(self.log_size); + vec![ + poly.eval_at_point(point + trace_domain.at(0).into_ef()), + poly.eval_at_point(point + trace_domain.at(1).into_ef()), + poly.eval_at_point(point + trace_domain.at(2).into_ef()), + ] + } + + fn evaluate_quotients_by_mask( + &self, + point: CirclePoint, + mask: &[SecureField], + evaluation_accumulator: &mut PointEvaluationAccumulator, + ) { + let res = self.step_constraint_eval_quotient_by_mask(point, mask.try_into().unwrap()); + let constraint_log_degree_bound = self.log_size + 1; + evaluation_accumulator.accumulate(constraint_log_degree_bound, res); + let res = + self.boundary_constraint_eval_quotient_by_mask(point, &mask[..1].try_into().unwrap()); + let constraint_log_degree_bound = self.log_size; + evaluation_accumulator.accumulate(constraint_log_degree_bound, res); + } +} diff --git a/src/fibonacci.rs b/src/fibonacci/mod.rs similarity index 84% rename from src/fibonacci.rs rename to src/fibonacci/mod.rs index de22645f8..b608eaab3 100644 --- a/src/fibonacci.rs +++ b/src/fibonacci/mod.rs @@ -2,17 +2,16 @@ use std::iter::zip; use num_traits::One; +use self::component::FibonacciComponent; use crate::commitment_scheme::blake2_hash::Blake2sHasher; use crate::commitment_scheme::hasher::Hasher; use crate::commitment_scheme::merkle_decommitment::MerkleDecommitment; use crate::commitment_scheme::merkle_tree::MerkleTree; -use crate::core::air::{Mask, MaskItem}; +use crate::core::air::evaluation::{DomainEvaluationAccumulator, PointEvaluationAccumulator}; +use crate::core::air::{Component, ComponentTrace, Mask, MaskItem}; use crate::core::channel::{Blake2sChannel, Channel as ChannelTrait}; use crate::core::circle::{CirclePoint, Coset}; -use crate::core::constraints::{ - coset_vanishing, pair_excluder, point_vanishing, EvalByEvaluation, EvalByPointMapping, - PolyOracle, -}; +use crate::core::constraints::{coset_vanishing, pair_excluder, point_vanishing, PolyOracle}; use crate::core::fields::m31::BaseField; use crate::core::fields::qm31::SecureField; use crate::core::fields::{ExtensionOf, Field, IntoSlice}; @@ -22,7 +21,7 @@ use crate::core::fri::{ use crate::core::oods::{ get_oods_points, get_oods_quotient, get_oods_values, get_pair_oods_quotient, }; -use crate::core::poly::circle::{CanonicCoset, CircleDomain, CircleEvaluation, PointMapping}; +use crate::core::poly::circle::{CanonicCoset, CircleDomain, CircleEvaluation, CirclePoly}; use crate::core::poly::BitReversedOrder; use crate::core::proof_of_work::{ProofOfWork, ProofOfWorkProof}; use crate::core::queries::Queries; @@ -30,6 +29,8 @@ use crate::core::queries::Queries; type Channel = Blake2sChannel; type MerkleHasher = Blake2sHasher; +mod component; + const LOG_BLOWUP_FACTOR: u32 = 1; // TODO(Andrew): Change to 0 once related bug is fixed. const LOG_LAST_LAYER_DEGREE_BOUND: u32 = 1; @@ -37,6 +38,7 @@ const PROOF_OF_WORK_BITS: u32 = 12; const N_QUERIES: usize = 3; pub struct Fibonacci { + pub component: FibonacciComponent, pub trace_domain: CanonicCoset, pub trace_eval_domain: CircleDomain, pub trace_commitment_domain: CanonicCoset, @@ -83,6 +85,7 @@ impl Fibonacci { let composition_polynomial_commitment_domain = CanonicCoset::new(log_size + 1 + LOG_BLOWUP_FACTOR); Self { + component: FibonacciComponent { log_size, claim }, trace_domain, trace_eval_domain, trace_commitment_domain, @@ -150,22 +153,6 @@ impl Fibonacci { num / denom } - pub fn eval_composition_polynomial, EF: ExtensionOf>( - &self, - random_coeff: EF, - trace: impl PolyOracle, - ) -> EF { - let mut value = random_coeff.pow(0) * self.eval_step_quotient(trace); - value += random_coeff.pow(1) * self.eval_boundary_quotient(trace, 0, BaseField::one()); - value += random_coeff.pow(2) - * self.eval_boundary_quotient( - trace, - self.constraint_zero_domain.size() - 1, - self.claim, - ); - value - } - pub fn get_mask(&self) -> Mask { Mask::new( (0..3) @@ -181,20 +168,15 @@ impl Fibonacci { fn compute_composition_polynomial( &self, random_coeff: SecureField, - trace_evaluation: &CircleEvaluation, - ) -> CircleEvaluation { - let mut composition_polynomial_values = - Vec::with_capacity(self.composition_polynomial_eval_domain.size()); - for p_ind in self.composition_polynomial_eval_domain.iter_indices() { - composition_polynomial_values.push(self.eval_composition_polynomial( - random_coeff, - EvalByEvaluation::new(p_ind, trace_evaluation), - )); - } - CircleEvaluation::new( - self.composition_polynomial_eval_domain, - composition_polynomial_values, - ) + trace: &ComponentTrace, + ) -> CirclePoly { + let mut accumulator = DomainEvaluationAccumulator::new( + random_coeff, + self.component.max_constraint_log_degree_bound(), + ); + self.component + .evaluate_constraint_quotients_on_domain(trace, &mut accumulator); + accumulator.finalize() } pub fn prove(&self) -> FibonacciProof { @@ -202,9 +184,8 @@ impl Fibonacci { // Evaluate and commit on trace. let trace = self.get_trace(); - let trace_poly = trace.interpolate(); - let trace_evaluation = trace_poly.evaluate(self.trace_eval_domain); - let trace_commitment_evaluation = trace_poly + let trace_poly = ComponentTrace(vec![trace.interpolate()]); + let trace_commitment_evaluation = trace_poly.0[0] .evaluate(self.trace_commitment_domain.circle_domain()) .bit_reverse(); let trace_commitment = @@ -215,9 +196,8 @@ impl Fibonacci { // Evaluate and commit on composition polynomial. let random_coeff = channel.draw_random_secure_felts()[0]; - let composition_polynomial = - self.compute_composition_polynomial(random_coeff, &trace_evaluation); - let composition_polynomial_poly = composition_polynomial.interpolate(); + let composition_polynomial_poly = + self.compute_composition_polynomial(random_coeff, &trace_poly); let composition_polynomial_commitment_evaluation = composition_polynomial_poly .evaluate( self.composition_polynomial_commitment_domain @@ -233,8 +213,7 @@ impl Fibonacci { // Evaluate the trace mask and the composition polynomial on the OODS point. let oods_point = CirclePoint::::get_random_point(channel); let mask = self.get_mask(); - let trace_oods_evaluation = - get_oods_values(&mask, oods_point, &[self.trace_domain], &[trace_poly]); + let trace_oods_evaluation = get_oods_values(&mask, oods_point, &trace_poly.0); let composition_polynomial_oods_value = composition_polynomial_poly.eval_at_point(oods_point); @@ -327,15 +306,24 @@ pub fn verify_proof(proof: FibonacciProof) -> bool { let oods_point = CirclePoint::::get_random_point(channel); let mask = fib.get_mask(); let trace_oods_points = get_oods_points(&mask, oods_point, &[fib.trace_domain]); - let oods_point_eval = EvalByPointMapping { - point: oods_point, - point_mapping: &PointMapping { - points: trace_oods_points.clone(), - values: proof.trace_oods_values.clone(), - }, - }; - let composition_polynomial_oods_value = - fib.eval_composition_polynomial(random_coeff, oods_point_eval); + + let mut evaluation_accumulator = PointEvaluationAccumulator::new( + random_coeff, + fib.component.max_constraint_log_degree_bound(), + ); + fib.component.evaluate_quotients_by_mask( + oods_point, + &proof.trace_oods_values, + &mut evaluation_accumulator, + ); + let composition_polynomial_oods_value = evaluation_accumulator.finalize(); + + assert_eq!( + composition_polynomial_oods_value, + proof + .additional_proof_data + .composition_polynomial_oods_value + ); let fri_config = FriConfig::new(LOG_LAST_LAYER_DEGREE_BOUND, LOG_BLOWUP_FACTOR); // TODO(AlonH): Get bounds as public params of the proof. @@ -437,12 +425,13 @@ mod tests { use super::Fibonacci; use crate::commitment_scheme::utils::tests::generate_test_queries; + use crate::core::air::evaluation::PointEvaluationAccumulator; + use crate::core::air::{Component, ComponentTrace}; use crate::core::circle::CirclePoint; - use crate::core::constraints::{EvalByEvaluation, EvalByPointMapping, EvalByPoly}; + use crate::core::constraints::EvalByEvaluation; use crate::core::fields::m31::{BaseField, M31}; use crate::core::fields::qm31::SecureField; - use crate::core::oods::get_oods_points; - use crate::core::poly::circle::{CanonicCoset, CircleEvaluation, PointMapping}; + use crate::core::poly::circle::CanonicCoset; use crate::core::queries::Queries; use crate::core::utils::bit_reverse; use crate::fibonacci::verify_proof; @@ -492,43 +481,26 @@ mod tests { fn test_composition_polynomial_is_low_degree() { let fib = Fibonacci::new(5, m31!(443693538)); let trace = fib.get_trace(); - let trace_poly = trace.interpolate(); - - let extended_evaluation = trace_poly.evaluate(fib.trace_eval_domain); + let trace = ComponentTrace(vec![trace.interpolate()]); // TODO(ShaharS), Change to a channel implementation to retrieve the random // coefficients from extension field. let random_coeff = qm31!(2213980, 2213981, 2213982, 2213983); - - // Compute composition_polynomial on the evaluation domain. - let mut composition_polynomial_values = - Vec::with_capacity(fib.composition_polynomial_eval_domain.size()); - for p_ind in fib.composition_polynomial_eval_domain.iter_indices() { - composition_polynomial_values.push(fib.eval_composition_polynomial( - random_coeff, - EvalByEvaluation::new(p_ind, &extended_evaluation), - )); - } - let composition_polynomial_eval = CircleEvaluation::new( - fib.composition_polynomial_eval_domain, - composition_polynomial_values, - ); - // Interpolate the poly. The poly is indeed of degree lower than the size of - // trace_eval_domain, then it should interpolate correctly. - let interpolated_composition_polynomial_poly = composition_polynomial_eval.interpolate(); + let composition_polynomial_poly = fib.compute_composition_polynomial(random_coeff, &trace); // Evaluate this polynomial at another point, out of trace_eval_domain and compare to what // we expect. - let oods_point = CirclePoint::::get_point(98989892); - let trace_evaluator = EvalByPoly { - point: oods_point, - poly: &trace_poly, - }; + let point = CirclePoint::::get_point(98989892); - assert_eq!( - interpolated_composition_polynomial_poly.eval_at_point(oods_point), - fib.eval_composition_polynomial(random_coeff, trace_evaluator) + let mask_values = fib.component.mask_values_at_point(point, &trace); + let mut evaluation_accumulator = PointEvaluationAccumulator::new( + random_coeff, + fib.component.max_constraint_log_degree_bound(), ); + fib.component + .evaluate_quotients_by_mask(point, &mask_values, &mut evaluation_accumulator); + let oods_value = evaluation_accumulator.finalize(); + assert_eq!(oods_value, composition_polynomial_poly.eval_at_point(point)); } #[test] @@ -594,23 +566,25 @@ mod tests { fn test_prove() { const FIB_LOG_SIZE: u32 = 5; let fib = Fibonacci::new(FIB_LOG_SIZE, m31!(443693538)); + let trace = fib.get_trace(); + let trace = ComponentTrace(vec![trace.interpolate()]); let proof = fib.prove(); let oods_point = proof.additional_proof_data.oods_point; - let mask = fib.get_mask(); - let oods_points = get_oods_points(&mask, oods_point, &[fib.trace_domain]); - let hz = fib.eval_composition_polynomial( + + let mask_values = fib.component.mask_values_at_point(oods_point, &trace); + let mut evaluation_accumulator = PointEvaluationAccumulator::new( proof .additional_proof_data .composition_polynomial_random_coeff, - EvalByPointMapping { - point: oods_point, - point_mapping: &PointMapping { - points: oods_points, - values: proof.trace_oods_values.clone(), - }, - }, + fib.component.max_constraint_log_degree_bound(), + ); + fib.component.evaluate_quotients_by_mask( + oods_point, + &mask_values, + &mut evaluation_accumulator, ); + let hz = evaluation_accumulator.finalize(); assert_eq!( proof