plonky2/src/polynomial/division.rs

98 lines
3.2 KiB
Rust
Raw Normal View History

2021-03-30 13:30:31 -07:00
use crate::field::fft::{fft, ifft};
use crate::field::field::Field;
use crate::polynomial::polynomial::PolynomialCoeffs;
2021-03-30 20:16:20 -07:00
use crate::util::log2_strict;
2021-03-30 13:30:31 -07:00
/// Takes a polynomial `a` in coefficient form, and divides it by `Z_H = X^n - 1`.
///
/// This assumes `Z_H | a`, otherwise result is meaningless.
pub(crate) fn divide_by_z_h<F: Field>(mut a: PolynomialCoeffs<F>, n: usize) -> PolynomialCoeffs<F> {
// TODO: Is this special case needed?
if a.coeffs.iter().all(|p| *p == F::ZERO) {
2021-04-23 12:35:19 -07:00
return a;
2021-03-30 13:30:31 -07:00
}
2021-04-02 19:04:26 -07:00
let g = F::MULTIPLICATIVE_GROUP_GENERATOR;
2021-03-30 13:30:31 -07:00
let mut g_pow = F::ONE;
// Multiply the i-th coefficient of `a` by `g^i`. Then `new_a(w^j) = old_a(g.w^j)`.
a.coeffs.iter_mut().for_each(|x| {
2021-04-23 12:35:19 -07:00
*x *= g_pow;
g_pow *= g;
2021-03-30 13:30:31 -07:00
});
let root = F::primitive_root_of_unity(log2_strict(a.len()));
// Equals to the evaluation of `a` on `{g.w^i}`.
let mut a_eval = fft(a);
// Compute the denominators `1/(g^n.w^(n*i) - 1)` using batch inversion.
let denominator_g = g.exp_usize(n);
let root_n = root.exp_usize(n);
let mut root_pow = F::ONE;
let denominators = (0..a_eval.len())
.map(|i| {
if i != 0 {
root_pow = root_pow * root_n;
}
denominator_g * root_pow - F::ONE
})
.collect::<Vec<_>>();
let denominators_inv = F::batch_multiplicative_inverse(&denominators);
// Divide every element of `a_eval` by the corresponding denominator.
// Then, `a_eval` is the evaluation of `a/Z_H` on `{g.w^i}`.
2021-04-21 22:31:45 +02:00
a_eval
.values
2021-03-30 13:30:31 -07:00
.iter_mut()
.zip(denominators_inv.iter())
.for_each(|(x, &d)| {
2021-04-23 12:35:19 -07:00
*x *= d;
2021-03-30 13:30:31 -07:00
});
// `p` is the interpolating polynomial of `a_eval` on `{w^i}`.
let mut p = ifft(a_eval);
// We need to scale it by `g^(-i)` to get the interpolating polynomial of `a_eval` on `{g.w^i}`,
// a.k.a `a/Z_H`.
let g_inv = g.inverse();
let mut g_inv_pow = F::ONE;
p.coeffs.iter_mut().for_each(|x| {
2021-04-23 12:35:19 -07:00
*x *= g_inv_pow;
g_inv_pow *= g_inv;
2021-03-30 13:30:31 -07:00
});
p
}
#[cfg(test)]
mod tests {
use crate::field::crandall_field::CrandallField;
use crate::field::field::Field;
use crate::polynomial::division::divide_by_z_h;
use crate::polynomial::polynomial::PolynomialCoeffs;
#[test]
fn zero_div_z_h() {
type F = CrandallField;
let zero = PolynomialCoeffs::<F>::zero(16);
let quotient = divide_by_z_h(zero.clone(), 4);
assert_eq!(quotient, zero);
}
2021-03-30 13:30:31 -07:00
#[test]
fn division_by_z_h() {
type F = CrandallField;
let zero = F::ZERO;
let one = F::ONE;
let two = F::TWO;
let three = F::from_canonical_u64(3);
let four = F::from_canonical_u64(4);
let five = F::from_canonical_u64(5);
let six = F::from_canonical_u64(6);
// a(x) = Z_4(x) q(x), where
// a(x) = 3 x^7 + 4 x^6 + 5 x^5 + 6 x^4 - 3 x^3 - 4 x^2 - 5 x - 6
// Z_4(x) = x^4 - 1
// q(x) = 3 x^3 + 4 x^2 + 5 x + 6
let a = PolynomialCoeffs::new(vec![-six, -five, -four, -three, six, five, four, three]);
let q = PolynomialCoeffs::new(vec![six, five, four, three, zero, zero, zero, zero]);
let computed_q = divide_by_z_h(a, 4);
assert_eq!(computed_q, q);
2021-03-30 13:30:31 -07:00
}
}