From 0f0228bf45a94267921b4f6867921b5797b75b0d Mon Sep 17 00:00:00 2001 From: Joaquin Carletti Date: Fri, 22 Nov 2024 13:24:05 -0300 Subject: [PATCH 01/13] wip --- math/src/field/fields/fft_friendly/mod.rs | 4 +- .../fields/fft_friendly/quartic_babybear.rs | 195 ++++++++++++++++++ 2 files changed, 198 insertions(+), 1 deletion(-) create mode 100644 math/src/field/fields/fft_friendly/quartic_babybear.rs diff --git a/math/src/field/fields/fft_friendly/mod.rs b/math/src/field/fields/fft_friendly/mod.rs index 535b94ecc..2b370caed 100644 --- a/math/src/field/fields/fft_friendly/mod.rs +++ b/math/src/field/fields/fft_friendly/mod.rs @@ -2,6 +2,8 @@ pub mod babybear; /// Implemenation of the quadratic extension of the babybear field pub mod quadratic_babybear; +/// Implemenation of the quadric extension of the babybear field +pub mod quartic_babybear; /// Implementation of the prime field used in [Stark101](https://starkware.co/stark-101/) tutorial, p = 3 * 2^30 + 1 pub mod stark_101_prime_field; /// Implementation of two-adic prime field over 256 bit unsigned integers. @@ -9,4 +11,4 @@ pub mod stark_252_prime_field; /// Implemenation of the Goldilocks Prime Field p = 2^64 - 2^32 + 1 pub mod u64_goldilocks; /// Implemenation of the Mersenne Prime field p = 2^31 - 1 -pub mod u64_mersenne_montgomery_field; +pub mod u64_mersenne_montgomery_field; \ No newline at end of file diff --git a/math/src/field/fields/fft_friendly/quartic_babybear.rs b/math/src/field/fields/fft_friendly/quartic_babybear.rs new file mode 100644 index 000000000..f53fa6c4d --- /dev/null +++ b/math/src/field/fields/fft_friendly/quartic_babybear.rs @@ -0,0 +1,195 @@ +use crate::field::{ + element::FieldElement, + fields::fft_friendly::babybear::Babybear31PrimeField, traits::IsField, +}; + +pub const BETA: FieldElement = FieldElement::::from(11); + +/// +#[derive(Clone, Debug)] +pub struct Degree4BabyBearExtensionField; + +impl IsField for Degree4BabyBearExtensionField { + type BaseType = [FieldElement; 4]; + + /// Returns the component wise addition of `a` and `b` + fn add(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { + [&a[0] + &b[0], &a[1] + &b[1], &a[2] + &b[2], &a[3] + &b[3] ] + } + + /// Returns the multiplication of `a` and `b` using the following + /// equation: + + fn mul(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { + [a[0] * b[0] - BETA * (a[1] * b[3] + a[3] * b[1] + a[2] * b[2]), + a[0] * b[1] + a[1] * b[0] - BETA * (a[2] * b[3] + a[3] * b[2]), + a[0] * b[2] + a[2] * b[0] + a[1] * b[1] - BETA * (a[3] * b[3]), + a[0] * b[3] + a[3] * b[0] + a[1] * b[2] + a[2] * b[1], + ] + } + + fn square(a: &Self::BaseType) -> Self::BaseType { + [a[0].square() - BETA * (2 * (a[1] * a[3]) + a[2].square()), + 2 * (a[0] * a[1] - BETA * (a[2] * a[3])), + 2 * (a[0] * a[2]) + a[1].square() - BETA * (a[3].square()), + 2 * (a[0] * a[3] + a[1] * a[2]), + ] + } + /// Returns the component wise subtraction of `a` and `b` + fn sub(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { + [&a[0] - &b[0], &a[1] - &b[1],&a[2] - &b[2], &a[3] - &b[3]] + } + + /// Returns the component wise negation of `a` + fn neg(a: &Self::BaseType) -> Self::BaseType { + [-&a[0], -&a[1], &a[2], -&a[3]] + } + + /// Returns the multiplicative inverse of `a` + /// This uses the equality `(a0 + a1 * u) * (a0 - a1 * u) = a0.pow(2) - a1.pow(2) * residue()` + fn inv(a: &Self::BaseType) -> Result { + + + + let mut b0 = a[0] * a[0] + BETA * (a[1] * (a[3] + a[3]) - a[2] * a[2]); + let mut b2 = a[0] * (a[2] + a[2]) - a[1] * a[1] + BETA * (a[3] * a[3]); + let c = b0 * b0 + BETA * b2 * b2; + let c_inv = c.inv(); + + b0 *= c_inv; + b2 *= c_inv; + + ExtField ([ + a[0] * b0 + BETA * a[2] * b2, + -a[1] * b0 + NBETA * a[3] * b2, + -a[0] * b2 + a[2] * b0, + a[1] * b2 - a[3] * b0, + ]) + //let inv_norm = (a[0].square() + a[1].square()).inv()?; + //Ok([&a[0] * &inv_norm, -&a[1] * inv_norm]) + } + + /// Returns the division of `a` and `b` + fn div(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { + ::mul(a, &Self::inv(b).unwrap()) + } + + /// Returns a boolean indicating whether `a` and `b` are equal component wise. + fn eq(a: &Self::BaseType, b: &Self::BaseType) -> bool { + a[0] == b[0] && a[1] == b[1] && a[2] == b[2] && a[3] == b[3] + } + + /// Returns the additive neutral element of the field extension. + fn zero() -> Self::BaseType { + [FieldElement::zero(), FieldElement::zero(), FieldElement::zero(), FieldElement::zero()] + } + + /// Returns the multiplicative neutral element of the field extension. + fn one() -> Self::BaseType { + [FieldElement::one(), FieldElement::zero(), FieldElement::zero(), FieldElement::zero()] + } + + /// Returns the element `x * 1` where 1 is the multiplicative neutral element. + fn from_u64(x: u64) -> Self::BaseType { + [FieldElement::from(x), FieldElement::zero(), FieldElement::zero(), FieldElement::zero()] + } + + /// Takes as input an element of BaseType and returns the internal representation + /// of that element in the field. + /// Note: for this case this is simply the identity, because the components + /// already have correct representations. + fn from_base_type(x: Self::BaseType) -> Self::BaseType { + x + } + + fn double(a: &Self::BaseType) -> Self::BaseType { + Self::add(a, a) + } + + fn pow(a: &Self::BaseType, mut exponent: T) -> Self::BaseType + where + T: crate::unsigned_integer::traits::IsUnsignedInteger, + { + let zero = T::from(0); + let one = T::from(1); + + if exponent == zero { + Self::one() + } else if exponent == one { + a.clone() + } else { + let mut result = a.clone(); + + while exponent & one == zero { + result = Self::square(&result); + exponent >>= 1; + } + + if exponent == zero { + result + } else { + let mut base = result.clone(); + exponent >>= 1; + + while exponent != zero { + base = Self::square(&base); + if exponent & one == one { + result = Self::mul(&result, &base); + } + exponent >>= 1; + } + + result + } + } + } +} + +impl IsSubFieldOf for Babybear31PrimeField { + fn mul( + a: &Self::BaseType, + b: &::BaseType, + ) -> ::BaseType { + let c0 = FieldElement::from_raw(::mul(a, b[0].value())); + let c1 = FieldElement::from_raw(::mul(a, b[1].value())); + [c0, c1] + } + + fn add( + a: &Self::BaseType, + b: &::BaseType, + ) -> ::BaseType { + let c0 = FieldElement::from_raw(::add(a, b[0].value())); + let c1 = FieldElement::from_raw(*b[1].value()); + [c0, c1] + } + + fn div( + a: &Self::BaseType, + b: &::BaseType, + ) -> ::BaseType { + let b_inv = Degree2ExtensionField::inv(b).unwrap(); + >::mul(a, &b_inv) + } + + fn sub( + a: &Self::BaseType, + b: &::BaseType, + ) -> ::BaseType { + let c0 = FieldElement::from_raw(::sub(a, b[0].value())); + let c1 = FieldElement::from_raw(::neg(b[1].value())); + [c0, c1] + } + + fn embed(a: Self::BaseType) -> ::BaseType { + [FieldElement::from_raw(a), FieldElement::zero()] + } + + #[cfg(feature = "alloc")] + fn to_subfield_vec( + b: ::BaseType, + ) -> alloc::vec::Vec { + b.into_iter().map(|x| x.to_raw()).collect() + } +} + From 44ab11a670f1591fcc8c588ebeb528b8643d6d49 Mon Sep 17 00:00:00 2001 From: Nicole Date: Fri, 22 Nov 2024 15:04:48 -0300 Subject: [PATCH 02/13] add byte conversion for quartic --- .../fields/fft_friendly/quartic_babybear.rs | 109 ++++++++++++------ 1 file changed, 75 insertions(+), 34 deletions(-) diff --git a/math/src/field/fields/fft_friendly/quartic_babybear.rs b/math/src/field/fields/fft_friendly/quartic_babybear.rs index f53fa6c4d..5e60a4b44 100644 --- a/math/src/field/fields/fft_friendly/quartic_babybear.rs +++ b/math/src/field/fields/fft_friendly/quartic_babybear.rs @@ -1,43 +1,46 @@ -use crate::field::{ - element::FieldElement, - fields::fft_friendly::babybear::Babybear31PrimeField, traits::IsField, +use crate::{ + field::{ + element::FieldElement, fields::fft_friendly::babybear::Babybear31PrimeField, + traits::IsField, + }, + traits::ByteConversion, }; pub const BETA: FieldElement = FieldElement::::from(11); -/// #[derive(Clone, Debug)] pub struct Degree4BabyBearExtensionField; impl IsField for Degree4BabyBearExtensionField { type BaseType = [FieldElement; 4]; - /// Returns the component wise addition of `a` and `b` fn add(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { - [&a[0] + &b[0], &a[1] + &b[1], &a[2] + &b[2], &a[3] + &b[3] ] + [&a[0] + &b[0], &a[1] + &b[1], &a[2] + &b[2], &a[3] + &b[3]] } - /// Returns the multiplication of `a` and `b` using the following - /// equation: - + // Result of multiplying two polynomials a = a0 + a1 * x + a2 * x^2 + a3 * x^3 and + // b = b0 + b1 * x + b2 * x^2 + b3 * x^3 by applying distribution and taking + // the remainder of the division by x^4 + 11. fn mul(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { - [a[0] * b[0] - BETA * (a[1] * b[3] + a[3] * b[1] + a[2] * b[2]), - a[0] * b[1] + a[1] * b[0] - BETA * (a[2] * b[3] + a[3] * b[2]), - a[0] * b[2] + a[2] * b[0] + a[1] * b[1] - BETA * (a[3] * b[3]), - a[0] * b[3] + a[3] * b[0] + a[1] * b[2] + a[2] * b[1], + [ + a[0] * b[0] - BETA * (a[1] * b[3] + a[3] * b[1] + a[2] * b[2]), + a[0] * b[1] + a[1] * b[0] - BETA * (a[2] * b[3] + a[3] * b[2]), + a[0] * b[2] + a[2] * b[0] + a[1] * b[1] - BETA * (a[3] * b[3]), + a[0] * b[3] + a[3] * b[0] + a[1] * b[2] + a[2] * b[1], ] } fn square(a: &Self::BaseType) -> Self::BaseType { - [a[0].square() - BETA * (2 * (a[1] * a[3]) + a[2].square()), - 2 * (a[0] * a[1] - BETA * (a[2] * a[3])), - 2 * (a[0] * a[2]) + a[1].square() - BETA * (a[3].square()), - 2 * (a[0] * a[3] + a[1] * a[2]), + [ + a[0].square() - BETA * (2 * (a[1] * a[3]) + a[2].square()), + 2 * (a[0] * a[1] - BETA * (a[2] * a[3])), + 2 * (a[0] * a[2]) + a[1].square() - BETA * (a[3].square()), + 2 * (a[0] * a[3] + a[1] * a[2]), ] } /// Returns the component wise subtraction of `a` and `b` fn sub(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { - [&a[0] - &b[0], &a[1] - &b[1],&a[2] - &b[2], &a[3] - &b[3]] + [&a[0] - &b[0], &a[1] - &b[1], &a[2] - &b[2], &a[3] - &b[3]] } /// Returns the component wise negation of `a` @@ -48,18 +51,15 @@ impl IsField for Degree4BabyBearExtensionField { /// Returns the multiplicative inverse of `a` /// This uses the equality `(a0 + a1 * u) * (a0 - a1 * u) = a0.pow(2) - a1.pow(2) * residue()` fn inv(a: &Self::BaseType) -> Result { - - - let mut b0 = a[0] * a[0] + BETA * (a[1] * (a[3] + a[3]) - a[2] * a[2]); let mut b2 = a[0] * (a[2] + a[2]) - a[1] * a[1] + BETA * (a[3] * a[3]); let c = b0 * b0 + BETA * b2 * b2; let c_inv = c.inv(); - + b0 *= c_inv; b2 *= c_inv; - - ExtField ([ + + ExtField([ a[0] * b0 + BETA * a[2] * b2, -a[1] * b0 + NBETA * a[3] * b2, -a[0] * b2 + a[2] * b0, @@ -81,17 +81,32 @@ impl IsField for Degree4BabyBearExtensionField { /// Returns the additive neutral element of the field extension. fn zero() -> Self::BaseType { - [FieldElement::zero(), FieldElement::zero(), FieldElement::zero(), FieldElement::zero()] + [ + FieldElement::zero(), + FieldElement::zero(), + FieldElement::zero(), + FieldElement::zero(), + ] } /// Returns the multiplicative neutral element of the field extension. fn one() -> Self::BaseType { - [FieldElement::one(), FieldElement::zero(), FieldElement::zero(), FieldElement::zero()] + [ + FieldElement::one(), + FieldElement::zero(), + FieldElement::zero(), + FieldElement::zero(), + ] } /// Returns the element `x * 1` where 1 is the multiplicative neutral element. fn from_u64(x: u64) -> Self::BaseType { - [FieldElement::from(x), FieldElement::zero(), FieldElement::zero(), FieldElement::zero()] + [ + FieldElement::from(x), + FieldElement::zero(), + FieldElement::zero(), + FieldElement::zero(), + ] } /// Takes as input an element of BaseType and returns the internal representation @@ -101,36 +116,36 @@ impl IsField for Degree4BabyBearExtensionField { fn from_base_type(x: Self::BaseType) -> Self::BaseType { x } - + fn double(a: &Self::BaseType) -> Self::BaseType { Self::add(a, a) } - + fn pow(a: &Self::BaseType, mut exponent: T) -> Self::BaseType where T: crate::unsigned_integer::traits::IsUnsignedInteger, { let zero = T::from(0); let one = T::from(1); - + if exponent == zero { Self::one() } else if exponent == one { a.clone() } else { let mut result = a.clone(); - + while exponent & one == zero { result = Self::square(&result); exponent >>= 1; } - + if exponent == zero { result } else { let mut base = result.clone(); exponent >>= 1; - + while exponent != zero { base = Self::square(&base); if exponent & one == one { @@ -138,7 +153,7 @@ impl IsField for Degree4BabyBearExtensionField { } exponent >>= 1; } - + result } } @@ -193,3 +208,29 @@ impl IsSubFieldOf for Babybear31PrimeField { } } +#[cfg(feature = "lambdaworks-serde-binary")] +impl ByteConversion for [FieldElement; 4] { + #[cfg(feature = "alloc")] + fn to_bytes_be(&self) -> alloc::vec::Vec { + unimplemented!() + } + + #[cfg(feature = "alloc")] + fn to_bytes_le(&self) -> alloc::vec::Vec { + unimplemented!() + } + + fn from_bytes_be(_bytes: &[u8]) -> Result + where + Self: Sized, + { + unimplemented!() + } + + fn from_bytes_le(_bytes: &[u8]) -> Result + where + Self: Sized, + { + unimplemented!() + } +} From 24ebe36330c2326c2db6cf1e322c2c2981761657 Mon Sep 17 00:00:00 2001 From: Nicole Date: Fri, 22 Nov 2024 18:42:22 -0300 Subject: [PATCH 03/13] fft tests for baby bear quartic extension working --- .../fields/fft_friendly/quartic_babybear.rs | 355 +++++++++++++++--- 1 file changed, 309 insertions(+), 46 deletions(-) diff --git a/math/src/field/fields/fft_friendly/quartic_babybear.rs b/math/src/field/fields/fft_friendly/quartic_babybear.rs index 5e60a4b44..32e742894 100644 --- a/math/src/field/fields/fft_friendly/quartic_babybear.rs +++ b/math/src/field/fields/fft_friendly/quartic_babybear.rs @@ -1,12 +1,18 @@ use crate::{ field::{ - element::FieldElement, fields::fft_friendly::babybear::Babybear31PrimeField, - traits::IsField, + element::FieldElement, + errors::FieldError, + fields::fft_friendly::babybear::Babybear31PrimeField, + traits::{IsFFTField, IsField, IsSubFieldOf}, }, traits::ByteConversion, }; -pub const BETA: FieldElement = FieldElement::::from(11); +// BETA = 11 +// -BETA = -11 is the non-residue. +// We are implementig the extension of Baby Bear of degree 4 using the irreducible polynomial x^4 + 11. +pub const BETA: FieldElement = + FieldElement::::from_hex_unchecked("b"); #[derive(Clone, Debug)] pub struct Degree4BabyBearExtensionField; @@ -23,19 +29,19 @@ impl IsField for Degree4BabyBearExtensionField { // the remainder of the division by x^4 + 11. fn mul(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { [ - a[0] * b[0] - BETA * (a[1] * b[3] + a[3] * b[1] + a[2] * b[2]), - a[0] * b[1] + a[1] * b[0] - BETA * (a[2] * b[3] + a[3] * b[2]), - a[0] * b[2] + a[2] * b[0] + a[1] * b[1] - BETA * (a[3] * b[3]), - a[0] * b[3] + a[3] * b[0] + a[1] * b[2] + a[2] * b[1], + &a[0] * &b[0] - BETA * (&a[1] * &b[3] + &a[3] * &b[1] + &a[2] * &b[2]), + &a[0] * &b[1] + &a[1] * &b[0] - BETA * (&a[2] * &b[3] + &a[3] * &b[2]), + &a[0] * &b[2] + &a[2] * &b[0] + &a[1] * &b[1] - BETA * (&a[3] * &b[3]), + &a[0] * &b[3] + &a[3] * &b[0] + &a[1] * &b[2] + &a[2] * &b[1], ] } fn square(a: &Self::BaseType) -> Self::BaseType { [ - a[0].square() - BETA * (2 * (a[1] * a[3]) + a[2].square()), - 2 * (a[0] * a[1] - BETA * (a[2] * a[3])), - 2 * (a[0] * a[2]) + a[1].square() - BETA * (a[3].square()), - 2 * (a[0] * a[3] + a[1] * a[2]), + &a[0].square() - BETA * ((&a[1] * &a[3]).double() + &a[2].square()), + (&a[0] * &a[1] - BETA * (&a[2] * &a[3])).double(), + (&a[0] * &a[2]).double() + &a[1].square() - BETA * (&a[3].square()), + (&a[0] * &a[3] + &a[1] * &a[2]).double(), ] } /// Returns the component wise subtraction of `a` and `b` @@ -45,28 +51,24 @@ impl IsField for Degree4BabyBearExtensionField { /// Returns the component wise negation of `a` fn neg(a: &Self::BaseType) -> Self::BaseType { - [-&a[0], -&a[1], &a[2], -&a[3]] + [-&a[0], -&a[1], -&a[2], -&a[3]] } /// Returns the multiplicative inverse of `a` - /// This uses the equality `(a0 + a1 * u) * (a0 - a1 * u) = a0.pow(2) - a1.pow(2) * residue()` + /// fn inv(a: &Self::BaseType) -> Result { - let mut b0 = a[0] * a[0] + BETA * (a[1] * (a[3] + a[3]) - a[2] * a[2]); - let mut b2 = a[0] * (a[2] + a[2]) - a[1] * a[1] + BETA * (a[3] * a[3]); - let c = b0 * b0 + BETA * b2 * b2; - let c_inv = c.inv(); - - b0 *= c_inv; - b2 *= c_inv; - - ExtField([ - a[0] * b0 + BETA * a[2] * b2, - -a[1] * b0 + NBETA * a[3] * b2, - -a[0] * b2 + a[2] * b0, - a[1] * b2 - a[3] * b0, + let mut b0 = &a[0] * &a[0] + BETA * (&a[1] * (&a[3] + &a[3]) - &a[2] * &a[2]); + let mut b2 = &a[0] * (&a[2] + &a[2]) - &a[1] * &a[1] + BETA * (&a[3] * &a[3]); + let c = &b0.square() + BETA * b2.square(); + let c_inv = c.inv()?; + b0 = b0 * &c_inv; + b2 = b2 * &c_inv; + Ok([ + &a[0] * &b0 + BETA * &a[2] * &b2, + -&a[1] * &b0 - BETA * &a[3] * &b2, + -&a[0] * &b2 + &a[2] * &b0, + &a[1] * &b2 - &a[3] * &b0, ]) - //let inv_norm = (a[0].square() + a[1].square()).inv()?; - //Ok([&a[0] * &inv_norm, -&a[1] * inv_norm]) } /// Returns the division of `a` and `b` @@ -118,7 +120,7 @@ impl IsField for Degree4BabyBearExtensionField { } fn double(a: &Self::BaseType) -> Self::BaseType { - Self::add(a, a) + ::add(a, a) } fn pow(a: &Self::BaseType, mut exponent: T) -> Self::BaseType @@ -149,7 +151,7 @@ impl IsField for Degree4BabyBearExtensionField { while exponent != zero { base = Self::square(&base); if exponent & one == one { - result = Self::mul(&result, &base); + result = ::mul(&result, &base); } exponent >>= 1; } @@ -163,46 +165,59 @@ impl IsField for Degree4BabyBearExtensionField { impl IsSubFieldOf for Babybear31PrimeField { fn mul( a: &Self::BaseType, - b: &::BaseType, - ) -> ::BaseType { + b: &::BaseType, + ) -> ::BaseType { let c0 = FieldElement::from_raw(::mul(a, b[0].value())); let c1 = FieldElement::from_raw(::mul(a, b[1].value())); - [c0, c1] + let c2 = FieldElement::from_raw(::mul(a, b[2].value())); + let c3 = FieldElement::from_raw(::mul(a, b[3].value())); + + [c0, c1, c2, c3] } fn add( a: &Self::BaseType, - b: &::BaseType, - ) -> ::BaseType { + b: &::BaseType, + ) -> ::BaseType { let c0 = FieldElement::from_raw(::add(a, b[0].value())); let c1 = FieldElement::from_raw(*b[1].value()); - [c0, c1] + let c2 = FieldElement::from_raw(*b[2].value()); + let c3 = FieldElement::from_raw(*b[3].value()); + + [c0, c1, c2, c3] } fn div( a: &Self::BaseType, - b: &::BaseType, - ) -> ::BaseType { - let b_inv = Degree2ExtensionField::inv(b).unwrap(); - >::mul(a, &b_inv) + b: &::BaseType, + ) -> ::BaseType { + let b_inv = Degree4BabyBearExtensionField::inv(b).unwrap(); + >::mul(a, &b_inv) } fn sub( a: &Self::BaseType, - b: &::BaseType, - ) -> ::BaseType { + b: &::BaseType, + ) -> ::BaseType { let c0 = FieldElement::from_raw(::sub(a, b[0].value())); let c1 = FieldElement::from_raw(::neg(b[1].value())); - [c0, c1] + let c2 = FieldElement::from_raw(::neg(b[2].value())); + let c3 = FieldElement::from_raw(::neg(b[3].value())); + [c0, c1, c2, c3] } - fn embed(a: Self::BaseType) -> ::BaseType { - [FieldElement::from_raw(a), FieldElement::zero()] + fn embed(a: Self::BaseType) -> ::BaseType { + [ + FieldElement::from_raw(a), + FieldElement::zero(), + FieldElement::zero(), + FieldElement::zero(), + ] } #[cfg(feature = "alloc")] fn to_subfield_vec( - b: ::BaseType, + b: ::BaseType, ) -> alloc::vec::Vec { b.into_iter().map(|x| x.to_raw()).collect() } @@ -234,3 +249,251 @@ impl ByteConversion for [FieldElement; 4] { unimplemented!() } } + +impl IsFFTField for Degree4BabyBearExtensionField { + const TWO_ADICITY: u64 = 29; + const TWO_ADIC_PRIMITVE_ROOT_OF_UNITY: Self::BaseType = [ + FieldElement::from_hex_unchecked("0"), + FieldElement::from_hex_unchecked("0"), + FieldElement::from_hex_unchecked("0"), + FieldElement::from_hex_unchecked("771F1C8"), + ]; +} + +#[cfg(test)] +mod tests { + + use crate::{ + fft::cpu::roots_of_unity::{ + get_powers_of_primitive_root, get_powers_of_primitive_root_coset, + }, + field::traits::RootsConfig, + polynomial::Polynomial, + }; + + use super::*; + + type FpE = FieldElement; + type Fp4E = FieldElement; + + #[test] + fn test_add() { + let a = Fp4E::new([FpE::from(0), FpE::from(1), FpE::from(2), FpE::from(3)]); + let b = Fp4E::new([-FpE::from(2), FpE::from(4), FpE::from(6), -FpE::from(8)]); + let expected_result = Fp4E::new([ + FpE::from(0) - FpE::from(2), + FpE::from(1) + FpE::from(4), + FpE::from(2) + FpE::from(6), + FpE::from(3) - FpE::from(8), + ]); + assert_eq!(a + b, expected_result); + } + + #[test] + fn test_sub() { + let a = Fp4E::new([FpE::from(0), FpE::from(1), FpE::from(2), FpE::from(3)]); + let b = Fp4E::new([-FpE::from(2), FpE::from(4), FpE::from(6), -FpE::from(8)]); + let expected_result = Fp4E::new([ + FpE::from(0) + FpE::from(2), + FpE::from(1) - FpE::from(4), + FpE::from(2) - FpE::from(6), + FpE::from(3) + FpE::from(8), + ]); + assert_eq!(a - b, expected_result); + } + + #[test] + fn test_mul_by_0() { + let a = Fp4E::new([FpE::from(4), FpE::from(1), FpE::from(2), FpE::from(3)]); + let b = Fp4E::new([FpE::zero(), FpE::zero(), FpE::zero(), FpE::zero()]); + assert_eq!(&a * &b, b); + } + + #[test] + fn test_mul_by_1() { + let a = Fp4E::new([FpE::from(4), FpE::from(1), FpE::from(2), FpE::from(3)]); + let b = Fp4E::new([FpE::one(), FpE::zero(), FpE::zero(), FpE::zero()]); + assert_eq!(&a * b, a); + } + + #[test] + fn test_mul() { + let a = Fp4E::new([FpE::from(0), FpE::from(1), FpE::from(2), FpE::from(3)]); + let b = Fp4E::new([FpE::from(2), FpE::from(4), FpE::from(6), FpE::from(8)]); + let expected_result = Fp4E::new([ + -FpE::from(352), + -FpE::from(372), + -FpE::from(256), + FpE::from(20), + ]); + assert_eq!(a * b, expected_result); + } + + #[test] + fn test_pow() { + let a = Fp4E::new([FpE::from(0), FpE::from(1), FpE::from(2), FpE::from(3)]); + let expected_result = &a * &a * &a; + assert_eq!(a.pow(3u64), expected_result); + } + + #[test] + fn test_inv_of_one_is_one() { + let a = Fp4E::one(); + assert_eq!(a.inv().unwrap(), a); + } + + #[test] + fn test_mul_by_inv_is_identity() { + let a = Fp4E::from(123456); + assert_eq!(&a * a.inv().unwrap(), Fp4E::one()); + } + + #[test] + fn test_mul_as_subfield() { + let a = FpE::from(2); + let b = Fp4E::new([FpE::from(2), FpE::from(4), FpE::from(6), FpE::from(8)]); + let expected_result = Fp4E::new([ + FpE::from(2) * FpE::from(2), + FpE::from(4) * FpE::from(2), + FpE::from(6) * FpE::from(2), + FpE::from(8) * FpE::from(2), + ]); + assert_eq!(a * b, expected_result); + } + + #[test] + fn test_double_equals_sum_two_times() { + let a = Fp4E::new([FpE::from(2), FpE::from(4), FpE::from(6), FpE::from(8)]); + + assert_eq!(a.double(), &a + &a); + } + + #[test] + fn test_mul_group_generator_pow_order_is_one() { + let generator = Fp4E::new([FpE::from(8), FpE::from(1), FpE::zero(), FpE::zero()]); + let extension_order: u128 = 78000001_u128.pow(4); + assert_eq!(generator.pow(extension_order), generator); + } + + #[test] + fn test_two_adic_primitve_root_of_unity() { + let generator = Fp4E::new(Degree4BabyBearExtensionField::TWO_ADIC_PRIMITVE_ROOT_OF_UNITY); + assert_eq!( + generator.pow(2u64.pow(Degree4BabyBearExtensionField::TWO_ADICITY as u32)), + Fp4E::one() + ); + } + + #[test] + fn test_fft() { + let c0 = Fp4E::new([FpE::from(1), FpE::from(2), FpE::from(3), FpE::from(4)]); + let c1 = Fp4E::new([FpE::from(2), FpE::from(3), FpE::from(4), FpE::from(5)]); + let c2 = Fp4E::new([FpE::from(3), FpE::from(4), FpE::from(5), FpE::from(6)]); + let c3 = Fp4E::new([FpE::from(4), FpE::from(5), FpE::from(6), FpE::from(7)]); + let c4 = Fp4E::new([FpE::from(5), FpE::from(6), FpE::from(7), FpE::from(8)]); + let c5 = Fp4E::new([FpE::from(6), FpE::from(7), FpE::from(8), FpE::from(9)]); + let c6 = Fp4E::new([FpE::from(7), FpE::from(8), FpE::from(9), FpE::from(0)]); + let c7 = Fp4E::new([FpE::from(8), FpE::from(9), FpE::from(0), FpE::from(1)]); + + let poly = Polynomial::new(&[c0, c1, c2, c3, c4, c5, c6, c7]); + let evaluations = + Polynomial::evaluate_fft::(&poly, 1, None).unwrap(); + let poly_interpol = + Polynomial::interpolate_fft::(&evaluations).unwrap(); + + assert_eq!(poly, poly_interpol) + } + + #[test] + fn test_fft_and_naive_evaluation() { + let c0 = Fp4E::new([FpE::from(1), FpE::from(2), FpE::from(3), FpE::from(4)]); + let c1 = Fp4E::new([FpE::from(2), FpE::from(3), FpE::from(4), FpE::from(5)]); + let c2 = Fp4E::new([FpE::from(3), FpE::from(4), FpE::from(5), FpE::from(6)]); + let c3 = Fp4E::new([FpE::from(4), FpE::from(5), FpE::from(6), FpE::from(7)]); + let c4 = Fp4E::new([FpE::from(5), FpE::from(6), FpE::from(7), FpE::from(8)]); + let c5 = Fp4E::new([FpE::from(6), FpE::from(7), FpE::from(8), FpE::from(9)]); + let c6 = Fp4E::new([FpE::from(7), FpE::from(8), FpE::from(9), FpE::from(0)]); + let c7 = Fp4E::new([FpE::from(8), FpE::from(9), FpE::from(0), FpE::from(1)]); + + let poly = Polynomial::new(&[c0, c1, c2, c3, c4, c5, c6, c7]); + + let len = poly.coeff_len().next_power_of_two(); + let order = len.trailing_zeros(); + let twiddles = + get_powers_of_primitive_root(order.into(), len, RootsConfig::Natural).unwrap(); + + let fft_eval = + Polynomial::evaluate_fft::(&poly, 1, None).unwrap(); + let naive_eval = poly.evaluate_slice(&twiddles); + + assert_eq!(fft_eval, naive_eval); + } + + #[test] + fn gen_fft_coset_and_naive_evaluation() { + let c0 = Fp4E::new([FpE::from(1), FpE::from(2), FpE::from(3), FpE::from(4)]); + let c1 = Fp4E::new([FpE::from(2), FpE::from(3), FpE::from(4), FpE::from(5)]); + let c2 = Fp4E::new([FpE::from(3), FpE::from(4), FpE::from(5), FpE::from(6)]); + let c3 = Fp4E::new([FpE::from(4), FpE::from(5), FpE::from(6), FpE::from(7)]); + let c4 = Fp4E::new([FpE::from(5), FpE::from(6), FpE::from(7), FpE::from(8)]); + let c5 = Fp4E::new([FpE::from(6), FpE::from(7), FpE::from(8), FpE::from(9)]); + let c6 = Fp4E::new([FpE::from(7), FpE::from(8), FpE::from(9), FpE::from(0)]); + let c7 = Fp4E::new([FpE::from(8), FpE::from(9), FpE::from(0), FpE::from(1)]); + + let poly = Polynomial::new(&[c0, c1, c2, c3, c4, c5, c6, c7]); + + let offset = Fp4E::new([FpE::from(10), FpE::from(11), FpE::from(12), FpE::from(13)]); + let blowup_factor = 4; + + let len = poly.coeff_len().next_power_of_two(); + let order = (len * blowup_factor).trailing_zeros(); + let twiddles = + get_powers_of_primitive_root_coset(order.into(), len * blowup_factor, &offset).unwrap(); + + let fft_eval = Polynomial::evaluate_offset_fft::( + &poly, + blowup_factor, + None, + &offset, + ) + .unwrap(); + let naive_eval = poly.evaluate_slice(&twiddles); + + assert_eq!(fft_eval, naive_eval); + } + + #[test] + fn test_fft_and_naive_interpolate() { + let c0 = Fp4E::new([FpE::from(1), FpE::from(2), FpE::from(3), FpE::from(4)]); + let c1 = Fp4E::new([FpE::from(2), FpE::from(3), FpE::from(4), FpE::from(5)]); + let c2 = Fp4E::new([FpE::from(3), FpE::from(4), FpE::from(5), FpE::from(6)]); + let c3 = Fp4E::new([FpE::from(4), FpE::from(5), FpE::from(6), FpE::from(7)]); + let c4 = Fp4E::new([FpE::from(5), FpE::from(6), FpE::from(7), FpE::from(8)]); + let c5 = Fp4E::new([FpE::from(6), FpE::from(7), FpE::from(8), FpE::from(9)]); + let c6 = Fp4E::new([FpE::from(7), FpE::from(8), FpE::from(9), FpE::from(0)]); + let c7 = Fp4E::new([FpE::from(8), FpE::from(9), FpE::from(0), FpE::from(1)]); + + let fft_evals = [c0, c1, c2, c3, c4, c5, c6, c7]; + let order = fft_evals.len().trailing_zeros() as u64; + let twiddles: Vec> = + get_powers_of_primitive_root(order, 1 << order, RootsConfig::Natural).unwrap(); + + let naive_poly = Polynomial::interpolate(&twiddles, &fft_evals).unwrap(); + let fft_poly = + Polynomial::interpolate_fft::(&fft_evals).unwrap(); + + assert_eq!(fft_poly, naive_poly) + } + /* + #[test] + fn gen_fft_and_naive_coset_interpolate( + fft_evals: &[FieldElement], + ) -> (Polynomial>, Polynomial>) { + let order = fft_evals.len().trailing_zeros() as u64; + let twiddles = get_powers_of_primitive_root_coset(order, 1 << order, offset).unwrap(); + let offset = Fp4E::new([FpE::from(10), FpE::from(11), FpE::from(12), FpE::from(13)]); + + let naive_poly = Polynomial::interpolate(&twiddles, fft_evals).unwrap(); + let fft_poly = Polynomial::interpolate_offset_fft(fft_evals, &offset).unwrap(); + }*/ +} From 9ea854d35d7b428ab5eb0eb3980b59823f0c3d83 Mon Sep 17 00:00:00 2001 From: Joaquin Carletti Date: Mon, 25 Nov 2024 12:35:50 -0300 Subject: [PATCH 04/13] add test / add comments --- .../fields/fft_friendly/quartic_babybear.rs | 292 ++++++++++-------- 1 file changed, 170 insertions(+), 122 deletions(-) diff --git a/math/src/field/fields/fft_friendly/quartic_babybear.rs b/math/src/field/fields/fft_friendly/quartic_babybear.rs index 32e742894..82b88b1b1 100644 --- a/math/src/field/fields/fft_friendly/quartic_babybear.rs +++ b/math/src/field/fields/fft_friendly/quartic_babybear.rs @@ -44,18 +44,18 @@ impl IsField for Degree4BabyBearExtensionField { (&a[0] * &a[3] + &a[1] * &a[2]).double(), ] } - /// Returns the component wise subtraction of `a` and `b` + fn sub(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { [&a[0] - &b[0], &a[1] - &b[1], &a[2] - &b[2], &a[3] - &b[3]] } - /// Returns the component wise negation of `a` fn neg(a: &Self::BaseType) -> Self::BaseType { [-&a[0], -&a[1], -&a[2], -&a[3]] } - /// Returns the multiplicative inverse of `a` - /// + // Return te inverse of a fp4 element if exist. + // This algorithm is inspired by R1sc0 implementation: + // https://github.com/risc0/risc0/blob/4c41c739779ef2759a01ebcf808faf0fbffe8793/risc0/core/src/field/baby_bear.rs#L460 fn inv(a: &Self::BaseType) -> Result { let mut b0 = &a[0] * &a[0] + BETA * (&a[1] * (&a[3] + &a[3]) - &a[2] * &a[2]); let mut b2 = &a[0] * (&a[2] + &a[2]) - &a[1] * &a[1] + BETA * (&a[3] * &a[3]); @@ -71,17 +71,14 @@ impl IsField for Degree4BabyBearExtensionField { ]) } - /// Returns the division of `a` and `b` fn div(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { ::mul(a, &Self::inv(b).unwrap()) } - /// Returns a boolean indicating whether `a` and `b` are equal component wise. fn eq(a: &Self::BaseType, b: &Self::BaseType) -> bool { a[0] == b[0] && a[1] == b[1] && a[2] == b[2] && a[3] == b[3] } - /// Returns the additive neutral element of the field extension. fn zero() -> Self::BaseType { [ FieldElement::zero(), @@ -91,7 +88,6 @@ impl IsField for Degree4BabyBearExtensionField { ] } - /// Returns the multiplicative neutral element of the field extension. fn one() -> Self::BaseType { [ FieldElement::one(), @@ -101,7 +97,6 @@ impl IsField for Degree4BabyBearExtensionField { ] } - /// Returns the element `x * 1` where 1 is the multiplicative neutral element. fn from_u64(x: u64) -> Self::BaseType { [ FieldElement::from(x), @@ -262,15 +257,6 @@ impl IsFFTField for Degree4BabyBearExtensionField { #[cfg(test)] mod tests { - - use crate::{ - fft::cpu::roots_of_unity::{ - get_powers_of_primitive_root, get_powers_of_primitive_root_coset, - }, - field::traits::RootsConfig, - polynomial::Polynomial, - }; - use super::*; type FpE = FieldElement; @@ -371,7 +357,7 @@ mod tests { #[test] fn test_mul_group_generator_pow_order_is_one() { let generator = Fp4E::new([FpE::from(8), FpE::from(1), FpE::zero(), FpE::zero()]); - let extension_order: u128 = 78000001_u128.pow(4); + let extension_order: u128 = 2013265921_u128.pow(4); assert_eq!(generator.pow(extension_order), generator); } @@ -384,116 +370,178 @@ mod tests { ); } - #[test] - fn test_fft() { - let c0 = Fp4E::new([FpE::from(1), FpE::from(2), FpE::from(3), FpE::from(4)]); - let c1 = Fp4E::new([FpE::from(2), FpE::from(3), FpE::from(4), FpE::from(5)]); - let c2 = Fp4E::new([FpE::from(3), FpE::from(4), FpE::from(5), FpE::from(6)]); - let c3 = Fp4E::new([FpE::from(4), FpE::from(5), FpE::from(6), FpE::from(7)]); - let c4 = Fp4E::new([FpE::from(5), FpE::from(6), FpE::from(7), FpE::from(8)]); - let c5 = Fp4E::new([FpE::from(6), FpE::from(7), FpE::from(8), FpE::from(9)]); - let c6 = Fp4E::new([FpE::from(7), FpE::from(8), FpE::from(9), FpE::from(0)]); - let c7 = Fp4E::new([FpE::from(8), FpE::from(9), FpE::from(0), FpE::from(1)]); + #[cfg(all(feature = "std", not(feature = "instruments")))] + mod test_babybear_31_fft { + use super::*; + #[cfg(not(any(feature = "metal", feature = "cuda")))] + use crate::fft::cpu::roots_of_unity::{ + get_powers_of_primitive_root, get_powers_of_primitive_root_coset, + }; + #[cfg(not(any(feature = "metal", feature = "cuda")))] + use crate::field::element::FieldElement; + #[cfg(not(any(feature = "metal", feature = "cuda")))] + use crate::field::traits::{IsFFTField, RootsConfig}; + use crate::polynomial::Polynomial; + use proptest::{collection, prelude::*, std_facade::Vec}; + + #[cfg(not(any(feature = "metal", feature = "cuda")))] + fn gen_fft_and_naive_evaluation( + poly: Polynomial>, + ) -> (Vec>, Vec>) { + let len = poly.coeff_len().next_power_of_two(); + let order = len.trailing_zeros(); + let twiddles = + get_powers_of_primitive_root(order.into(), len, RootsConfig::Natural).unwrap(); + + let fft_eval = Polynomial::evaluate_fft::(&poly, 1, None).unwrap(); + let naive_eval = poly.evaluate_slice(&twiddles); + + (fft_eval, naive_eval) + } - let poly = Polynomial::new(&[c0, c1, c2, c3, c4, c5, c6, c7]); - let evaluations = - Polynomial::evaluate_fft::(&poly, 1, None).unwrap(); - let poly_interpol = - Polynomial::interpolate_fft::(&evaluations).unwrap(); + #[cfg(not(any(feature = "metal", feature = "cuda")))] + fn gen_fft_coset_and_naive_evaluation( + poly: Polynomial>, + offset: FieldElement, + blowup_factor: usize, + ) -> (Vec>, Vec>) { + let len = poly.coeff_len().next_power_of_two(); + let order = (len * blowup_factor).trailing_zeros(); + let twiddles = + get_powers_of_primitive_root_coset(order.into(), len * blowup_factor, &offset) + .unwrap(); + + let fft_eval = + Polynomial::evaluate_offset_fft::(&poly, blowup_factor, None, &offset).unwrap(); + let naive_eval = poly.evaluate_slice(&twiddles); + + (fft_eval, naive_eval) + } - assert_eq!(poly, poly_interpol) - } + #[cfg(not(any(feature = "metal", feature = "cuda")))] + fn gen_fft_and_naive_interpolate( + fft_evals: &[FieldElement], + ) -> (Polynomial>, Polynomial>) { + let order = fft_evals.len().trailing_zeros() as u64; + let twiddles = + get_powers_of_primitive_root(order, 1 << order, RootsConfig::Natural).unwrap(); - #[test] - fn test_fft_and_naive_evaluation() { - let c0 = Fp4E::new([FpE::from(1), FpE::from(2), FpE::from(3), FpE::from(4)]); - let c1 = Fp4E::new([FpE::from(2), FpE::from(3), FpE::from(4), FpE::from(5)]); - let c2 = Fp4E::new([FpE::from(3), FpE::from(4), FpE::from(5), FpE::from(6)]); - let c3 = Fp4E::new([FpE::from(4), FpE::from(5), FpE::from(6), FpE::from(7)]); - let c4 = Fp4E::new([FpE::from(5), FpE::from(6), FpE::from(7), FpE::from(8)]); - let c5 = Fp4E::new([FpE::from(6), FpE::from(7), FpE::from(8), FpE::from(9)]); - let c6 = Fp4E::new([FpE::from(7), FpE::from(8), FpE::from(9), FpE::from(0)]); - let c7 = Fp4E::new([FpE::from(8), FpE::from(9), FpE::from(0), FpE::from(1)]); + let naive_poly = Polynomial::interpolate(&twiddles, fft_evals).unwrap(); + let fft_poly = Polynomial::interpolate_fft::(fft_evals).unwrap(); + + (fft_poly, naive_poly) + } - let poly = Polynomial::new(&[c0, c1, c2, c3, c4, c5, c6, c7]); + #[cfg(not(any(feature = "metal", feature = "cuda")))] + fn gen_fft_and_naive_coset_interpolate( + fft_evals: &[FieldElement], + offset: &FieldElement, + ) -> (Polynomial>, Polynomial>) { + let order = fft_evals.len().trailing_zeros() as u64; + let twiddles = get_powers_of_primitive_root_coset(order, 1 << order, offset).unwrap(); - let len = poly.coeff_len().next_power_of_two(); - let order = len.trailing_zeros(); - let twiddles = - get_powers_of_primitive_root(order.into(), len, RootsConfig::Natural).unwrap(); + let naive_poly = Polynomial::interpolate(&twiddles, fft_evals).unwrap(); + let fft_poly = Polynomial::interpolate_offset_fft(fft_evals, offset).unwrap(); - let fft_eval = - Polynomial::evaluate_fft::(&poly, 1, None).unwrap(); - let naive_eval = poly.evaluate_slice(&twiddles); + (fft_poly, naive_poly) + } - assert_eq!(fft_eval, naive_eval); - } + #[cfg(not(any(feature = "metal", feature = "cuda")))] + fn gen_fft_interpolate_and_evaluate( + poly: Polynomial>, + ) -> (Polynomial>, Polynomial>) { + let eval = Polynomial::evaluate_fft::(&poly, 1, None).unwrap(); + let new_poly = Polynomial::interpolate_fft::(&eval).unwrap(); - #[test] - fn gen_fft_coset_and_naive_evaluation() { - let c0 = Fp4E::new([FpE::from(1), FpE::from(2), FpE::from(3), FpE::from(4)]); - let c1 = Fp4E::new([FpE::from(2), FpE::from(3), FpE::from(4), FpE::from(5)]); - let c2 = Fp4E::new([FpE::from(3), FpE::from(4), FpE::from(5), FpE::from(6)]); - let c3 = Fp4E::new([FpE::from(4), FpE::from(5), FpE::from(6), FpE::from(7)]); - let c4 = Fp4E::new([FpE::from(5), FpE::from(6), FpE::from(7), FpE::from(8)]); - let c5 = Fp4E::new([FpE::from(6), FpE::from(7), FpE::from(8), FpE::from(9)]); - let c6 = Fp4E::new([FpE::from(7), FpE::from(8), FpE::from(9), FpE::from(0)]); - let c7 = Fp4E::new([FpE::from(8), FpE::from(9), FpE::from(0), FpE::from(1)]); - - let poly = Polynomial::new(&[c0, c1, c2, c3, c4, c5, c6, c7]); - - let offset = Fp4E::new([FpE::from(10), FpE::from(11), FpE::from(12), FpE::from(13)]); - let blowup_factor = 4; - - let len = poly.coeff_len().next_power_of_two(); - let order = (len * blowup_factor).trailing_zeros(); - let twiddles = - get_powers_of_primitive_root_coset(order.into(), len * blowup_factor, &offset).unwrap(); - - let fft_eval = Polynomial::evaluate_offset_fft::( - &poly, - blowup_factor, - None, - &offset, - ) - .unwrap(); - let naive_eval = poly.evaluate_slice(&twiddles); - - assert_eq!(fft_eval, naive_eval); - } + (poly, new_poly) + } - #[test] - fn test_fft_and_naive_interpolate() { - let c0 = Fp4E::new([FpE::from(1), FpE::from(2), FpE::from(3), FpE::from(4)]); - let c1 = Fp4E::new([FpE::from(2), FpE::from(3), FpE::from(4), FpE::from(5)]); - let c2 = Fp4E::new([FpE::from(3), FpE::from(4), FpE::from(5), FpE::from(6)]); - let c3 = Fp4E::new([FpE::from(4), FpE::from(5), FpE::from(6), FpE::from(7)]); - let c4 = Fp4E::new([FpE::from(5), FpE::from(6), FpE::from(7), FpE::from(8)]); - let c5 = Fp4E::new([FpE::from(6), FpE::from(7), FpE::from(8), FpE::from(9)]); - let c6 = Fp4E::new([FpE::from(7), FpE::from(8), FpE::from(9), FpE::from(0)]); - let c7 = Fp4E::new([FpE::from(8), FpE::from(9), FpE::from(0), FpE::from(1)]); - - let fft_evals = [c0, c1, c2, c3, c4, c5, c6, c7]; - let order = fft_evals.len().trailing_zeros() as u64; - let twiddles: Vec> = - get_powers_of_primitive_root(order, 1 << order, RootsConfig::Natural).unwrap(); - - let naive_poly = Polynomial::interpolate(&twiddles, &fft_evals).unwrap(); - let fft_poly = - Polynomial::interpolate_fft::(&fft_evals).unwrap(); - - assert_eq!(fft_poly, naive_poly) - } - /* - #[test] - fn gen_fft_and_naive_coset_interpolate( - fft_evals: &[FieldElement], - ) -> (Polynomial>, Polynomial>) { - let order = fft_evals.len().trailing_zeros() as u64; - let twiddles = get_powers_of_primitive_root_coset(order, 1 << order, offset).unwrap(); - let offset = Fp4E::new([FpE::from(10), FpE::from(11), FpE::from(12), FpE::from(13)]); - - let naive_poly = Polynomial::interpolate(&twiddles, fft_evals).unwrap(); - let fft_poly = Polynomial::interpolate_offset_fft(fft_evals, &offset).unwrap(); - }*/ + prop_compose! { + fn powers_of_two(max_exp: u8)(exp in 1..max_exp) -> usize { 1 << exp } + // max_exp cannot be multiple of the bits that represent a usize, generally 64 or 32. + // also it can't exceed the test field's two-adicity. + } + prop_compose! { + fn field_element()(coeffs in [any::(); 4]) -> Fp4E { + Fp4E::new([ + FpE::from(coeffs[0]), + FpE::from(coeffs[1]), + FpE::from(coeffs[2]), + FpE::from(coeffs[3])] + ) + } + } + prop_compose! { + fn offset()(num in field_element(), factor in any::()) -> Fp4E { num.pow(factor) } + } + + prop_compose! { + fn field_vec(max_exp: u8)(vec in collection::vec(field_element(), 0..1 << max_exp)) -> Vec { + vec + } + } + prop_compose! { + fn non_power_of_two_sized_field_vec(max_exp: u8)(vec in collection::vec(field_element(), 2..1< Vec { + vec + } + } + prop_compose! { + fn poly(max_exp: u8)(coeffs in field_vec(max_exp)) -> Polynomial { + Polynomial::new(&coeffs) + } + } + prop_compose! { + fn poly_with_non_power_of_two_coeffs(max_exp: u8)(coeffs in non_power_of_two_sized_field_vec(max_exp)) -> Polynomial { + Polynomial::new(&coeffs) + } + } + + proptest! { + // Property-based test that ensures FFT eval. gives same result as a naive polynomial evaluation. + #[test] + #[cfg(not(any(feature = "metal",feature = "cuda")))] + fn test_fft_matches_naive_evaluation(poly in poly(8)) { + let (fft_eval, naive_eval) = gen_fft_and_naive_evaluation(poly); + prop_assert_eq!(fft_eval, naive_eval); + } + + // Property-based test that ensures FFT eval. with coset gives same result as a naive polynomial evaluation. + #[test] + #[cfg(not(any(feature = "metal",feature = "cuda")))] + fn test_fft_coset_matches_naive_evaluation(poly in poly(4), offset in offset(), blowup_factor in powers_of_two(4)) { + let (fft_eval, naive_eval) = gen_fft_coset_and_naive_evaluation(poly, offset, blowup_factor); + prop_assert_eq!(fft_eval, naive_eval); + } + + // #[cfg(not(any(feature = "metal"),not(feature = "cuda")))] + // Property-based test that ensures FFT interpolation is the same as naive.. + #[test] + #[cfg(not(any(feature = "metal",feature = "cuda")))] + fn test_fft_interpolate_matches_naive(fft_evals in field_vec(4) + .prop_filter("Avoid polynomials of size not power of two", + |evals| evals.len().is_power_of_two())) { + let (fft_poly, naive_poly) = gen_fft_and_naive_interpolate(&fft_evals); + prop_assert_eq!(fft_poly, naive_poly); + } + + // Property-based test that ensures FFT interpolation with an offset is the same as naive. + #[test] + #[cfg(not(any(feature = "metal",feature = "cuda")))] + fn test_fft_interpolate_coset_matches_naive(offset in offset(), fft_evals in field_vec(4) + .prop_filter("Avoid polynomials of size not power of two", + |evals| evals.len().is_power_of_two())) { + let (fft_poly, naive_poly) = gen_fft_and_naive_coset_interpolate(&fft_evals, &offset); + prop_assert_eq!(fft_poly, naive_poly); + } + + // Property-based test that ensures interpolation is the inverse operation of evaluation. + #[test] + #[cfg(not(any(feature = "metal",feature = "cuda")))] + fn test_fft_interpolate_is_inverse_of_evaluate( + poly in poly(4).prop_filter("Avoid non pows of two", |poly| poly.coeff_len().is_power_of_two())) { + let (poly, new_poly) = gen_fft_interpolate_and_evaluate(poly); + prop_assert_eq!(poly, new_poly); + } + } + } } From 43bd195206e117c66a21a36fd4101a2453978ac0 Mon Sep 17 00:00:00 2001 From: Joaquin Carletti Date: Mon, 25 Nov 2024 12:44:10 -0300 Subject: [PATCH 05/13] fix typo --- math/src/field/fields/fft_friendly/quartic_babybear.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/math/src/field/fields/fft_friendly/quartic_babybear.rs b/math/src/field/fields/fft_friendly/quartic_babybear.rs index 82b88b1b1..336d34b31 100644 --- a/math/src/field/fields/fft_friendly/quartic_babybear.rs +++ b/math/src/field/fields/fft_friendly/quartic_babybear.rs @@ -54,7 +54,7 @@ impl IsField for Degree4BabyBearExtensionField { } // Return te inverse of a fp4 element if exist. - // This algorithm is inspired by R1sc0 implementation: + // This algorithm is inspired by Risc0 implementation: // https://github.com/risc0/risc0/blob/4c41c739779ef2759a01ebcf808faf0fbffe8793/risc0/core/src/field/baby_bear.rs#L460 fn inv(a: &Self::BaseType) -> Result { let mut b0 = &a[0] * &a[0] + BETA * (&a[1] * (&a[3] + &a[3]) - &a[2] * &a[2]); From cbd52a84ec094a0482468fabb98ba10d041d22d8 Mon Sep 17 00:00:00 2001 From: jotabulacios Date: Mon, 25 Nov 2024 12:45:20 -0300 Subject: [PATCH 06/13] fix clippy --- math/src/field/fields/fft_friendly/quartic_babybear.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/math/src/field/fields/fft_friendly/quartic_babybear.rs b/math/src/field/fields/fft_friendly/quartic_babybear.rs index 336d34b31..6465542e1 100644 --- a/math/src/field/fields/fft_friendly/quartic_babybear.rs +++ b/math/src/field/fields/fft_friendly/quartic_babybear.rs @@ -61,8 +61,8 @@ impl IsField for Degree4BabyBearExtensionField { let mut b2 = &a[0] * (&a[2] + &a[2]) - &a[1] * &a[1] + BETA * (&a[3] * &a[3]); let c = &b0.square() + BETA * b2.square(); let c_inv = c.inv()?; - b0 = b0 * &c_inv; - b2 = b2 * &c_inv; + b0 *= &c_inv; + b2 *= &c_inv; Ok([ &a[0] * &b0 + BETA * &a[2] * &b2, -&a[1] * &b0 - BETA * &a[3] * &b2, From e2a34afda05559791cb20730d67dacb4eef72abd Mon Sep 17 00:00:00 2001 From: Nicole Date: Mon, 25 Nov 2024 12:49:05 -0300 Subject: [PATCH 07/13] add test inv of zero error --- math/src/field/fields/fft_friendly/quartic_babybear.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/math/src/field/fields/fft_friendly/quartic_babybear.rs b/math/src/field/fields/fft_friendly/quartic_babybear.rs index 6465542e1..0429130ca 100644 --- a/math/src/field/fields/fft_friendly/quartic_babybear.rs +++ b/math/src/field/fields/fft_friendly/quartic_babybear.rs @@ -328,6 +328,12 @@ mod tests { assert_eq!(a.inv().unwrap(), a); } + #[test] + fn test_inv_of_zero_error() { + let result = Fp4E::zero().inv(); + assert!(result.is_err()); + } + #[test] fn test_mul_by_inv_is_identity() { let a = Fp4E::from(123456); From 7a9e66c643dee19f5c023626c5b02425a4e260f6 Mon Sep 17 00:00:00 2001 From: Nicole Date: Mon, 25 Nov 2024 13:01:28 -0300 Subject: [PATCH 08/13] fix fmt --- math/src/field/fields/fft_friendly/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/math/src/field/fields/fft_friendly/mod.rs b/math/src/field/fields/fft_friendly/mod.rs index 2b370caed..7ba6a0943 100644 --- a/math/src/field/fields/fft_friendly/mod.rs +++ b/math/src/field/fields/fft_friendly/mod.rs @@ -11,4 +11,4 @@ pub mod stark_252_prime_field; /// Implemenation of the Goldilocks Prime Field p = 2^64 - 2^32 + 1 pub mod u64_goldilocks; /// Implemenation of the Mersenne Prime field p = 2^31 - 1 -pub mod u64_mersenne_montgomery_field; \ No newline at end of file +pub mod u64_mersenne_montgomery_field; From 13fae9c770ef8e39380358d9125fd5a2f2584e10 Mon Sep 17 00:00:00 2001 From: Nicole Date: Mon, 25 Nov 2024 13:25:25 -0300 Subject: [PATCH 09/13] fix clippy and doc --- .../fields/fft_friendly/quartic_babybear.rs | 33 +++++++++---------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/math/src/field/fields/fft_friendly/quartic_babybear.rs b/math/src/field/fields/fft_friendly/quartic_babybear.rs index 0429130ca..a9b6a8d98 100644 --- a/math/src/field/fields/fft_friendly/quartic_babybear.rs +++ b/math/src/field/fields/fft_friendly/quartic_babybear.rs @@ -1,16 +1,15 @@ -use crate::{ - field::{ - element::FieldElement, - errors::FieldError, - fields::fft_friendly::babybear::Babybear31PrimeField, - traits::{IsFFTField, IsField, IsSubFieldOf}, - }, - traits::ByteConversion, +use crate::field::{ + element::FieldElement, + errors::FieldError, + fields::fft_friendly::babybear::Babybear31PrimeField, + traits::{IsFFTField, IsField, IsSubFieldOf}, }; -// BETA = 11 -// -BETA = -11 is the non-residue. -// We are implementig the extension of Baby Bear of degree 4 using the irreducible polynomial x^4 + 11. +#[cfg(feature = "lambdaworks-serde-binary")] +use crate::traits::ByteConversion; + +/// We are implementig the extension of Baby Bear of degree 4 using the irreducible polynomial x^4 + 11. +/// BETA = 11 and -BETA = -11 is the non-residue. pub const BETA: FieldElement = FieldElement::::from_hex_unchecked("b"); @@ -24,9 +23,9 @@ impl IsField for Degree4BabyBearExtensionField { [&a[0] + &b[0], &a[1] + &b[1], &a[2] + &b[2], &a[3] + &b[3]] } - // Result of multiplying two polynomials a = a0 + a1 * x + a2 * x^2 + a3 * x^3 and - // b = b0 + b1 * x + b2 * x^2 + b3 * x^3 by applying distribution and taking - // the remainder of the division by x^4 + 11. + /// Result of multiplying two polynomials a = a0 + a1 * x + a2 * x^2 + a3 * x^3 and + /// b = b0 + b1 * x + b2 * x^2 + b3 * x^3 by applying distribution and taking + /// the remainder of the division by x^4 + 11. fn mul(a: &Self::BaseType, b: &Self::BaseType) -> Self::BaseType { [ &a[0] * &b[0] - BETA * (&a[1] * &b[3] + &a[3] * &b[1] + &a[2] * &b[2]), @@ -53,9 +52,9 @@ impl IsField for Degree4BabyBearExtensionField { [-&a[0], -&a[1], -&a[2], -&a[3]] } - // Return te inverse of a fp4 element if exist. - // This algorithm is inspired by Risc0 implementation: - // https://github.com/risc0/risc0/blob/4c41c739779ef2759a01ebcf808faf0fbffe8793/risc0/core/src/field/baby_bear.rs#L460 + /// Return te inverse of a fp4 element if exist. + /// This algorithm is inspired by Risc0 implementation: + /// fn inv(a: &Self::BaseType) -> Result { let mut b0 = &a[0] * &a[0] + BETA * (&a[1] * (&a[3] + &a[3]) - &a[2] * &a[2]); let mut b2 = &a[0] * (&a[2] + &a[2]) - &a[1] * &a[1] + BETA * (&a[3] * &a[3]); From a6d9ea13b64e9742bcc538c3dd0ad1a11510242e Mon Sep 17 00:00:00 2001 From: jotabulacios Date: Thu, 28 Nov 2024 16:29:27 -0300 Subject: [PATCH 10/13] save work, bench for p3 in lambdaworks works ok --- math/Cargo.toml | 2 + math/benches/criterion_field.rs | 9 +- math/benches/fields/babybear.rs | 370 ++++++++++++++++++++++++++++++++ math/benches/fields/mod.rs | 1 + 4 files changed, 380 insertions(+), 2 deletions(-) create mode 100644 math/benches/fields/babybear.rs diff --git a/math/Cargo.toml b/math/Cargo.toml index 9a6838a10..03d668fe4 100644 --- a/math/Cargo.toml +++ b/math/Cargo.toml @@ -39,6 +39,8 @@ const-random = "0.1.15" iai-callgrind.workspace = true proptest = "1.1.0" pprof = { version = "0.13.0", features = ["criterion", "flamegraph"] } +p3-baby-bear = { git = "https://github.com/Plonky3/Plonky3" } +p3-field = { git = "https://github.com/Plonky3/Plonky3" } [features] default = ["parallel", "std"] diff --git a/math/benches/criterion_field.rs b/math/benches/criterion_field.rs index 6e41cbb4b..558eddba2 100644 --- a/math/benches/criterion_field.rs +++ b/math/benches/criterion_field.rs @@ -5,13 +5,18 @@ mod fields; use fields::mersenne31::{mersenne31_extension_ops_benchmarks, mersenne31_ops_benchmarks}; use fields::mersenne31_montgomery::mersenne31_mont_ops_benchmarks; use fields::{ - stark252::starkfield_ops_benchmarks, u64_goldilocks::u64_goldilocks_ops_benchmarks, + babybear::{ + babybear_extension_ops_benchmarks, babybear_extension_ops_benchmarks_p3, + babybear_ops_benchmarks, babybear_p3_ops_benchmarks, + }, + stark252::starkfield_ops_benchmarks, + u64_goldilocks::u64_goldilocks_ops_benchmarks, u64_goldilocks_montgomery::u64_goldilocks_montgomery_ops_benchmarks, }; criterion_group!( name = field_benches; config = Criterion::default().with_profiler(PProfProfiler::new(100, Output::Flamegraph(None))); - targets = mersenne31_ops_benchmarks, mersenne31_extension_ops_benchmarks, mersenne31_mont_ops_benchmarks, starkfield_ops_benchmarks, u64_goldilocks_ops_benchmarks, u64_goldilocks_montgomery_ops_benchmarks + targets = babybear_p3_ops_benchmarks,babybear_extension_ops_benchmarks_p3, ); criterion_main!(field_benches); diff --git a/math/benches/fields/babybear.rs b/math/benches/fields/babybear.rs new file mode 100644 index 000000000..f8706f3ff --- /dev/null +++ b/math/benches/fields/babybear.rs @@ -0,0 +1,370 @@ +use criterion::Criterion; +use std::hint::black_box; + +use lambdaworks_math::field::fields::fft_friendly::quadratic_babybear::QuadraticBabybearField; +use lambdaworks_math::field::fields::fft_friendly::quartic_babybear::Degree4BabyBearExtensionField; +use lambdaworks_math::field::{ + element::FieldElement, + errors::FieldError, + fields::fft_friendly::babybear::Babybear31PrimeField, + traits::{IsFFTField, IsField, IsSubFieldOf}, +}; + +use p3_baby_bear::BabyBear; +use p3_field::extension::BinomialExtensionField; +use p3_field::{Field, FieldAlgebra}; + +use rand::random; + +use rand::Rng; + +pub type F = FieldElement; +pub type Fp2E = FieldElement; +pub type Fp4E = FieldElement; +type EF4 = BinomialExtensionField; + +// Create a vector of random field elements for the elements using LambdaWorks + +pub fn rand_field_elements(num: usize) -> Vec<(F, F)> { + //let mut result = Vec::with_capacity(num); + let mut result = Vec::with_capacity(num); + for _ in 0..result.capacity() { + result.push((F::from(random::()), F::from(random::()))); + } + result +} + +pub fn rand_babybear_fp4_elements(num: usize) -> Vec<(Fp4E, Fp4E)> { + let mut result = Vec::with_capacity(num); + for _ in 0..num { + result.push(( + Fp4E::new([ + F::from(random::()), + F::from(random::()), + F::from(random::()), + F::from(random::()), + ]), + Fp4E::new([ + F::from(random::()), + F::from(random::()), + F::from(random::()), + F::from(random::()), + ]), + )); + } + result +} + +// Create a vector of random field elements for the elements using Plonky3 + +fn rand_babybear_elements_p3(num: usize) -> Vec<(BabyBear, BabyBear)> { + let mut rng = rand::thread_rng(); + (0..num) + .map(|_| (rng.gen::(), rng.gen::())) + .collect() +} + +fn rand_babybear_fp4_elements_p3(num: usize) -> Vec<(EF4, EF4)> { + let mut rng = rand::thread_rng(); + (0..num) + .map(|_| (rng.gen::(), rng.gen::())) + .collect() +} + +// Operations for BabyBear extension field 4 using Lambdaworks +pub fn babybear_extension_ops_benchmarks(c: &mut Criterion) { + let input: Vec> = [1000000] + .into_iter() + .map(rand_babybear_fp4_elements) + .collect::>(); + + let mut group = c.benchmark_group("BabyBear Fp4 operations"); + + for i in input.clone().into_iter() { + group.bench_with_input(format!("Add of Fp4 {:?}", &i.len()), &i, |bench, i| { + bench.iter(|| { + for (x, y) in i { + black_box(black_box(x) + black_box(y)); + } + }); + }); + } + + for i in input.clone().into_iter() { + group.bench_with_input(format!("Mul of Fp4 {:?}", &i.len()), &i, |bench, i| { + bench.iter(|| { + for (x, y) in i { + black_box(black_box(x) * black_box(y)); + } + }); + }); + } + + for i in input.clone().into_iter() { + group.bench_with_input(format!("Square of Fp4 {:?}", &i.len()), &i, |bench, i| { + bench.iter(|| { + for (x, _) in i { + black_box(black_box(x).square()); + } + }); + }); + } + + for i in input.clone().into_iter() { + group.bench_with_input(format!("Inv of Fp4 {:?}", &i.len()), &i, |bench, i| { + bench.iter(|| { + for (x, y) in i { + black_box(black_box(x) / black_box(y)); + } + }); + }); + } + + for i in input.clone().into_iter() { + group.bench_with_input(format!("Div of Fp4 {:?}", &i.len()), &i, |bench, i| { + bench.iter(|| { + for (x, _) in i { + black_box(black_box(x).inv().unwrap()); + } + }); + }); + } +} + +pub fn babybear_ops_benchmarks(c: &mut Criterion) { + let input: Vec> = [1, 10, 100, 1000, 10000, 100000, 1000000] + .into_iter() + .map(rand_field_elements) + .collect::>(); + let mut group = c.benchmark_group("BabyBear operations using Lambdaworks"); + + for i in input.clone().into_iter() { + group.bench_with_input(format!("add {:?}", &i.len()), &i, |bench, i| { + bench.iter(|| { + for (x, y) in i { + black_box(black_box(x) + black_box(y)); + } + }); + }); + } + + for i in input.clone().into_iter() { + group.bench_with_input(format!("mul {:?}", &i.len()), &i, |bench, i| { + bench.iter(|| { + for (x, y) in i { + black_box(black_box(x) * black_box(y)); + } + }); + }); + } + + for i in input.clone().into_iter() { + group.bench_with_input(format!("pow by 1 {:?}", &i.len()), &i, |bench, i| { + bench.iter(|| { + for (x, _) in i { + black_box(black_box(x).pow(1_u64)); + } + }); + }); + } + + for i in input.clone().into_iter() { + group.bench_with_input(format!("square {:?}", &i.len()), &i, |bench, i| { + bench.iter(|| { + for (x, _) in i { + black_box(black_box(x).square()); + } + }); + }); + } + + for i in input.clone().into_iter() { + group.bench_with_input(format!("square with pow {:?}", &i.len()), &i, |bench, i| { + bench.iter(|| { + for (x, _) in i { + black_box(black_box(x).pow(2_u64)); + } + }); + }); + } + + for i in input.clone().into_iter() { + group.bench_with_input(format!("square with mul {:?}", &i.len()), &i, |bench, i| { + bench.iter(|| { + for (x, _) in i { + black_box(black_box(x) * black_box(x)); + } + }); + }); + } + + for i in input.clone().into_iter() { + group.bench_with_input( + format!("pow {:?}", &i.len()), + &(i, 5u64), + |bench, (i, a)| { + bench.iter(|| { + for (x, _) in i { + black_box(black_box(x).pow(*a)); + } + }); + }, + ); + } + for i in input.clone().into_iter() { + group.bench_with_input(format!("sub {:?}", &i.len()), &i, |bench, i| { + bench.iter(|| { + for (x, y) in i { + black_box(black_box(x) - black_box(y)); + } + }); + }); + } + + for i in input.clone().into_iter() { + group.bench_with_input(format!("inv {:?}", &i.len()), &i, |bench, i| { + bench.iter(|| { + for (x, _) in i { + black_box(black_box(x).inv().unwrap()); + } + }); + }); + } + + for i in input.clone().into_iter() { + group.bench_with_input(format!("div {:?}", &i.len()), &i, |bench, i| { + bench.iter(|| { + for (x, y) in i { + black_box(black_box(x) / black_box(y)); + } + }); + }); + } +} +// Operations benchmarks for BabyBear field using Plonky3 +pub fn babybear_p3_ops_benchmarks(c: &mut Criterion) { + let input: Vec> = [1, 10, 100, 1000, 10000, 100000, 1000000] + .into_iter() + .map(rand_babybear_elements_p3) + .collect::>(); + + let mut group = c.benchmark_group("BabyBear operations using Plonky3"); + + for i in input.clone().into_iter() { + group.bench_with_input(format!("add {:?}", &i.len()), &i, |bench, i| { + bench.iter(|| { + for (x, y) in i { + black_box(black_box(*x) + black_box(*y)); + } + }); + }); + } + + for i in input.clone().into_iter() { + group.bench_with_input(format!("sub {:?}", &i.len()), &i, |bench, i| { + bench.iter(|| { + for (x, y) in i { + black_box(black_box(*x) - black_box(*y)); + } + }); + }); + } + for i in input.clone().into_iter() { + group.bench_with_input(format!("mul {:?}", &i.len()), &i, |bench, i| { + bench.iter(|| { + for (x, y) in i { + black_box(black_box(*x) * black_box(*y)); + } + }); + }); + } + + for i in input.clone().into_iter() { + group.bench_with_input(format!("square {:?}", &i.len()), &i, |bench, i| { + bench.iter(|| { + for (x, _) in i { + black_box(black_box(x).square()); + } + }); + }); + } + for i in input.clone().into_iter() { + group.bench_with_input(format!("inv {:?}", &i.len()), &i, |bench, i| { + bench.iter(|| { + for (x, _) in i { + black_box(black_box(x).inverse()); + } + }); + }); + } + + for i in input.clone().into_iter() { + group.bench_with_input(format!("div {:?}", &i.len()), &i, |bench, i| { + bench.iter(|| { + for (x, y) in i { + black_box(black_box(*x) / black_box(*y)); + } + }); + }); + } +} + +// Operations benchmarks for BabyBear extension 4 field using Plonky3 + +pub fn babybear_extension_ops_benchmarks_p3(c: &mut Criterion) { + let input_sizes = [1, 10, 100, 1000, 10000, 100000, 1000000]; + let input: Vec> = input_sizes + .into_iter() + .map(|size| rand_babybear_fp4_elements_p3(size)) + .collect::>(); + + let mut group = c.benchmark_group("BabyBear Fp4 operations using Plonky3"); + + for i in input.clone().into_iter() { + group.bench_with_input(format!("Add of Fp4 {:?}", &i.len()), &i, |bench, i| { + bench.iter(|| { + for (x, y) in i { + black_box(black_box(*x) + black_box(*y)); + } + }); + }); + } + for i in input.clone().into_iter() { + group.bench_with_input(format!("Mul of Fp4 {:?}", &i.len()), &i, |bench, i| { + bench.iter(|| { + for (x, y) in i { + black_box(black_box(*x) * black_box(*y)); + } + }); + }); + } + for i in input.clone().into_iter() { + group.bench_with_input(format!("Square of Fp4 {:?}", &i.len()), &i, |bench, i| { + bench.iter(|| { + for (x, _) in i { + black_box(black_box(x).square()); + } + }); + }); + } + + for i in input.clone().into_iter() { + group.bench_with_input(format!("Inv of Fp4 {:?}", &i.len()), &i, |bench, i| { + bench.iter(|| { + for (x, _) in i { + black_box(black_box(x).inverse()); + } + }); + }); + } + + for i in input.clone().into_iter() { + group.bench_with_input(format!("Div of Fp4 {:?}", &i.len()), &i, |bench, i| { + bench.iter(|| { + for (x, y) in i { + black_box(black_box(*x) / black_box(*y)); + } + }); + }); + } +} diff --git a/math/benches/fields/mod.rs b/math/benches/fields/mod.rs index a28773c6c..4de5940fa 100644 --- a/math/benches/fields/mod.rs +++ b/math/benches/fields/mod.rs @@ -1,3 +1,4 @@ +pub mod babybear; pub mod mersenne31; pub mod mersenne31_montgomery; pub mod stark252; From a9a22fc56356ec486670a40558f02e869f96702d Mon Sep 17 00:00:00 2001 From: jotabulacios Date: Fri, 29 Nov 2024 11:55:51 -0300 Subject: [PATCH 11/13] save work --- math/benches/criterion_field.rs | 2 +- math/benches/fields/babybear.rs | 49 ++++----------------------------- 2 files changed, 6 insertions(+), 45 deletions(-) diff --git a/math/benches/criterion_field.rs b/math/benches/criterion_field.rs index 558eddba2..f48eb5abe 100644 --- a/math/benches/criterion_field.rs +++ b/math/benches/criterion_field.rs @@ -17,6 +17,6 @@ use fields::{ criterion_group!( name = field_benches; config = Criterion::default().with_profiler(PProfProfiler::new(100, Output::Flamegraph(None))); - targets = babybear_p3_ops_benchmarks,babybear_extension_ops_benchmarks_p3, + targets = babybear_ops_benchmarks,babybear_extension_ops_benchmarks,babybear_p3_ops_benchmarks,babybear_extension_ops_benchmarks_p3 ); criterion_main!(field_benches); diff --git a/math/benches/fields/babybear.rs b/math/benches/fields/babybear.rs index f8706f3ff..f8aaf3cc2 100644 --- a/math/benches/fields/babybear.rs +++ b/math/benches/fields/babybear.rs @@ -56,6 +56,10 @@ pub fn rand_babybear_fp4_elements(num: usize) -> Vec<(Fp4E, Fp4E)> { } // Create a vector of random field elements for the elements using Plonky3 +// use u64? + +//to do create u32 for montgomery in lambdaworks? +// use a more idiomatic way to do the benches fn rand_babybear_elements_p3(num: usize) -> Vec<(BabyBear, BabyBear)> { let mut rng = rand::thread_rng(); @@ -73,7 +77,7 @@ fn rand_babybear_fp4_elements_p3(num: usize) -> Vec<(EF4, EF4)> { // Operations for BabyBear extension field 4 using Lambdaworks pub fn babybear_extension_ops_benchmarks(c: &mut Criterion) { - let input: Vec> = [1000000] + let input: Vec> = [1, 10, 100, 1000, 10000, 100000, 1000000] .into_iter() .map(rand_babybear_fp4_elements) .collect::>(); @@ -158,16 +162,6 @@ pub fn babybear_ops_benchmarks(c: &mut Criterion) { }); } - for i in input.clone().into_iter() { - group.bench_with_input(format!("pow by 1 {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, _) in i { - black_box(black_box(x).pow(1_u64)); - } - }); - }); - } - for i in input.clone().into_iter() { group.bench_with_input(format!("square {:?}", &i.len()), &i, |bench, i| { bench.iter(|| { @@ -178,39 +172,6 @@ pub fn babybear_ops_benchmarks(c: &mut Criterion) { }); } - for i in input.clone().into_iter() { - group.bench_with_input(format!("square with pow {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, _) in i { - black_box(black_box(x).pow(2_u64)); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("square with mul {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, _) in i { - black_box(black_box(x) * black_box(x)); - } - }); - }); - } - - for i in input.clone().into_iter() { - group.bench_with_input( - format!("pow {:?}", &i.len()), - &(i, 5u64), - |bench, (i, a)| { - bench.iter(|| { - for (x, _) in i { - black_box(black_box(x).pow(*a)); - } - }); - }, - ); - } for i in input.clone().into_iter() { group.bench_with_input(format!("sub {:?}", &i.len()), &i, |bench, i| { bench.iter(|| { From 8ad2490ee60487b3c28580d2a6dc7b76cc4478f2 Mon Sep 17 00:00:00 2001 From: jotabulacios Date: Fri, 29 Nov 2024 17:20:53 -0300 Subject: [PATCH 12/13] restore commented benches --- math/benches/criterion_field.rs | 4 +- math/benches/fields/babybear.rs | 140 +++++++++++++------------------- 2 files changed, 58 insertions(+), 86 deletions(-) diff --git a/math/benches/criterion_field.rs b/math/benches/criterion_field.rs index f48eb5abe..e7733c1ad 100644 --- a/math/benches/criterion_field.rs +++ b/math/benches/criterion_field.rs @@ -17,6 +17,8 @@ use fields::{ criterion_group!( name = field_benches; config = Criterion::default().with_profiler(PProfProfiler::new(100, Output::Flamegraph(None))); - targets = babybear_ops_benchmarks,babybear_extension_ops_benchmarks,babybear_p3_ops_benchmarks,babybear_extension_ops_benchmarks_p3 + targets = babybear_ops_benchmarks,babybear_extension_ops_benchmarks,babybear_p3_ops_benchmarks,babybear_extension_ops_benchmarks_p3, + mersenne31_extension_ops_benchmarks,mersenne31_ops_benchmarks,mersenne31_mont_ops_benchmarks,starkfield_ops_benchmarks,u64_goldilocks_ops_benchmarks + ,u64_goldilocks_montgomery_ops_benchmarks ); criterion_main!(field_benches); diff --git a/math/benches/fields/babybear.rs b/math/benches/fields/babybear.rs index f8aaf3cc2..a7a7afd02 100644 --- a/math/benches/fields/babybear.rs +++ b/math/benches/fields/babybear.rs @@ -1,13 +1,9 @@ use criterion::Criterion; use std::hint::black_box; -use lambdaworks_math::field::fields::fft_friendly::quadratic_babybear::QuadraticBabybearField; use lambdaworks_math::field::fields::fft_friendly::quartic_babybear::Degree4BabyBearExtensionField; use lambdaworks_math::field::{ - element::FieldElement, - errors::FieldError, - fields::fft_friendly::babybear::Babybear31PrimeField, - traits::{IsFFTField, IsField, IsSubFieldOf}, + element::FieldElement, fields::fft_friendly::babybear::Babybear31PrimeField, }; use p3_baby_bear::BabyBear; @@ -15,18 +11,13 @@ use p3_field::extension::BinomialExtensionField; use p3_field::{Field, FieldAlgebra}; use rand::random; - use rand::Rng; pub type F = FieldElement; -pub type Fp2E = FieldElement; pub type Fp4E = FieldElement; type EF4 = BinomialExtensionField; -// Create a vector of random field elements for the elements using LambdaWorks - pub fn rand_field_elements(num: usize) -> Vec<(F, F)> { - //let mut result = Vec::with_capacity(num); let mut result = Vec::with_capacity(num); for _ in 0..result.capacity() { result.push((F::from(random::()), F::from(random::()))); @@ -55,12 +46,6 @@ pub fn rand_babybear_fp4_elements(num: usize) -> Vec<(Fp4E, Fp4E)> { result } -// Create a vector of random field elements for the elements using Plonky3 -// use u64? - -//to do create u32 for montgomery in lambdaworks? -// use a more idiomatic way to do the benches - fn rand_babybear_elements_p3(num: usize) -> Vec<(BabyBear, BabyBear)> { let mut rng = rand::thread_rng(); (0..num) @@ -75,17 +60,15 @@ fn rand_babybear_fp4_elements_p3(num: usize) -> Vec<(EF4, EF4)> { .collect() } -// Operations for BabyBear extension field 4 using Lambdaworks -pub fn babybear_extension_ops_benchmarks(c: &mut Criterion) { - let input: Vec> = [1, 10, 100, 1000, 10000, 100000, 1000000] +pub fn babybear_ops_benchmarks(c: &mut Criterion) { + let input: Vec> = [1, 10, 100, 1000, 10000, 100000, 1000000] .into_iter() - .map(rand_babybear_fp4_elements) + .map(rand_field_elements) .collect::>(); - - let mut group = c.benchmark_group("BabyBear Fp4 operations"); + let mut group = c.benchmark_group("BabyBear operations using Lambdaworks"); for i in input.clone().into_iter() { - group.bench_with_input(format!("Add of Fp4 {:?}", &i.len()), &i, |bench, i| { + group.bench_with_input(format!("Addition {:?}", &i.len()), &i, |bench, i| { bench.iter(|| { for (x, y) in i { black_box(black_box(x) + black_box(y)); @@ -95,7 +78,7 @@ pub fn babybear_extension_ops_benchmarks(c: &mut Criterion) { } for i in input.clone().into_iter() { - group.bench_with_input(format!("Mul of Fp4 {:?}", &i.len()), &i, |bench, i| { + group.bench_with_input(format!("Multiplication {:?}", &i.len()), &i, |bench, i| { bench.iter(|| { for (x, y) in i { black_box(black_box(x) * black_box(y)); @@ -105,7 +88,7 @@ pub fn babybear_extension_ops_benchmarks(c: &mut Criterion) { } for i in input.clone().into_iter() { - group.bench_with_input(format!("Square of Fp4 {:?}", &i.len()), &i, |bench, i| { + group.bench_with_input(format!("Square {:?}", &i.len()), &i, |bench, i| { bench.iter(|| { for (x, _) in i { black_box(black_box(x).square()); @@ -115,35 +98,35 @@ pub fn babybear_extension_ops_benchmarks(c: &mut Criterion) { } for i in input.clone().into_iter() { - group.bench_with_input(format!("Inv of Fp4 {:?}", &i.len()), &i, |bench, i| { + group.bench_with_input(format!("Inverse {:?}", &i.len()), &i, |bench, i| { bench.iter(|| { - for (x, y) in i { - black_box(black_box(x) / black_box(y)); + for (x, _) in i { + black_box(black_box(x).inv().unwrap()); } }); }); } for i in input.clone().into_iter() { - group.bench_with_input(format!("Div of Fp4 {:?}", &i.len()), &i, |bench, i| { + group.bench_with_input(format!("Division {:?}", &i.len()), &i, |bench, i| { bench.iter(|| { - for (x, _) in i { - black_box(black_box(x).inv().unwrap()); + for (x, y) in i { + black_box(black_box(x) / black_box(y)); } }); }); } } - -pub fn babybear_ops_benchmarks(c: &mut Criterion) { - let input: Vec> = [1, 10, 100, 1000, 10000, 100000, 1000000] +pub fn babybear_extension_ops_benchmarks(c: &mut Criterion) { + let input: Vec> = [1, 10, 100, 1000, 10000, 100000, 1000000] .into_iter() - .map(rand_field_elements) + .map(rand_babybear_fp4_elements) .collect::>(); - let mut group = c.benchmark_group("BabyBear operations using Lambdaworks"); + + let mut group = c.benchmark_group("BabyBear Fp4 operations"); for i in input.clone().into_iter() { - group.bench_with_input(format!("add {:?}", &i.len()), &i, |bench, i| { + group.bench_with_input(format!("Addition of Fp4 {:?}", &i.len()), &i, |bench, i| { bench.iter(|| { for (x, y) in i { black_box(black_box(x) + black_box(y)); @@ -153,17 +136,21 @@ pub fn babybear_ops_benchmarks(c: &mut Criterion) { } for i in input.clone().into_iter() { - group.bench_with_input(format!("mul {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, y) in i { - black_box(black_box(x) * black_box(y)); - } - }); - }); + group.bench_with_input( + format!("Multiplication of Fp4 {:?}", &i.len()), + &i, + |bench, i| { + bench.iter(|| { + for (x, y) in i { + black_box(black_box(x) * black_box(y)); + } + }); + }, + ); } for i in input.clone().into_iter() { - group.bench_with_input(format!("square {:?}", &i.len()), &i, |bench, i| { + group.bench_with_input(format!("Square of Fp4 {:?}", &i.len()), &i, |bench, i| { bench.iter(|| { for (x, _) in i { black_box(black_box(x).square()); @@ -173,17 +160,17 @@ pub fn babybear_ops_benchmarks(c: &mut Criterion) { } for i in input.clone().into_iter() { - group.bench_with_input(format!("sub {:?}", &i.len()), &i, |bench, i| { + group.bench_with_input(format!("Inverse of Fp4 {:?}", &i.len()), &i, |bench, i| { bench.iter(|| { for (x, y) in i { - black_box(black_box(x) - black_box(y)); + black_box(black_box(x) / black_box(y)); } }); }); } for i in input.clone().into_iter() { - group.bench_with_input(format!("inv {:?}", &i.len()), &i, |bench, i| { + group.bench_with_input(format!("Division of Fp4 {:?}", &i.len()), &i, |bench, i| { bench.iter(|| { for (x, _) in i { black_box(black_box(x).inv().unwrap()); @@ -191,18 +178,8 @@ pub fn babybear_ops_benchmarks(c: &mut Criterion) { }); }); } - - for i in input.clone().into_iter() { - group.bench_with_input(format!("div {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, y) in i { - black_box(black_box(x) / black_box(y)); - } - }); - }); - } } -// Operations benchmarks for BabyBear field using Plonky3 + pub fn babybear_p3_ops_benchmarks(c: &mut Criterion) { let input: Vec> = [1, 10, 100, 1000, 10000, 100000, 1000000] .into_iter() @@ -212,7 +189,7 @@ pub fn babybear_p3_ops_benchmarks(c: &mut Criterion) { let mut group = c.benchmark_group("BabyBear operations using Plonky3"); for i in input.clone().into_iter() { - group.bench_with_input(format!("add {:?}", &i.len()), &i, |bench, i| { + group.bench_with_input(format!("Addition {:?}", &i.len()), &i, |bench, i| { bench.iter(|| { for (x, y) in i { black_box(black_box(*x) + black_box(*y)); @@ -222,16 +199,7 @@ pub fn babybear_p3_ops_benchmarks(c: &mut Criterion) { } for i in input.clone().into_iter() { - group.bench_with_input(format!("sub {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, y) in i { - black_box(black_box(*x) - black_box(*y)); - } - }); - }); - } - for i in input.clone().into_iter() { - group.bench_with_input(format!("mul {:?}", &i.len()), &i, |bench, i| { + group.bench_with_input(format!("Multiplication {:?}", &i.len()), &i, |bench, i| { bench.iter(|| { for (x, y) in i { black_box(black_box(*x) * black_box(*y)); @@ -241,7 +209,7 @@ pub fn babybear_p3_ops_benchmarks(c: &mut Criterion) { } for i in input.clone().into_iter() { - group.bench_with_input(format!("square {:?}", &i.len()), &i, |bench, i| { + group.bench_with_input(format!("Square {:?}", &i.len()), &i, |bench, i| { bench.iter(|| { for (x, _) in i { black_box(black_box(x).square()); @@ -250,7 +218,7 @@ pub fn babybear_p3_ops_benchmarks(c: &mut Criterion) { }); } for i in input.clone().into_iter() { - group.bench_with_input(format!("inv {:?}", &i.len()), &i, |bench, i| { + group.bench_with_input(format!("Inverse {:?}", &i.len()), &i, |bench, i| { bench.iter(|| { for (x, _) in i { black_box(black_box(x).inverse()); @@ -260,7 +228,7 @@ pub fn babybear_p3_ops_benchmarks(c: &mut Criterion) { } for i in input.clone().into_iter() { - group.bench_with_input(format!("div {:?}", &i.len()), &i, |bench, i| { + group.bench_with_input(format!("Division {:?}", &i.len()), &i, |bench, i| { bench.iter(|| { for (x, y) in i { black_box(black_box(*x) / black_box(*y)); @@ -270,8 +238,6 @@ pub fn babybear_p3_ops_benchmarks(c: &mut Criterion) { } } -// Operations benchmarks for BabyBear extension 4 field using Plonky3 - pub fn babybear_extension_ops_benchmarks_p3(c: &mut Criterion) { let input_sizes = [1, 10, 100, 1000, 10000, 100000, 1000000]; let input: Vec> = input_sizes @@ -282,7 +248,7 @@ pub fn babybear_extension_ops_benchmarks_p3(c: &mut Criterion) { let mut group = c.benchmark_group("BabyBear Fp4 operations using Plonky3"); for i in input.clone().into_iter() { - group.bench_with_input(format!("Add of Fp4 {:?}", &i.len()), &i, |bench, i| { + group.bench_with_input(format!("Addition of Fp4 {:?}", &i.len()), &i, |bench, i| { bench.iter(|| { for (x, y) in i { black_box(black_box(*x) + black_box(*y)); @@ -291,13 +257,17 @@ pub fn babybear_extension_ops_benchmarks_p3(c: &mut Criterion) { }); } for i in input.clone().into_iter() { - group.bench_with_input(format!("Mul of Fp4 {:?}", &i.len()), &i, |bench, i| { - bench.iter(|| { - for (x, y) in i { - black_box(black_box(*x) * black_box(*y)); - } - }); - }); + group.bench_with_input( + format!("Multiplication of Fp4 {:?}", &i.len()), + &i, + |bench, i| { + bench.iter(|| { + for (x, y) in i { + black_box(black_box(*x) * black_box(*y)); + } + }); + }, + ); } for i in input.clone().into_iter() { group.bench_with_input(format!("Square of Fp4 {:?}", &i.len()), &i, |bench, i| { @@ -310,7 +280,7 @@ pub fn babybear_extension_ops_benchmarks_p3(c: &mut Criterion) { } for i in input.clone().into_iter() { - group.bench_with_input(format!("Inv of Fp4 {:?}", &i.len()), &i, |bench, i| { + group.bench_with_input(format!("Inverse of Fp4 {:?}", &i.len()), &i, |bench, i| { bench.iter(|| { for (x, _) in i { black_box(black_box(x).inverse()); @@ -320,7 +290,7 @@ pub fn babybear_extension_ops_benchmarks_p3(c: &mut Criterion) { } for i in input.clone().into_iter() { - group.bench_with_input(format!("Div of Fp4 {:?}", &i.len()), &i, |bench, i| { + group.bench_with_input(format!("Division of Fp4 {:?}", &i.len()), &i, |bench, i| { bench.iter(|| { for (x, y) in i { black_box(black_box(*x) / black_box(*y)); From 456c4fa18358493cca98fd3124e8d4a15bfd54ad Mon Sep 17 00:00:00 2001 From: jotabulacios Date: Fri, 29 Nov 2024 17:35:40 -0300 Subject: [PATCH 13/13] fix clippy redundant closure --- math/benches/fields/babybear.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/math/benches/fields/babybear.rs b/math/benches/fields/babybear.rs index a7a7afd02..6a1bc250f 100644 --- a/math/benches/fields/babybear.rs +++ b/math/benches/fields/babybear.rs @@ -242,7 +242,7 @@ pub fn babybear_extension_ops_benchmarks_p3(c: &mut Criterion) { let input_sizes = [1, 10, 100, 1000, 10000, 100000, 1000000]; let input: Vec> = input_sizes .into_iter() - .map(|size| rand_babybear_fp4_elements_p3(size)) + .map(rand_babybear_fp4_elements_p3) .collect::>(); let mut group = c.benchmark_group("BabyBear Fp4 operations using Plonky3");