From 93af730c6ca55502d5f1b8eeda8b0587cd8d9328 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 24 Nov 2022 00:36:10 +0000 Subject: [PATCH] Simplify `EvaluationDomain` to only accept field elements We do not use `EvaluationDomain` with group elements, so there is no need to directly use the FFT `Group` abstraction trait here. --- halo2_proofs/src/poly/domain.rs | 127 ++++++++++++++++---------------- 1 file changed, 62 insertions(+), 65 deletions(-) diff --git a/halo2_proofs/src/poly/domain.rs b/halo2_proofs/src/poly/domain.rs index 68ccd5ae..a0987fe2 100644 --- a/halo2_proofs/src/poly/domain.rs +++ b/halo2_proofs/src/poly/domain.rs @@ -2,13 +2,13 @@ //! domain that is of a suitable size for the application. use crate::{ - arithmetic::{best_fft, parallelize, FieldExt, Group}, + arithmetic::{best_fft, parallelize, FieldExt}, plonk::Assigned, }; use super::{Coeff, ExtendedLagrangeCoeff, LagrangeCoeff, Polynomial, Rotation}; -use group::ff::{BatchInvert, Field, PrimeField}; +use group::ff::{BatchInvert, Field}; use std::marker::PhantomData; @@ -16,24 +16,24 @@ use std::marker::PhantomData; /// performing operations on an evaluation domain of size $2^k$ and an extended /// domain of size $2^{k} * j$ with $j \neq 0$. #[derive(Clone, Debug)] -pub struct EvaluationDomain { +pub struct EvaluationDomain { n: u64, k: u32, extended_k: u32, - omega: G::Scalar, - omega_inv: G::Scalar, - extended_omega: G::Scalar, - extended_omega_inv: G::Scalar, - g_coset: G::Scalar, - g_coset_inv: G::Scalar, + omega: F, + omega_inv: F, + extended_omega: F, + extended_omega_inv: F, + g_coset: F, + g_coset_inv: F, quotient_poly_degree: u64, - ifft_divisor: G::Scalar, - extended_ifft_divisor: G::Scalar, - t_evaluations: Vec, - barycentric_weight: G::Scalar, + ifft_divisor: F, + extended_ifft_divisor: F, + t_evaluations: Vec, + barycentric_weight: F, } -impl EvaluationDomain { +impl EvaluationDomain { /// This constructs a new evaluation domain object based on the provided /// values $j, k$. pub fn new(j: u32, k: u32) -> Self { @@ -51,12 +51,12 @@ impl EvaluationDomain { extended_k += 1; } - let mut extended_omega = G::Scalar::root_of_unity(); + let mut extended_omega = F::root_of_unity(); // Get extended_omega, the 2^{extended_k}'th root of unity // The loop computes extended_omega = omega^{2 ^ (S - extended_k)} // Notice that extended_omega ^ {2 ^ extended_k} = omega ^ {2^S} = 1. - for _ in extended_k..G::Scalar::S { + for _ in extended_k..F::S { extended_omega = extended_omega.square(); } let extended_omega = extended_omega; @@ -78,14 +78,14 @@ impl EvaluationDomain { // already. // The coset evaluation domain is: // zeta {1, extended_omega, extended_omega^2, ..., extended_omega^{(2^extended_k) - 1}} - let g_coset = G::Scalar::ZETA; + let g_coset = F::ZETA; let g_coset_inv = g_coset.square(); let mut t_evaluations = Vec::with_capacity(1 << (extended_k - k)); { // Compute the evaluations of t(X) = X^n - 1 in the coset evaluation domain. // We don't have to compute all of them, because it will repeat. - let orig = G::Scalar::ZETA.pow_vartime(&[n as u64, 0, 0, 0]); + let orig = F::ZETA.pow_vartime(&[n as u64, 0, 0, 0]); let step = extended_omega.pow_vartime(&[n as u64, 0, 0, 0]); let mut cur = orig; loop { @@ -99,19 +99,19 @@ impl EvaluationDomain { // Subtract 1 from each to give us t_evaluations[i] = t(zeta * extended_omega^i) for coeff in &mut t_evaluations { - *coeff -= &G::Scalar::one(); + *coeff -= &F::one(); } // Invert, because we're dividing by this polynomial. // We invert in a batch, below. } - let mut ifft_divisor = G::Scalar::from(1 << k); // Inversion computed later - let mut extended_ifft_divisor = G::Scalar::from(1 << extended_k); // Inversion computed later + let mut ifft_divisor = F::from(1 << k); // Inversion computed later + let mut extended_ifft_divisor = F::from(1 << extended_k); // Inversion computed later // The barycentric weight of 1 over the evaluation domain // 1 / \prod_{i != 0} (1 - omega^i) - let mut barycentric_weight = G::Scalar::from(n); // Inversion computed later + let mut barycentric_weight = F::from(n); // Inversion computed later // Compute batch inversion t_evaluations @@ -144,7 +144,7 @@ impl EvaluationDomain { /// Obtains a polynomial in Lagrange form when given a vector of Lagrange /// coefficients of size `n`; panics if the provided vector is the wrong /// length. - pub fn lagrange_from_vec(&self, values: Vec) -> Polynomial { + pub fn lagrange_from_vec(&self, values: Vec) -> Polynomial { assert_eq!(values.len(), self.n as usize); Polynomial { @@ -156,7 +156,7 @@ impl EvaluationDomain { /// Obtains a polynomial in coefficient form when given a vector of /// coefficients of size `n`; panics if the provided vector is the wrong /// length. - pub fn coeff_from_vec(&self, values: Vec) -> Polynomial { + pub fn coeff_from_vec(&self, values: Vec) -> Polynomial { assert_eq!(values.len(), self.n as usize); Polynomial { @@ -166,35 +166,32 @@ impl EvaluationDomain { } /// Returns an empty (zero) polynomial in the coefficient basis - pub fn empty_coeff(&self) -> Polynomial { + pub fn empty_coeff(&self) -> Polynomial { Polynomial { - values: vec![G::group_zero(); self.n as usize], + values: vec![F::zero(); self.n as usize], _marker: PhantomData, } } /// Returns an empty (zero) polynomial in the Lagrange coefficient basis - pub fn empty_lagrange(&self) -> Polynomial { + pub fn empty_lagrange(&self) -> Polynomial { Polynomial { - values: vec![G::group_zero(); self.n as usize], + values: vec![F::zero(); self.n as usize], _marker: PhantomData, } } /// Returns an empty (zero) polynomial in the Lagrange coefficient basis, with /// deferred inversions. - pub(crate) fn empty_lagrange_assigned(&self) -> Polynomial, LagrangeCoeff> - where - G: Field, - { + pub(crate) fn empty_lagrange_assigned(&self) -> Polynomial, LagrangeCoeff> { Polynomial { - values: vec![G::group_zero().into(); self.n as usize], + values: vec![F::zero().into(); self.n as usize], _marker: PhantomData, } } /// Returns a constant polynomial in the Lagrange coefficient basis - pub fn constant_lagrange(&self, scalar: G) -> Polynomial { + pub fn constant_lagrange(&self, scalar: F) -> Polynomial { Polynomial { values: vec![scalar; self.n as usize], _marker: PhantomData, @@ -203,16 +200,16 @@ impl EvaluationDomain { /// Returns an empty (zero) polynomial in the extended Lagrange coefficient /// basis - pub fn empty_extended(&self) -> Polynomial { + pub fn empty_extended(&self) -> Polynomial { Polynomial { - values: vec![G::group_zero(); self.extended_len()], + values: vec![F::zero(); self.extended_len()], _marker: PhantomData, } } /// Returns a constant polynomial in the extended Lagrange coefficient /// basis - pub fn constant_extended(&self, scalar: G) -> Polynomial { + pub fn constant_extended(&self, scalar: F) -> Polynomial { Polynomial { values: vec![scalar; self.extended_len()], _marker: PhantomData, @@ -223,7 +220,7 @@ impl EvaluationDomain { /// /// This function will panic if the provided vector is not the correct /// length. - pub fn lagrange_to_coeff(&self, mut a: Polynomial) -> Polynomial { + pub fn lagrange_to_coeff(&self, mut a: Polynomial) -> Polynomial { assert_eq!(a.values.len(), 1 << self.k); // Perform inverse FFT to obtain the polynomial in coefficient form @@ -239,12 +236,12 @@ impl EvaluationDomain { /// evaluation domain, rotating by `rotation` if desired. pub fn coeff_to_extended( &self, - mut a: Polynomial, - ) -> Polynomial { + mut a: Polynomial, + ) -> Polynomial { assert_eq!(a.values.len(), 1 << self.k); self.distribute_powers_zeta(&mut a.values, true); - a.values.resize(self.extended_len(), G::group_zero()); + a.values.resize(self.extended_len(), F::zero()); best_fft(&mut a.values, self.extended_omega, self.extended_k); Polynomial { @@ -256,9 +253,9 @@ impl EvaluationDomain { /// Rotate the extended domain polynomial over the original domain. pub fn rotate_extended( &self, - poly: &Polynomial, + poly: &Polynomial, rotation: Rotation, - ) -> Polynomial { + ) -> Polynomial { let new_rotation = ((1 << (self.extended_k - self.k)) * rotation.0.abs()) as usize; let mut poly = poly.clone(); @@ -284,11 +281,11 @@ impl EvaluationDomain { /// ``` pub(crate) fn get_chunk_of_rotated_extended( &self, - poly: &Polynomial, + poly: &Polynomial, rotation: Rotation, chunk_size: usize, chunk_index: usize, - ) -> Vec { + ) -> Vec { let new_rotation = ((1 << (self.extended_k - self.k)) * rotation.0.abs()) as usize; poly.get_chunk_of_rotated_helper(rotation.0 < 0, new_rotation, chunk_size, chunk_index) } @@ -299,7 +296,7 @@ impl EvaluationDomain { /// This function will panic if the provided vector is not the correct /// length. // TODO/FIXME: caller should be responsible for truncating - pub fn extended_to_coeff(&self, mut a: Polynomial) -> Vec { + pub fn extended_to_coeff(&self, mut a: Polynomial) -> Vec { assert_eq!(a.values.len(), self.extended_len()); // Inverse FFT @@ -327,15 +324,15 @@ impl EvaluationDomain { /// polynomial of the $2^k$ size domain. pub fn divide_by_vanishing_poly( &self, - mut a: Polynomial, - ) -> Polynomial { + mut a: Polynomial, + ) -> Polynomial { assert_eq!(a.values.len(), self.extended_len()); // Divide to obtain the quotient polynomial in the coset evaluation // domain. parallelize(&mut a.values, |h, mut index| { for h in h { - h.group_scale(&self.t_evaluations[index % self.t_evaluations.len()]); + *h *= &self.t_evaluations[index % self.t_evaluations.len()]; index += 1; } }); @@ -353,7 +350,7 @@ impl EvaluationDomain { /// /// `into_coset` should be set to `true` when moving into the coset, /// and `false` when moving out. This toggles the choice of `zeta`. - fn distribute_powers_zeta(&self, a: &mut [G], into_coset: bool) { + fn distribute_powers_zeta(&self, a: &mut [F], into_coset: bool) { let coset_powers = if into_coset { [self.g_coset, self.g_coset_inv] } else { @@ -364,19 +361,19 @@ impl EvaluationDomain { // Distribute powers to move into/from coset let i = index % (coset_powers.len() + 1); if i != 0 { - a.group_scale(&coset_powers[i - 1]); + *a *= &coset_powers[i - 1]; } index += 1; } }); } - fn ifft(a: &mut [G], omega_inv: G::Scalar, log_n: u32, divisor: G::Scalar) { + fn ifft(a: &mut [F], omega_inv: F, log_n: u32, divisor: F) { best_fft(a, omega_inv, log_n); parallelize(a, |a, _| { for a in a { // Finish iFFT - a.group_scale(&divisor); + *a *= &divisor; } }); } @@ -387,24 +384,24 @@ impl EvaluationDomain { } /// Get $\omega$, the generator of the $2^k$ order multiplicative subgroup. - pub fn get_omega(&self) -> G::Scalar { + pub fn get_omega(&self) -> F { self.omega } /// Get $\omega^{-1}$, the inverse of the generator of the $2^k$ order /// multiplicative subgroup. - pub fn get_omega_inv(&self) -> G::Scalar { + pub fn get_omega_inv(&self) -> F { self.omega_inv } /// Get the generator of the extended domain's multiplicative subgroup. - pub fn get_extended_omega(&self) -> G::Scalar { + pub fn get_extended_omega(&self) -> F { self.extended_omega } /// Multiplies a value by some power of $\omega$, essentially rotating over /// the domain. - pub fn rotate_omega(&self, value: G::Scalar, rotation: Rotation) -> G::Scalar { + pub fn rotate_omega(&self, value: F, rotation: Rotation) -> F { let mut point = value; if rotation.0 >= 0 { point *= &self.get_omega().pow_vartime(&[rotation.0 as u64]); @@ -445,23 +442,23 @@ impl EvaluationDomain { /// which is the barycentric weight of $\omega^i$. pub fn l_i_range + Clone>( &self, - x: G::Scalar, - xn: G::Scalar, + x: F, + xn: F, rotations: I, - ) -> Vec { + ) -> Vec { let mut results; { let rotations = rotations.clone().into_iter(); results = Vec::with_capacity(rotations.size_hint().1.unwrap_or(0)); for rotation in rotations { let rotation = Rotation(rotation); - let result = x - self.rotate_omega(G::Scalar::one(), rotation); + let result = x - self.rotate_omega(F::one(), rotation); results.push(result); } results.iter_mut().batch_invert(); } - let common = (xn - G::Scalar::one()) * self.barycentric_weight; + let common = (xn - F::one()) * self.barycentric_weight; for (rotation, result) in rotations.into_iter().zip(results.iter_mut()) { let rotation = Rotation(rotation); *result = self.rotate_omega(*result * common, rotation); @@ -478,7 +475,7 @@ impl EvaluationDomain { /// Obtain a pinned version of this evaluation domain; a structure with the /// minimal parameters needed to determine the rest of the evaluation /// domain. - pub fn pinned(&self) -> PinnedEvaluationDomain<'_, G> { + pub fn pinned(&self) -> PinnedEvaluationDomain<'_, F> { PinnedEvaluationDomain { k: &self.k, extended_k: &self.extended_k, @@ -490,10 +487,10 @@ impl EvaluationDomain { /// Represents the minimal parameters that determine an `EvaluationDomain`. #[allow(dead_code)] #[derive(Debug)] -pub struct PinnedEvaluationDomain<'a, G: Group> { +pub struct PinnedEvaluationDomain<'a, F: Field> { k: &'a u32, extended_k: &'a u32, - omega: &'a G::Scalar, + omega: &'a F, } #[test]