From 1e8257f1ab67a0e7d49091f41b45984a45bd2e9a Mon Sep 17 00:00:00 2001 From: Pratyush Mishra Date: Fri, 4 Aug 2023 00:06:25 -0400 Subject: [PATCH] Add "filter" polynomial (#664) * Add selector polynomial * Work * Rename `selector` -> `poly` * fmt * avoid importing config twice * swo * noop-method-call --------- Co-authored-by: weikengchen --- Cargo.toml | 2 ++ ff/src/fields/mod.rs | 2 +- poly/benches/dense_uv_polynomial.rs | 10 +++--- poly/src/domain/mod.rs | 37 ++++++++++++++++++++++- poly/src/domain/radix2/mod.rs | 47 ++++++++++++++++++++++++++++- test-curves/src/bls12_381/mod.rs | 9 +++++- 6 files changed, 98 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6baa587a7..63ee2a31d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,8 @@ members = [ "test-templates", ] +resolver = "2" + [profile.release] opt-level = 3 lto = "thin" diff --git a/ff/src/fields/mod.rs b/ff/src/fields/mod.rs index e3fa0ee3b..2f85643d4 100644 --- a/ff/src/fields/mod.rs +++ b/ff/src/fields/mod.rs @@ -477,7 +477,7 @@ mod no_std_tests { let felt2 = Fr::one() + Fr::one(); let felt16 = felt2 * felt2 * felt2 * felt2; - assert_eq!(Fr::from(1u8), Fr::one()); + assert_eq!(Fr::from(1u8), Fr::one()); assert_eq!(Fr::from(1u16), Fr::one()); assert_eq!(Fr::from(1u32), Fr::one()); assert_eq!(Fr::from(1u64), Fr::one()); diff --git a/poly/benches/dense_uv_polynomial.rs b/poly/benches/dense_uv_polynomial.rs index 8300d13b5..c825bf8c8 100644 --- a/poly/benches/dense_uv_polynomial.rs +++ b/poly/benches/dense_uv_polynomial.rs @@ -100,23 +100,23 @@ fn bench_div_by_vanishing_poly(b: &mut Bencher, degree: &usize) { fn poly_benches(c: &mut Criterion, name: &'static str) { if ENABLE_ADD_BENCH { - let cur_name = format!("{:?} - add_polynomial", name.clone()); + let cur_name = format!("{:?} - add_polynomial", name); setup_bench::(c, &cur_name, bench_poly_add::); } if ENABLE_ADD_ASSIGN_BENCH { - let cur_name = format!("{:?} - add_assign_polynomial", name.clone()); + let cur_name = format!("{:?} - add_assign_polynomial", name); setup_bench::(c, &cur_name, bench_poly_add_assign::); } if ENABLE_EVALUATE_BENCH { - let cur_name = format!("{:?} - evaluate_polynomial", name.clone()); + let cur_name = format!("{:?} - evaluate_polynomial", name); setup_bench::(c, &cur_name, bench_poly_evaluate::); } if ENABLE_SPARSE_EVALUATE_BENCH { - let cur_name = format!("{:?} - evaluate_sparse_polynomial", name.clone()); + let cur_name = format!("{:?} - evaluate_sparse_polynomial", name); setup_bench::(c, &cur_name, bench_sparse_poly_evaluate::); } if ENABLE_DIV_BY_VANISHING_POLY_BENCH { - let cur_name = format!("{:?} - evaluate_div_by_vanishing_poly", name.clone()); + let cur_name = format!("{:?} - evaluate_div_by_vanishing_poly", name); setup_bench::(c, &cur_name, bench_div_by_vanishing_poly::); } } diff --git a/poly/src/domain/mod.rs b/poly/src/domain/mod.rs index f4cdeb588..2de78e308 100644 --- a/poly/src/domain/mod.rs +++ b/poly/src/domain/mod.rs @@ -9,7 +9,7 @@ use ark_ff::FftField; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; -use ark_std::{fmt, hash, rand::Rng, vec::Vec}; +use ark_std::{fmt, hash, rand::Rng, vec::Vec, Zero}; #[cfg(feature = "parallel")] use rayon::prelude::*; @@ -234,6 +234,41 @@ pub trait EvaluationDomain: tau.pow([self.size() as u64]) - self.coset_offset_pow_size() } + /// Return the filter polynomial of `self` with respect to the subdomain `subdomain`. + /// Assumes that `subdomain` is contained within `self`. + /// + /// # Panics + /// + /// Panics if `subdomain` is not contained within `self`. + fn filter_polynomial(&self, subdomain: &Self) -> crate::univariate::DensePolynomial { + use crate::univariate::DenseOrSparsePolynomial; + let self_vanishing_poly = DenseOrSparsePolynomial::from( + &self.vanishing_polynomial() + * (subdomain.size_as_field_element() + * subdomain.coset_offset().pow([subdomain.size() as u64])), + ); + let subdomain_vanishing_poly = DenseOrSparsePolynomial::from( + &subdomain.vanishing_polynomial() * self.size_as_field_element(), + ); + let (quotient, remainder) = self_vanishing_poly + .divide_with_q_and_r(&subdomain_vanishing_poly) + .unwrap(); + assert!(remainder.is_zero()); + quotient + } + + /// This evaluates at `tau` the filter polynomial for `self` with respect + /// to the subdomain `subdomain`. + fn evaluate_filter_polynomial(&self, subdomain: &Self, tau: F) -> F { + let v_subdomain_of_tau = subdomain.evaluate_vanishing_polynomial(tau); + if v_subdomain_of_tau.is_zero() { + F::one() + } else { + subdomain.size_as_field_element() * self.evaluate_vanishing_polynomial(tau) + / (self.size_as_field_element() * v_subdomain_of_tau) + } + } + /// Returns the `i`-th element of the domain. fn element(&self, i: usize) -> F { let mut result = self.group_gen().pow([i as u64]); diff --git a/poly/src/domain/radix2/mod.rs b/poly/src/domain/radix2/mod.rs index e2e2953c9..73b58fdb6 100644 --- a/poly/src/domain/radix2/mod.rs +++ b/poly/src/domain/radix2/mod.rs @@ -178,7 +178,7 @@ mod tests { EvaluationDomain, Radix2EvaluationDomain, }; use ark_ff::{FftField, Field, One, UniformRand, Zero}; - use ark_std::{rand::Rng, test_rng}; + use ark_std::{collections::BTreeSet, rand::Rng, test_rng}; use ark_test_curves::bls12_381::Fr; #[test] @@ -220,6 +220,51 @@ mod tests { } } + #[test] + fn filter_polynomial_test() { + for log_domain_size in 1..=4 { + let domain_size = 1 << log_domain_size; + let domain = Radix2EvaluationDomain::::new(domain_size).unwrap(); + for log_subdomain_size in 1..=log_domain_size { + let subdomain_size = 1 << log_subdomain_size; + let subdomain = Radix2EvaluationDomain::::new(subdomain_size).unwrap(); + + // Obtain all possible offsets of `subdomain` within `domain`. + let mut possible_offsets = vec![Fr::one()]; + let domain_generator = domain.group_gen(); + + let mut offset = domain_generator; + let subdomain_generator = subdomain.group_gen(); + while offset != subdomain_generator { + possible_offsets.push(offset); + offset *= domain_generator; + } + + assert_eq!(possible_offsets.len(), domain_size / subdomain_size); + + // Get all possible cosets of `subdomain` within `domain`. + let cosets = possible_offsets + .iter() + .map(|offset| subdomain.get_coset(*offset).unwrap()); + + for coset in cosets { + let coset_elements = coset.elements().collect::>(); + let filter_poly = domain.filter_polynomial(&coset); + assert_eq!(filter_poly.degree(), domain_size - subdomain_size); + for element in domain.elements() { + let evaluation = domain.evaluate_filter_polynomial(&coset, element); + assert_eq!(evaluation, filter_poly.evaluate(&element)); + if coset_elements.contains(&element) { + assert_eq!(evaluation, Fr::one()) + } else { + assert_eq!(evaluation, Fr::zero()) + } + } + } + } + } + } + #[test] fn size_of_elements() { for coeffs in 1..10 { diff --git a/test-curves/src/bls12_381/mod.rs b/test-curves/src/bls12_381/mod.rs index c297ac580..7588fb494 100644 --- a/test-curves/src/bls12_381/mod.rs +++ b/test-curves/src/bls12_381/mod.rs @@ -18,7 +18,14 @@ pub mod g2; #[cfg(feature = "bls12_381_curve")] pub mod g2_swu_iso; #[cfg(feature = "bls12_381_curve")] -pub use {fq::*, fq12::*, fq2::*, fq6::*, g1::*, g1_swu_iso::*, g2::*, g2_swu_iso::*}; +pub use { + fq::*, + fq12::*, + fq2::*, + fq6::*, + g1::{G1Affine, G1Projective}, + g2::{G2Affine, G2Projective}, +}; #[cfg(test)] mod tests;