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.
This commit is contained in:
Jack Grigg 2022-11-24 00:36:10 +00:00
parent 0ba0e40b87
commit 93af730c6c
1 changed files with 62 additions and 65 deletions

View File

@ -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<G: Group> {
pub struct EvaluationDomain<F: FieldExt> {
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<G::Scalar>,
barycentric_weight: G::Scalar,
ifft_divisor: F,
extended_ifft_divisor: F,
t_evaluations: Vec<F>,
barycentric_weight: F,
}
impl<G: Group> EvaluationDomain<G> {
impl<F: FieldExt> EvaluationDomain<F> {
/// 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<G: Group> EvaluationDomain<G> {
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<G: Group> EvaluationDomain<G> {
// 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<G: Group> EvaluationDomain<G> {
// 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<G: Group> EvaluationDomain<G> {
/// 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<G>) -> Polynomial<G, LagrangeCoeff> {
pub fn lagrange_from_vec(&self, values: Vec<F>) -> Polynomial<F, LagrangeCoeff> {
assert_eq!(values.len(), self.n as usize);
Polynomial {
@ -156,7 +156,7 @@ impl<G: Group> EvaluationDomain<G> {
/// 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<G>) -> Polynomial<G, Coeff> {
pub fn coeff_from_vec(&self, values: Vec<F>) -> Polynomial<F, Coeff> {
assert_eq!(values.len(), self.n as usize);
Polynomial {
@ -166,35 +166,32 @@ impl<G: Group> EvaluationDomain<G> {
}
/// Returns an empty (zero) polynomial in the coefficient basis
pub fn empty_coeff(&self) -> Polynomial<G, Coeff> {
pub fn empty_coeff(&self) -> Polynomial<F, Coeff> {
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<G, LagrangeCoeff> {
pub fn empty_lagrange(&self) -> Polynomial<F, LagrangeCoeff> {
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<Assigned<G>, LagrangeCoeff>
where
G: Field,
{
pub(crate) fn empty_lagrange_assigned(&self) -> Polynomial<Assigned<F>, 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<G, LagrangeCoeff> {
pub fn constant_lagrange(&self, scalar: F) -> Polynomial<F, LagrangeCoeff> {
Polynomial {
values: vec![scalar; self.n as usize],
_marker: PhantomData,
@ -203,16 +200,16 @@ impl<G: Group> EvaluationDomain<G> {
/// Returns an empty (zero) polynomial in the extended Lagrange coefficient
/// basis
pub fn empty_extended(&self) -> Polynomial<G, ExtendedLagrangeCoeff> {
pub fn empty_extended(&self) -> Polynomial<F, ExtendedLagrangeCoeff> {
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<G, ExtendedLagrangeCoeff> {
pub fn constant_extended(&self, scalar: F) -> Polynomial<F, ExtendedLagrangeCoeff> {
Polynomial {
values: vec![scalar; self.extended_len()],
_marker: PhantomData,
@ -223,7 +220,7 @@ impl<G: Group> EvaluationDomain<G> {
///
/// This function will panic if the provided vector is not the correct
/// length.
pub fn lagrange_to_coeff(&self, mut a: Polynomial<G, LagrangeCoeff>) -> Polynomial<G, Coeff> {
pub fn lagrange_to_coeff(&self, mut a: Polynomial<F, LagrangeCoeff>) -> Polynomial<F, Coeff> {
assert_eq!(a.values.len(), 1 << self.k);
// Perform inverse FFT to obtain the polynomial in coefficient form
@ -239,12 +236,12 @@ impl<G: Group> EvaluationDomain<G> {
/// evaluation domain, rotating by `rotation` if desired.
pub fn coeff_to_extended(
&self,
mut a: Polynomial<G, Coeff>,
) -> Polynomial<G, ExtendedLagrangeCoeff> {
mut a: Polynomial<F, Coeff>,
) -> Polynomial<F, ExtendedLagrangeCoeff> {
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<G: Group> EvaluationDomain<G> {
/// Rotate the extended domain polynomial over the original domain.
pub fn rotate_extended(
&self,
poly: &Polynomial<G, ExtendedLagrangeCoeff>,
poly: &Polynomial<F, ExtendedLagrangeCoeff>,
rotation: Rotation,
) -> Polynomial<G, ExtendedLagrangeCoeff> {
) -> Polynomial<F, ExtendedLagrangeCoeff> {
let new_rotation = ((1 << (self.extended_k - self.k)) * rotation.0.abs()) as usize;
let mut poly = poly.clone();
@ -284,11 +281,11 @@ impl<G: Group> EvaluationDomain<G> {
/// ```
pub(crate) fn get_chunk_of_rotated_extended(
&self,
poly: &Polynomial<G, ExtendedLagrangeCoeff>,
poly: &Polynomial<F, ExtendedLagrangeCoeff>,
rotation: Rotation,
chunk_size: usize,
chunk_index: usize,
) -> Vec<G> {
) -> Vec<F> {
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<G: Group> EvaluationDomain<G> {
/// 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<G, ExtendedLagrangeCoeff>) -> Vec<G> {
pub fn extended_to_coeff(&self, mut a: Polynomial<F, ExtendedLagrangeCoeff>) -> Vec<F> {
assert_eq!(a.values.len(), self.extended_len());
// Inverse FFT
@ -327,15 +324,15 @@ impl<G: Group> EvaluationDomain<G> {
/// polynomial of the $2^k$ size domain.
pub fn divide_by_vanishing_poly(
&self,
mut a: Polynomial<G, ExtendedLagrangeCoeff>,
) -> Polynomial<G, ExtendedLagrangeCoeff> {
mut a: Polynomial<F, ExtendedLagrangeCoeff>,
) -> Polynomial<F, ExtendedLagrangeCoeff> {
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<G: Group> EvaluationDomain<G> {
///
/// `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<G: Group> EvaluationDomain<G> {
// 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<G: Group> EvaluationDomain<G> {
}
/// 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<G: Group> EvaluationDomain<G> {
/// which is the barycentric weight of $\omega^i$.
pub fn l_i_range<I: IntoIterator<Item = i32> + Clone>(
&self,
x: G::Scalar,
xn: G::Scalar,
x: F,
xn: F,
rotations: I,
) -> Vec<G::Scalar> {
) -> Vec<F> {
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<G: Group> EvaluationDomain<G> {
/// 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<G: Group> EvaluationDomain<G> {
/// 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]