// 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 std::time::Duration; use air::{ Air, AirContext, Assertion, AuxRandElements, ConstraintCompositionCoefficients, EvaluationFrame, FieldExtension, GkrRandElements, LagrangeKernelRandElements, PartitionOptions, ProofOptions, TraceInfo, TransitionConstraintDegree, }; use criterion::{criterion_group, criterion_main, BatchSize, BenchmarkId, Criterion}; use crypto::{hashers::Blake3_256, DefaultRandomCoin, MerkleTree, RandomCoin}; use math::{fields::f64::BaseElement, ExtensionOf, FieldElement}; use winter_prover::{ matrix::ColMatrix, CompositionPoly, CompositionPolyTrace, DefaultConstraintCommitment, DefaultConstraintEvaluator, DefaultTraceLde, Prover, ProverGkrProof, StarkDomain, Trace, TracePolyTable, }; const TRACE_LENS: [usize; 2] = [2_usize.pow(16), 2_usize.pow(20)]; const AUX_TRACE_WIDTH: usize = 2; fn prove_with_lagrange_kernel(c: &mut Criterion) { let mut group = c.benchmark_group("Lagrange kernel column"); group.sample_size(10); group.measurement_time(Duration::from_secs(20)); for &trace_len in TRACE_LENS.iter() { group.bench_function(BenchmarkId::new("prover", trace_len), |b| { let trace = LagrangeTrace::new(trace_len, AUX_TRACE_WIDTH); let prover = LagrangeProver::new(AUX_TRACE_WIDTH); b.iter_batched( || trace.clone(), |trace| prover.prove(trace).unwrap(), BatchSize::SmallInput, ) }); } } criterion_group!(lagrange_kernel_group, prove_with_lagrange_kernel); criterion_main!(lagrange_kernel_group); // TRACE // ================================================================================================= #[derive(Clone, Debug)] struct LagrangeTrace { // dummy main trace main_trace: ColMatrix, info: TraceInfo, } impl LagrangeTrace { fn new(trace_len: usize, aux_segment_width: usize) -> Self { assert!(trace_len < u32::MAX.try_into().unwrap()); let main_trace_col: Vec = (0..trace_len).map(|idx| BaseElement::from(idx as u32)).collect(); Self { main_trace: ColMatrix::new(vec![main_trace_col]), info: TraceInfo::new_multi_segment(1, aux_segment_width, 0, trace_len, vec![]), } } fn len(&self) -> usize { self.main_trace.num_rows() } } impl Trace for LagrangeTrace { type BaseField = BaseElement; fn info(&self) -> &TraceInfo { &self.info } fn main_segment(&self) -> &ColMatrix { &self.main_trace } fn read_main_frame(&self, row_idx: usize, frame: &mut air::EvaluationFrame) { let next_row_idx = row_idx + 1; assert_ne!(next_row_idx, self.len()); self.main_trace.read_row_into(row_idx, frame.current_mut()); self.main_trace.read_row_into(next_row_idx, frame.next_mut()); } } // AIR // ================================================================================================= struct LagrangeKernelAir { context: AirContext, } impl Air for LagrangeKernelAir { type BaseField = BaseElement; type GkrProof = (); type GkrVerifier = (); type PublicInputs = (); fn new(trace_info: TraceInfo, _pub_inputs: Self::PublicInputs, options: ProofOptions) -> Self { Self { context: AirContext::new_multi_segment( trace_info, vec![TransitionConstraintDegree::new(1)], vec![TransitionConstraintDegree::new(1)], 1, 1, Some(0), options, ), } } fn context(&self) -> &AirContext { &self.context } fn evaluate_transition>( &self, frame: &EvaluationFrame, _periodic_values: &[E], result: &mut [E], ) { let current = frame.current()[0]; let next = frame.next()[0]; // increments by 1 result[0] = next - current - E::ONE; } fn get_assertions(&self) -> Vec> { vec![Assertion::single(0, 0, BaseElement::ZERO)] } fn evaluate_aux_transition( &self, _main_frame: &EvaluationFrame, _aux_frame: &EvaluationFrame, _periodic_values: &[F], _aux_rand_elements: &AuxRandElements, _result: &mut [E], ) where F: FieldElement, E: FieldElement + ExtensionOf, { // do nothing } fn get_aux_assertions>( &self, _aux_rand_elements: &AuxRandElements, ) -> Vec> { vec![Assertion::single(1, 0, E::ZERO)] } } // LagrangeProver // ================================================================================================ struct LagrangeProver { aux_trace_width: usize, options: ProofOptions, } impl LagrangeProver { fn new(aux_trace_width: usize) -> Self { Self { aux_trace_width, options: ProofOptions::new(1, 2, 0, FieldExtension::None, 2, 1), } } } impl Prover for LagrangeProver { type BaseField = BaseElement; type Air = LagrangeKernelAir; type Trace = LagrangeTrace; type HashFn = Blake3_256; type VC = MerkleTree>; type RandomCoin = DefaultRandomCoin; type TraceLde> = DefaultTraceLde; type ConstraintCommitment> = DefaultConstraintCommitment; type ConstraintEvaluator<'a, E: FieldElement> = DefaultConstraintEvaluator<'a, LagrangeKernelAir, E>; fn get_pub_inputs(&self, _trace: &Self::Trace) -> <::Air as Air>::PublicInputs { } fn options(&self) -> &ProofOptions { &self.options } fn new_trace_lde( &self, trace_info: &TraceInfo, main_trace: &ColMatrix, domain: &StarkDomain, partition_option: PartitionOptions, ) -> (Self::TraceLde, TracePolyTable) where E: math::FieldElement, { DefaultTraceLde::new(trace_info, main_trace, domain, partition_option) } fn build_constraint_commitment>( &self, composition_poly_trace: CompositionPolyTrace, num_constraint_composition_columns: usize, domain: &StarkDomain, partition_options: PartitionOptions, ) -> (Self::ConstraintCommitment, CompositionPoly) { DefaultConstraintCommitment::new( composition_poly_trace, num_constraint_composition_columns, domain, partition_options, ) } fn new_evaluator<'a, E>( &self, air: &'a Self::Air, aux_rand_elements: Option>, composition_coefficients: ConstraintCompositionCoefficients, ) -> Self::ConstraintEvaluator<'a, E> where E: math::FieldElement, { DefaultConstraintEvaluator::new(air, aux_rand_elements, composition_coefficients) } fn generate_gkr_proof( &self, main_trace: &Self::Trace, public_coin: &mut Self::RandomCoin, ) -> (ProverGkrProof, GkrRandElements) where E: FieldElement, { let main_trace = main_trace.main_segment(); let lagrange_kernel_rand_elements = { let log_trace_len = main_trace.num_rows().ilog2() as usize; let mut rand_elements = Vec::with_capacity(log_trace_len); for _ in 0..log_trace_len { rand_elements.push(public_coin.draw().unwrap()); } LagrangeKernelRandElements::new(rand_elements) }; ((), GkrRandElements::new(lagrange_kernel_rand_elements, Vec::new())) } fn build_aux_trace( &self, main_trace: &Self::Trace, aux_rand_elements: &AuxRandElements, ) -> ColMatrix where E: FieldElement, { let main_trace = main_trace.main_segment(); let mut columns = Vec::new(); let lagrange_kernel_rand_elements = aux_rand_elements .lagrange() .expect("expected lagrange kernel random elements to be present."); // first build the Lagrange kernel column { let r = lagrange_kernel_rand_elements; let mut lagrange_col = Vec::with_capacity(main_trace.num_rows()); for row_idx in 0..main_trace.num_rows() { let mut row_value = E::ONE; for (bit_idx, &r_i) in r.iter().enumerate() { if row_idx & (1 << bit_idx) == 0 { row_value *= E::ONE - r_i; } else { row_value *= r_i; } } lagrange_col.push(row_value); } columns.push(lagrange_col); } // Then all other auxiliary columns let rand_summed = lagrange_kernel_rand_elements.iter().fold(E::ZERO, |acc, &r| acc + r); for _ in 1..self.aux_trace_width { // building a dummy auxiliary column let column = main_trace .get_column(0) .iter() .map(|row_val| rand_summed.mul_base(*row_val)) .collect(); columns.push(column); } ColMatrix::new(columns) } }