diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e5eced54..10e382fb7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ - (`ark-poly`) Add fast polynomial division - (`ark-ec`) Improve GLV scalar multiplication performance by skipping leading zeroes. - (`ark-poly`) Make `SparsePolynomial.coeffs` field public +- [\#1044](https://github.com/arkworks-rs/algebra/pull/1044) Add implementation for small field with native integer types ### Breaking changes diff --git a/ff/Cargo.toml b/ff/Cargo.toml index 30323fdaa..31965cb92 100644 --- a/ff/Cargo.toml +++ b/ff/Cargo.toml @@ -42,4 +42,4 @@ hex.workspace = true default = [] std = [ "ark-std/std", "ark-serialize/std" ] parallel = [ "std", "rayon", "ark-std/parallel", "ark-serialize/parallel" ] -asm = [] +asm = [] \ No newline at end of file diff --git a/test-curves/Cargo.toml b/test-curves/Cargo.toml index fda91432b..adf24b8db 100644 --- a/test-curves/Cargo.toml +++ b/test-curves/Cargo.toml @@ -78,3 +78,8 @@ harness = false name = "mnt6_753" path = "benches/mnt6_753.rs" harness = false + +[[bench]] +name = "smallfp" +path = "benches/smallfp.rs" +harness = false diff --git a/test-curves/benches/smallfp.rs b/test-curves/benches/smallfp.rs new file mode 100644 index 000000000..75bb91563 --- /dev/null +++ b/test-curves/benches/smallfp.rs @@ -0,0 +1,35 @@ +use ark_algebra_bench_templates::*; +use ark_ff::fields::{Fp64, MontBackend, MontConfig}; +use ark_test_curves::{ + smallfp32::{SmallF32, SmallF32Mont}, + smallfp64::{SmallF64, SmallF64Mont}, +}; + +#[derive(MontConfig)] +#[modulus = "18446744069414584321"] +#[generator = "7"] +pub struct F64Config; +pub type F64 = Fp64>; + +#[derive(MontConfig)] +#[modulus = "2147483647"] +#[generator = "7"] +pub struct F32Config; +pub type F32 = Fp64>; + +f_bench!(prime, "F32", F32); +f_bench!(prime, "SmallF32", SmallF32); +f_bench!(prime, "SmallF32Mont", SmallF32Mont); + +f_bench!(prime, "F64", F64); +f_bench!(prime, "SmallF64", SmallF64); +f_bench!(prime, "SmallF64Mont", SmallF64Mont); + +criterion_main!( + f32::benches, + smallf32::benches, + smallf32mont::benches, + f64::benches, + smallf64::benches, + smallf64mont::benches, +); diff --git a/test-curves/src/lib.rs b/test-curves/src/lib.rs index 0ebe128f5..15abae16d 100644 --- a/test-curves/src/lib.rs +++ b/test-curves/src/lib.rs @@ -31,3 +31,9 @@ pub mod bn384_small_two_adicity; pub mod secp256k1; pub mod fp128; + +pub mod smallfp128; +pub mod smallfp16; +pub mod smallfp32; +pub mod smallfp64; +pub mod smallfp8; diff --git a/test-curves/src/smallfp128.rs b/test-curves/src/smallfp128.rs new file mode 100644 index 000000000..0ffaf9d43 --- /dev/null +++ b/test-curves/src/smallfp128.rs @@ -0,0 +1,26 @@ +use ark_ff::ark_ff_macros::SmallFpConfig; +use ark_ff::{BigInt, SmallFp, SmallFpConfig, SqrtPrecomputation}; + +#[derive(SmallFpConfig)] +#[modulus = "143244528689204659050391023439224324689"] +#[generator = "3"] +#[backend = "standard"] +pub struct SmallF128Config; +pub type SmallF128 = SmallFp; + +#[derive(SmallFpConfig)] +#[modulus = "143244528689204659050391023439224324689"] +#[generator = "3"] +#[backend = "montgomery"] +pub struct SmallF128ConfigMont; +pub type SmallF128Mont = SmallFp; + +#[cfg(test)] +mod tests { + use super::*; + use ark_algebra_test_templates::*; + use ark_std::vec; + + test_small_field!(f128; SmallF128); + test_small_field!(f128_mont; SmallF128Mont); +} diff --git a/test-curves/src/smallfp16.rs b/test-curves/src/smallfp16.rs new file mode 100644 index 000000000..e0813e397 --- /dev/null +++ b/test-curves/src/smallfp16.rs @@ -0,0 +1,26 @@ +use ark_ff::ark_ff_macros::SmallFpConfig; +use ark_ff::{BigInt, SmallFp, SmallFpConfig, SqrtPrecomputation}; + +#[derive(SmallFpConfig)] +#[modulus = "65521"] +#[generator = "17"] +#[backend = "standard"] +pub struct SmallF16Config; +pub type SmallF16 = SmallFp; + +#[derive(SmallFpConfig)] +#[modulus = "65521"] +#[generator = "17"] +#[backend = "montgomery"] +pub struct SmallF16ConfigMont; +pub type SmallF16Mont = SmallFp; + +#[cfg(test)] +mod tests { + use super::*; + use ark_algebra_test_templates::*; + use ark_std::vec; + + test_small_field!(f16; SmallF16); + test_small_field!(f16_mont; SmallF16Mont); +} diff --git a/test-curves/src/smallfp32.rs b/test-curves/src/smallfp32.rs new file mode 100644 index 000000000..38ce45e63 --- /dev/null +++ b/test-curves/src/smallfp32.rs @@ -0,0 +1,26 @@ +use ark_ff::ark_ff_macros::SmallFpConfig; +use ark_ff::{BigInt, SmallFp, SmallFpConfig, SqrtPrecomputation}; + +#[derive(SmallFpConfig)] +#[modulus = "2147483647"] // m31 +#[generator = "7"] +#[backend = "standard"] +pub struct SmallField; +pub type SmallF32 = SmallFp; + +#[derive(SmallFpConfig)] +#[modulus = "2147483647"] // m31 +#[generator = "7"] +#[backend = "montgomery"] +pub struct SmallFieldMont; +pub type SmallF32Mont = SmallFp; + +#[cfg(test)] +mod tests { + use super::*; + use ark_algebra_test_templates::*; + use ark_std::vec; + + test_small_field!(f32; SmallF32); + test_small_field!(f32_mont; SmallF32Mont); +} diff --git a/test-curves/src/smallfp64.rs b/test-curves/src/smallfp64.rs new file mode 100644 index 000000000..1e85e7fff --- /dev/null +++ b/test-curves/src/smallfp64.rs @@ -0,0 +1,26 @@ +use ark_ff::ark_ff_macros::SmallFpConfig; +use ark_ff::{BigInt, SmallFp, SmallFpConfig, SqrtPrecomputation}; + +#[derive(SmallFpConfig)] +#[modulus = "18446744069414584321"] // Goldilock's prime 2^64 - 2^32 + 1 +#[generator = "7"] +#[backend = "standard"] +pub struct SmallF64Config; +pub type SmallF64 = SmallFp; + +#[derive(SmallFpConfig)] +#[modulus = "18446744069414584321"] // Goldilock's prime 2^64 - 2^32 + 1 +#[generator = "7"] +#[backend = "montgomery"] +pub struct SmallF64ConfigMont; +pub type SmallF64Mont = SmallFp; + +#[cfg(test)] +mod tests { + use super::*; + use ark_algebra_test_templates::*; + use ark_std::vec; + + test_small_field!(f64; SmallF64); + test_small_field!(f64_mont; SmallF64Mont); +} diff --git a/test-curves/src/smallfp8.rs b/test-curves/src/smallfp8.rs new file mode 100644 index 000000000..f8a4ed64a --- /dev/null +++ b/test-curves/src/smallfp8.rs @@ -0,0 +1,26 @@ +use ark_ff::ark_ff_macros::SmallFpConfig; +use ark_ff::{BigInt, SmallFp, SmallFpConfig, SqrtPrecomputation}; + +#[derive(SmallFpConfig)] +#[modulus = "251"] +#[generator = "6"] +#[backend = "standard"] +pub struct SmallF8Config; +pub type SmallF8 = SmallFp; + +#[derive(SmallFpConfig)] +#[modulus = "251"] +#[generator = "6"] +#[backend = "montgomery"] +pub struct SmallF8ConfigMont; +pub type SmallF8Mont = SmallFp; + +#[cfg(test)] +mod tests { + use super::*; + use ark_algebra_test_templates::*; + use ark_std::vec; + + test_small_field!(f8; SmallF8); + test_small_field!(f8_mont; SmallF8Mont); +} diff --git a/test-templates/src/fields.rs b/test-templates/src/fields.rs index db0d9b61c..7003dcc06 100644 --- a/test-templates/src/fields.rs +++ b/test-templates/src/fields.rs @@ -586,3 +586,98 @@ macro_rules! test_field { } }; } + +#[macro_export] +#[doc(hidden)] +macro_rules! __test_small_field { + ($field: ty) => { + // Common field tests plus FFT tests + $crate::__test_field!($field; fft); + + // Constants test for prime field as above + #[test] + fn test_constants() { + use ark_ff::{FpConfig, BigInteger, SqrtPrecomputation}; + use $crate::num_bigint::BigUint; + use $crate::num_integer::Integer; + + let modulus: BigUint = <$field>::MODULUS.into(); + let modulus_minus_one = &modulus - 1u8; + assert_eq!(BigUint::from(<$field>::MODULUS_MINUS_ONE_DIV_TWO), &modulus_minus_one / 2u32); + assert_eq!(<$field>::MODULUS_BIT_SIZE as u64, modulus.bits()); + if let Some(SqrtPrecomputation::Case3Mod4 { modulus_plus_one_div_four }) = <$field>::SQRT_PRECOMP { + // Handle the case where `(MODULUS + 1) / 4` + // has fewer limbs than `MODULUS`. + let check = ((&modulus + 1u8) / 4u8).to_u64_digits(); + let len = check.len(); + assert_eq!(&modulus_plus_one_div_four[..len], &check); + assert!(modulus_plus_one_div_four[len..].iter().all(Zero::is_zero)); + } + + let mut two_adicity = 0; + let mut trace = modulus_minus_one; + while trace.is_even() { + trace /= 2u8; + two_adicity += 1; + } + assert_eq!(two_adicity, <$field>::TWO_ADICITY); + assert_eq!(BigUint::from(<$field>::TRACE), trace); + let trace_minus_one_div_two = (&trace - 1u8) / 2u8; + assert_eq!(BigUint::from(<$field>::TRACE_MINUS_ONE_DIV_TWO), trace_minus_one_div_two); + + let two_adic_root_of_unity: BigUint = <$field>::TWO_ADIC_ROOT_OF_UNITY.into(); + let generator: BigUint = <$field>::GENERATOR.into_bigint().into(); + assert_eq!(two_adic_root_of_unity, generator.modpow(&trace, &modulus)); + match (<$field>::SMALL_SUBGROUP_BASE, <$field>::SMALL_SUBGROUP_BASE_ADICITY) { + (Some(base), Some(adicity)) => { + let mut e = generator; + for _i in 0..adicity { + e = e.modpow(&base.into(), &modulus) + } + }, + (None, None) => {}, + (_, _) => { + panic!("Should specify both `SMALL_SUBGROUP_BASE` and `SMALL_SUBGROUP_BASE_ADICITY`") + }, + } + } + }; +} + +/// This macro includes most tests from `test_field!` but excludes: +// `test_montgomery_config`: small fields do not extend MontConfig +// `test_sum_of_products_edge_case`: cases assume use of BigInts +#[macro_export] +macro_rules! test_small_field { + ($mod_name:ident; $field:ty) => { + mod $mod_name { + use super::*; + use ark_ff::{ + fields::{Field, LegendreSymbol}, + FftField, PrimeField, SmallFp, SmallFpConfig, + }; + use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; + use ark_std::{ + io::Cursor, rand::Rng, rand::RngCore, test_rng, vec::Vec, One, UniformRand, Zero, + }; + const ITERATIONS: usize = 1000; + + $crate::__test_small_field!($field); + } + }; + + ($iters:expr; $mod_name:ident; $field:ty) => { + mod $mod_name { + use super::*; + use ark_ff::{ + fields::{Field, LegendreSymbol}, + FftField, SmallFp, SmallFpConfig, + }; + use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; + use ark_std::{rand::Rng, rand::RngCore, test_rng, vec::Vec, One, UniformRand, Zero}; + const ITERATIONS: usize = $iters; + + $crate::__test_small_field!($field); + } + }; +}