From 9bf637dd45476d1fc96c3bbfb332288dfdeb8651 Mon Sep 17 00:00:00 2001 From: Alan Szepieniec Date: Sat, 28 Sep 2024 23:44:26 +0200 Subject: [PATCH] perf(RAM): Drop cached polynomials The cached polynomials represent the same information as the original randomized trace, except in a different basis. If they are needed, the relevant randomized trace column is interpolated on the fly. Through various techniques, like batched barycentric evaluation, the drawback of losing the cached polynomials is offset almost completely. In the process, the code becomes notably simpler and, more importantly, requires considerably less RAM. Co-authored-by: Thorkil Schmidiger --- Cargo.toml | 2 +- triton-vm/benches/barycentric_eval.rs | 5 +- triton-vm/src/fri.rs | 55 ++------ triton-vm/src/stark.rs | 179 +++++++++++++++++++------- triton-vm/src/table/master_table.rs | 143 +++++++++----------- 5 files changed, 207 insertions(+), 177 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6ef0b1e8..19a045f6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -84,7 +84,7 @@ strum = { version = "0.26", features = ["derive"] } syn = "2.0" test-strategy = "0.4.0" thiserror = "1.0" -twenty-first = "0.42.0-alpha.9" +twenty-first = "0.42.0-alpha.10" unicode-width = "0.1" divan = "0.1.14" diff --git a/triton-vm/benches/barycentric_eval.rs b/triton-vm/benches/barycentric_eval.rs index 4e116b12..81c663a7 100644 --- a/triton-vm/benches/barycentric_eval.rs +++ b/triton-vm/benches/barycentric_eval.rs @@ -6,7 +6,8 @@ use itertools::Itertools; use rand::prelude::StdRng; use rand::Rng; use rand_core::SeedableRng; -use triton_vm::fri::barycentric_evaluate; +use twenty_first::math::polynomial::barycentric_evaluate; +use twenty_first::prelude::XFieldElement; criterion_main!(benches); criterion_group!( @@ -25,7 +26,7 @@ fn barycentric_eval(c: &mut Criterion) { c.bench_function(&format!("barycentric_evaluation_(1<<{LOG2_N})"), |b| { b.iter_batched( || ((0..1 << LOG2_N).map(|_| rng.gen()).collect_vec(), rng.gen()), - |(codeword, indeterminate)| barycentric_evaluate(&codeword, indeterminate), + |(cw, ind): (Vec, XFieldElement)| barycentric_evaluate(&cw, ind), BatchSize::SmallInput, ) }); diff --git a/triton-vm/src/fri.rs b/triton-vm/src/fri.rs index e0ef19aa..681c7d16 100644 --- a/triton-vm/src/fri.rs +++ b/triton-vm/src/fri.rs @@ -1,8 +1,8 @@ use itertools::Itertools; use num_traits::Zero; use rayon::prelude::*; +use std::ops::Mul; use twenty_first::math::traits::FiniteField; -use twenty_first::math::traits::PrimitiveRootOfUnity; use twenty_first::prelude::*; use crate::arithmetic_domain::ArithmeticDomain; @@ -471,6 +471,9 @@ impl<'stream> FriVerifier<'stream> { fn assert_last_round_codeword_corresponds_to_low_degree_polynomial( &mut self, ) -> VerifierResult<()> { + // todo: remove once deprecated local function `barycentric_evaluate` is removed + use twenty_first::math::polynomial::barycentric_evaluate; + if self.last_round_polynomial.degree() > self.last_round_max_degree.try_into().unwrap() { return Err(FriValidationError::LastRoundPolynomialHasTooHighDegree); } @@ -624,33 +627,15 @@ fn codeword_as_digests(codeword: &[XFieldElement]) -> Vec { /// Panics if the codeword is some length that is not a power of 2 or greater than (1 << 32). /// /// [Credit]: https://github.com/0xPolygonMiden/miden-vm/issues/568 -pub fn barycentric_evaluate( - codeword: &[XFieldElement], +#[deprecated( + since = "0.42.2", + note = "use `twenty_first::math::polynomial::barycentric_evaluate` instead" +)] +pub fn barycentric_evaluate>( + codeword: &[FF], indeterminate: XFieldElement, ) -> XFieldElement { - let root_order = codeword.len().try_into().unwrap(); - let generator = BFieldElement::primitive_root_of_unity(root_order).unwrap(); - let domain_iter = (0..root_order) - .scan(bfe!(1), |acc, _| { - let to_yield = Some(*acc); - *acc *= generator; - to_yield - }) - .collect_vec(); - - let domain_shift = domain_iter.iter().map(|&d| indeterminate - d).collect(); - let domain_shift_inverses = XFieldElement::batch_inversion(domain_shift); - let domain_over_domain_shift = domain_iter - .into_iter() - .zip(domain_shift_inverses) - .map(|(d, inv)| d * inv); - let numerator = domain_over_domain_shift - .clone() - .zip(codeword) - .map(|(dsi, &abscis)| dsi * abscis) - .sum::(); - let denominator = domain_over_domain_shift.sum::(); - numerator / denominator + twenty_first::math::polynomial::barycentric_evaluate(codeword, indeterminate) } #[cfg(test)] @@ -661,7 +646,6 @@ mod tests { use assert2::assert; use assert2::let_assert; use itertools::Itertools; - use proptest::collection::vec; use proptest::prelude::*; use proptest_arbitrary_interop::arb; use rand::prelude::*; @@ -1066,21 +1050,4 @@ mod tests { ) { let _verdict = fri.verify(&mut proof_stream); } - - #[proptest] - fn polynomial_evaluation_and_barycentric_evaluation_are_equivalent( - #[strategy(1_usize..13)] _log_num_coefficients: usize, - #[strategy(1_usize..6)] log_expansion_factor: usize, - #[strategy(vec(arb(), 1 << #_log_num_coefficients))] coefficients: Vec, - #[strategy(arb())] indeterminate: XFieldElement, - ) { - let domain_len = coefficients.len() * (1 << log_expansion_factor); - let domain = ArithmeticDomain::of_length(domain_len).unwrap(); - let polynomial = Polynomial::from(&coefficients); - let codeword = domain.evaluate(&polynomial); - prop_assert_eq!( - polynomial.evaluate(indeterminate), - barycentric_evaluate(&codeword, indeterminate) - ); - } } diff --git a/triton-vm/src/stark.rs b/triton-vm/src/stark.rs index 1de993ba..fcf251ed 100644 --- a/triton-vm/src/stark.rs +++ b/triton-vm/src/stark.rs @@ -23,6 +23,7 @@ use crate::error::ProvingError; use crate::error::VerificationError; use crate::fri; use crate::fri::Fri; +use crate::ndarray_helper::fast_zeros_column_major; use crate::profiler::profiler; use crate::proof::Claim; use crate::proof::Proof; @@ -246,16 +247,22 @@ impl Stark { profiler!(start "linear combination"); profiler!(start "main" ("CC")); - let main_combination_polynomial = - Self::random_linear_sum(master_main_table.interpolation_polynomials(), weights.main); - + let main_combination_codeword = Self::random_linear_combination_of_columns( + master_main_table.randomized_trace_table(), + weights.main, + ); profiler!(stop "main"); + profiler!(start "aux" ("CC")); - let aux_combination_polynomial = - Self::random_linear_sum(master_aux_table.interpolation_polynomials(), weights.aux); + let aux_combination_codeword = Self::random_linear_combination_of_columns( + master_aux_table.randomized_trace_table(), + weights.aux, + ); profiler!(stop "aux"); + let randomized_trace_domain = master_main_table.randomized_trace_domain(); + let main_and_aux_codeword = main_combination_codeword + aux_combination_codeword; let main_and_aux_combination_polynomial = - main_combination_polynomial + aux_combination_polynomial; + randomized_trace_domain.interpolate(main_and_aux_codeword.as_slice().unwrap()); let main_and_aux_codeword = short_domain.evaluate(&main_and_aux_combination_polynomial); profiler!(start "quotient" ("CC")); @@ -361,8 +368,9 @@ impl Stark { Self::read_revealed_rows(fri_domain_table, &revealed_current_row_indices)? } else { Self::recompute_revealed_rows::<{ MasterMainTable::NUM_COLUMNS }, BFieldElement>( - &master_main_table.interpolation_polynomials(), + &master_main_table.randomized_trace_table(), &revealed_current_row_indices, + randomized_trace_domain, fri.domain, ) }; @@ -378,8 +386,9 @@ impl Stark { Self::read_revealed_rows(fri_domain_table, &revealed_current_row_indices)? } else { Self::recompute_revealed_rows( - &master_aux_table.interpolation_polynomials(), + &master_aux_table.randomized_trace_table(), &revealed_current_row_indices, + randomized_trace_domain, fri.domain, ) }; @@ -423,8 +432,8 @@ impl Stark { profiler!(start "quotient calculation (just-in-time)"); let (fri_domain_quotient_segment_codewords, quotient_segment_polynomials) = Self::compute_quotient_segments_with_jit_lde( - master_main_table.interpolation_polynomials(), - master_aux_table.interpolation_polynomials(), + master_main_table.randomized_trace_table(), + master_aux_table.randomized_trace_table(), master_main_table.trace_domain(), master_main_table.randomized_trace_domain(), master_main_table.fri_domain(), @@ -504,6 +513,29 @@ impl Stark { // (and maybe alter trait bounds) once `twenty-first` v0.42.0 is released. } + /// Take a linear combination of the columns of the matrix with given weights. + /// The matrix can be defined over the base or extension fields. + /// + /// # Panics + /// + /// Panics in case of dimension mismatch + fn random_linear_combination_of_columns( + matrix: ArrayView2, + weights: Array1, + ) -> Array1 + where + FF: FiniteField + Mul, + { + assert_eq!(matrix.ncols(), weights.len()); + + let combo = matrix + .axis_iter(Axis(0)) + .into_par_iter() + .map(|row| row.iter().zip_eq(&weights).map(|(&r, &w)| r * w).sum()) + .collect(); + Array1::from_vec(combo) + } + fn fri_domain_segment_polynomials( quotient_segment_polynomials: ArrayView1>, fri_domain: ArithmeticDomain, @@ -604,12 +636,13 @@ impl Stark { /// Parameters: /// - `W : const usize` -- the width of the table, in number of field elements /// - `FF : {BFieldElement, XFieldElement}` - /// - `table_as_interpolation_polynomials : &[Polynomial]` -- the table as - /// a slice of X- or B-FieldElements polynomials, one for each column + /// - `matrix : ArrayView2` -- the table as a two-dimensional array of + /// X- or B-FieldElements, in column (“`F`”) majority /// - `revealed_indices: &[usize]` -- the indices coming from FRI - /// - `trace_domain : ArithmeticDomain` -- the domain over which the trace is - /// interpolated; the trace domain informs this function how the coefficients - /// in the table are to be interpreted + /// - `interpolation_domain : ArithmeticDomain` -- the domain over which the + /// trace is interpolated, probably the "randomized trace domain"; this interpolation + /// domain informs this function how the coefficients in the table are to + /// be interpreted /// - `fri_domain : ArithmeticDomain` -- the domain over which FRI is done; the /// FRI domain is used to determine which indeterminates the given indices /// correspond to. @@ -621,26 +654,34 @@ impl Stark { const W: usize, FF: FiniteField + From + MulAssign, >( - table_as_interpolation_polynomials: &ArrayView1>, + matrix: &ArrayView2, revealed_indices: &[usize], + interpolation_domain: ArithmeticDomain, fri_domain: ArithmeticDomain, ) -> Vec<[FF; W]> { // obtain the evaluation points from the FRI domain let indeterminates = revealed_indices - .iter() + .par_iter() .map(|i| fri_domain.domain_value(*i as u32)) .map(FF::from) - .collect_vec(); + .collect::>(); - // for every column (in parallel), fast multi-point evaluate - let columns = table_as_interpolation_polynomials + // for every column (in parallel), fast multi-point extrapolate + let columns = matrix + .axis_iter(Axis(1)) .into_par_iter() - .flat_map(|poly| poly.batch_evaluate(&indeterminates)) + .flat_map(|column| { + Polynomial::coset_extrapolate( + interpolation_domain.offset, + column.as_slice().unwrap(), + &indeterminates, + ) + }) .collect::>(); // transpose the resulting matrix out-of-place let n = revealed_indices.len(); - let mut rows = vec![FF::zero(); W * n]; + let mut rows = vec![FF::ZERO; W * n]; for i in 0..W { for j in 0..n { rows[j * W + i] = columns[i * n + j]; @@ -1057,8 +1098,8 @@ impl Stark { /// there. The resulting coset-quotients are linearly recombined to produce the /// quotient segment codewords. fn compute_quotient_segments_with_jit_lde( - main_polynomials: ArrayView1>, - aux_polynomials: ArrayView1>, + main_matrix: ArrayView2, + aux_matrix: ArrayView2, trace_domain: ArithmeticDomain, randomized_trace_domain: ArithmeticDomain, fri_domain: ArithmeticDomain, @@ -1074,26 +1115,38 @@ impl Stark { let domain = ArithmeticDomain::of_length(num_rows).unwrap(); // for every coset, evaluate constraints - let mut quotient_multicoset_evaluations = Array2::zeros([num_rows, NUM_QUOTIENT_SEGMENTS]); - let mut main_columns = Array2::zeros([num_rows, main_polynomials.len()]); - let mut aux_columns = Array2::zeros([num_rows, aux_polynomials.len()]); + profiler!(start "zero-initialization"); + let mut quotient_multicoset_evaluations = + fast_zeros_column_major(num_rows, NUM_QUOTIENT_SEGMENTS); + let mut main_columns = fast_zeros_column_major(num_rows, main_matrix.ncols()); + let mut aux_columns = fast_zeros_column_major(num_rows, aux_matrix.ncols()); + profiler!(stop "zero-initialization"); + + profiler!(start "calculate quotients"); for (coset_index, quotient_column) in (0..u64::try_from(NUM_QUOTIENT_SEGMENTS).unwrap()) .zip(quotient_multicoset_evaluations.columns_mut()) { // always also offset by fri domain offset to avoid division-by-zero errors let domain = domain.with_offset(iota.mod_pow(coset_index) * fri_domain.offset); - Zip::from(main_polynomials) + profiler!(start "LDE" ("LDE")); + Zip::from(main_matrix.axis_iter(Axis(1))) .and(main_columns.axis_iter_mut(Axis(1))) .into_par_iter() - .for_each(|(polynomial, column)| { - Array1::from(domain.evaluate(polynomial)).move_into(column) + .for_each(|(trace_column, column)| { + let polynomial = + randomized_trace_domain.interpolate(trace_column.as_slice().unwrap()); + Array1::from(domain.evaluate(&polynomial)).move_into(column); }); - Zip::from(aux_polynomials) + Zip::from(aux_matrix.axis_iter(Axis(1))) .and(aux_columns.axis_iter_mut(Axis(1))) .into_par_iter() - .for_each(|(polynomial, column)| { - Array1::from(domain.evaluate(polynomial)).move_into(column) + .for_each(|(trace_column, column)| { + let polynomial = + randomized_trace_domain.interpolate(trace_column.as_slice().unwrap()); + Array1::from(domain.evaluate(&polynomial)).move_into(column); }); + profiler!(stop "LDE"); + profiler!(start "AIR evaluation" ("AIR")); Array1::from(all_quotients_combined( main_columns.view(), aux_columns.view(), @@ -1103,15 +1156,21 @@ impl Stark { quotient_combination_weights, )) .move_into(quotient_column); + profiler!(stop "AIR evaluation"); } + profiler!(stop "calculate quotients"); - Self::segmentify( + profiler!(start "segmentify"); + let segmentification = Self::segmentify( quotient_multicoset_evaluations, fri_domain.offset, iota, randomized_trace_domain, fri_domain, - ) + ); + profiler!(stop "segmentify"); + + segmentification } /// Map a matrix whose columns represent the evaluation of a high-degree @@ -2561,16 +2620,28 @@ pub(crate) mod tests { #[strategy(arb())] challenges: Challenges, #[strategy(arb())] quotient_weights: [XFieldElement; MasterAuxTable::NUM_CONSTRAINTS], ) { - // set up - let main_polynomials = Array1::from_vec(main_polynomials.to_vec()); - let aux_polynomials = Array1::from_vec(aux_polynomials.to_vec()); + // truncate polynomials to randomized trace domain length many coefficients + fn truncate_coefficients( + mut polynomials: Vec>, + num_coefficients: usize, + ) -> Array1> { + polynomials + .par_iter_mut() + .for_each(|p| p.coefficients.truncate(num_coefficients)); + Array1::from_vec(polynomials) + } - let trace_domain = ArithmeticDomain::of_length(trace_length).unwrap(); - let randomized_trace_domain = ArithmeticDomain::of_length(randomized_trace_length).unwrap(); - let fri_domain = ArithmeticDomain::of_length(4 * randomized_trace_length).unwrap(); - let fri_domain = fri_domain.with_offset(offset); - let quotient_domain = ArithmeticDomain::of_length(4 * randomized_trace_length).unwrap(); - let quotient_domain = quotient_domain.with_offset(offset); + let main_polynomials = + truncate_coefficients(main_polynomials.to_vec(), randomized_trace_length); + let aux_polynomials = + truncate_coefficients(aux_polynomials.to_vec(), randomized_trace_length); + + let trace_domain = ArithmeticDomain::of_length(trace_length)?; + let randomized_trace_domain = ArithmeticDomain::of_length(randomized_trace_length)?; + let fri_domain = + ArithmeticDomain::of_length(4 * randomized_trace_length)?.with_offset(offset); + let quotient_domain = + ArithmeticDomain::of_length(4 * randomized_trace_length)?.with_offset(offset); let (quotient_segment_codewords_old, quotient_segment_polynomials_old) = compute_quotient_segments_old( @@ -2583,10 +2654,26 @@ pub(crate) mod tests { "ient_weights, ); + let mut main_matrix = Array2::zeros([randomized_trace_length, main_polynomials.len()].f()); + Zip::from(main_polynomials.axis_iter(Axis(0))) + .and(main_matrix.axis_iter_mut(Axis(1))) + .par_for_each(|polynomial, column| { + Array1::from_vec(randomized_trace_domain.evaluate(&polynomial[()])) + .move_into(column); + }); + + let mut aux_matrix = Array2::zeros([randomized_trace_length, aux_polynomials.len()].f()); + Zip::from(aux_polynomials.axis_iter(Axis(0))) + .and(aux_matrix.axis_iter_mut(Axis(1))) + .par_for_each(|polynomial, column| { + Array1::from_vec(randomized_trace_domain.evaluate(&polynomial[()])) + .move_into(column); + }); + let (quotient_segment_codewords_new, quotient_segment_polynomials_new) = Stark::compute_quotient_segments_with_jit_lde( - main_polynomials.view(), - aux_polynomials.view(), + main_matrix.view(), + aux_matrix.view(), trace_domain, randomized_trace_domain, fri_domain, diff --git a/triton-vm/src/table/master_table.rs b/triton-vm/src/table/master_table.rs index f7346dd5..4ee96598 100644 --- a/triton-vm/src/table/master_table.rs +++ b/triton-vm/src/table/master_table.rs @@ -139,7 +139,7 @@ use crate::table::TraceTable; /// defines that part of the AIR that is relevant to it. /// /// The following points are of note: -/// - The [`MasterMainColumns are the randomizer +/// - The [`MasterAuxiliaryTable`][master_aux_table]'s rightmost columns are the randomizer /// codewords. These are necessary for zero-knowledge. /// - The cross-table argument has zero width for the [`MasterMainTable`] and /// [`MasterAuxiliaryTable`][master_aux_table] but does induce a nonzero number of constraints @@ -159,7 +159,8 @@ where + MulAssign + From + BFieldCodec - + Mul; + + Mul + + Mul; const NUM_COLUMNS: usize; @@ -191,6 +192,7 @@ where /// polynomials. fn trace_table_mut(&mut self) -> ArrayViewMut2; + /// The trace data _with_ trace randomizers, in column-major (“`F`”) order. fn randomized_trace_table(&self) -> ArrayView2; fn randomized_trace_table_mut(&mut self) -> ArrayViewMut2; @@ -287,8 +289,6 @@ where self.memoize_low_degree_extended_table(extended_columns); profiler!(stop "memoize"); } - - self.memoize_interpolation_polynomials(interpolation_polynomials); } /// Not intended for direct use, but through [`Self::low_degree_extend_all_columns`]. @@ -308,23 +308,46 @@ where /// implementation we cannot access the implementing object's fields. fn fri_domain_table(&self) -> Option>; - /// Memoize the polynomials interpolating the columns. - /// Not intended for direct use, but through [`Self::low_degree_extend_all_columns`]. - #[doc(hidden)] - fn memoize_interpolation_polynomials( - &mut self, - interpolation_polynomials: Array1>, - ); - - /// Requires having called - /// [`low_degree_extend_all_columns`](Self::low_degree_extend_all_columns) first. - fn interpolation_polynomials(&self) -> ArrayView1>; - /// Get one row of the table at an arbitrary index. Notably, the index does not have to be in /// any of the domains. In other words, can be used to compute out-of-domain rows. Requires /// having called [`low_degree_extend_all_columns`](Self::low_degree_extend_all_columns) first. /// Does not include randomizer polynomials. - fn out_of_domain_row(&self, indeterminate: XFieldElement) -> Array1; + fn out_of_domain_row(&self, indeterminate: XFieldElement) -> Array1 { + let mut ood_row = Array1::from_vec(vec![ + XFieldElement::ZERO; + self.randomized_trace_table().ncols() + ]); + + // The following is a batched version of barycentric Lagrangian evaluation. + // Since the method `barycentric_evaluate` is self-contained, not returning + // intermediate items necessary for batching, and since returning and reusing + // those indermediate items would produce a challenging interface, the relevant + // parts are reimplemented here. + let domain = self.randomized_trace_domain().domain_values(); + let domain_shift = domain.iter().map(|&d| indeterminate - d).collect(); + let domain_shift_inverses = XFieldElement::batch_inversion(domain_shift); + let domain_over_domain_shift = domain + .into_iter() + .zip(domain_shift_inverses) + .map(|(d, inv)| d * inv); + let denominator_inverse = domain_over_domain_shift + .clone() + .sum::() + .inverse(); + + Zip::from(ood_row.axis_iter_mut(Axis(0))) + .and(self.randomized_trace_table().axis_iter(Axis(1))) + .par_for_each(|v, codeword| { + let numerator = domain_over_domain_shift + .clone() + .zip(codeword) + .map(|(dsi, &abscis)| abscis * dsi) + .sum::(); + + Array0::from_elem((), numerator * denominator_inverse).move_into(v); + }); + ood_row + } /// Compute a Merkle tree of the FRI domain table. Every row gives one leaf in the tree. /// The function [`hash_row`](Self::hash_one_row) is used to hash each row. @@ -368,15 +391,20 @@ where .unwrap_or(1); let fri_domain = self.fri_domain(); let mut sponge_states = vec![SpongeWithPendingAbsorb::new(); fri_domain.length]; - let interpolants = self.interpolation_polynomials(); let mut codewords = Array2::zeros([fri_domain.length, num_threads]); - for interpolants_chunk in interpolants.axis_chunks_iter(Axis(0), num_threads) { - let mut codewords = codewords.slice_mut(s![.., 0..interpolants_chunk.len()]); + for trace_columns in self + .randomized_trace_table() + .axis_chunks_iter(Axis(1), num_threads) + { + let mut codewords = codewords.slice_mut(s![.., 0..trace_columns.ncols()]); Zip::from(codewords.axis_iter_mut(Axis(1))) - .and(interpolants_chunk.axis_iter(Axis(0))) - .par_for_each(|codeword, interpolant| { - let lde_codeword = fri_domain.evaluate(&interpolant[()]); + .and(trace_columns.axis_iter(Axis(1))) + .par_for_each(|codeword, trace_column| { + let interpolant = self + .randomized_trace_domain() + .interpolate(trace_column.as_slice().unwrap()); + let lde_codeword = fri_domain.evaluate(&interpolant); Array1::from(lde_codeword).move_into(codeword); }); sponge_states @@ -466,7 +494,6 @@ pub struct MasterMainTable { randomized_trace_table: Array2, low_degree_extended_table: Option>, - interpolation_polynomials: Option>>, } /// See [`MasterTable`]. @@ -481,7 +508,6 @@ pub struct MasterAuxTable { randomized_trace_table: Array2, low_degree_extended_table: Option>, - interpolation_polynomials: Option>>, } impl MasterTable for MasterMainTable { @@ -557,39 +583,6 @@ impl MasterTable for MasterMainTable { Some(table.view()) } } - - fn memoize_interpolation_polynomials( - &mut self, - interpolation_polynomials: Array1>, - ) { - self.interpolation_polynomials = Some(interpolation_polynomials); - } - - fn interpolation_polynomials(&self) -> ArrayView1> { - let Some(interpolation_polynomials) = &self.interpolation_polynomials else { - panic!("Interpolation polynomials must be computed first."); - }; - interpolation_polynomials.view() - } - - fn out_of_domain_row(&self, indeterminate: XFieldElement) -> Array1 { - // Evaluate a base field polynomial in an extension field point. Manual re-implementation - // to overcome the lack of the corresponding functionality in `twenty-first`. - let evaluate = |bfp: &Polynomial<_>, x| { - let mut acc = XFieldElement::zero(); - for &coefficient in bfp.coefficients.iter().rev() { - acc *= x; - acc += coefficient; - } - acc - }; - - self.interpolation_polynomials() - .into_par_iter() - .map(|polynomial| evaluate(polynomial, indeterminate)) - .collect::>() - .into() - } } impl MasterTable for MasterAuxTable { @@ -667,29 +660,6 @@ impl MasterTable for MasterAuxTable { Some(table.view()) } } - - fn memoize_interpolation_polynomials( - &mut self, - interpolation_polynomials: Array1>, - ) { - self.interpolation_polynomials = Some(interpolation_polynomials); - } - - fn interpolation_polynomials(&self) -> ArrayView1> { - let Some(interpolation_polynomials) = &self.interpolation_polynomials else { - panic!("Interpolation polynomials must be computed first."); - }; - interpolation_polynomials.view() - } - - fn out_of_domain_row(&self, indeterminate: XFieldElement) -> Array1 { - self.interpolation_polynomials() - .slice(s![..Self::NUM_COLUMNS]) - .into_par_iter() - .map(|polynomial| polynomial.evaluate(indeterminate)) - .collect::>() - .into() - } } type PadFunction = fn(ArrayViewMut2, usize); @@ -729,7 +699,6 @@ impl MasterMainTable { fri_domain, randomized_trace_table, low_degree_extended_table: None, - interpolation_polynomials: None, }; // memory-like tables must be filled in before clock jump differences are known, hence @@ -858,7 +827,6 @@ impl MasterMainTable { fri_domain: self.fri_domain(), randomized_trace_table: randomized_trace_auxiliary_table, low_degree_extended_table: None, - interpolation_polynomials: None, }; profiler!(start "slice master table"); @@ -1342,6 +1310,14 @@ mod tests { ); } + #[test] + fn randomized_trace_tables_are_in_column_major_order() { + let (_, _, main, aux, _) = + master_tables_for_low_security_level(ProgramAndInput::new(triton_program!(halt))); + main.randomized_trace_table().column(0).as_slice().unwrap(); + aux.randomized_trace_table().column(0).as_slice().unwrap(); + } + #[test] fn zerofiers_are_correct() { let big_order = 16; @@ -2026,7 +2002,6 @@ mod tests { fri_domain, randomized_trace_table, low_degree_extended_table: None, - interpolation_polynomials: None, }; let num_rows = trace_domain.length;