Skip to content

Commit

Permalink
Merge branch 'circle-starks' into circle-protocol
Browse files Browse the repository at this point in the history
  • Loading branch information
Nicole authored and Nicole committed Oct 31, 2024
2 parents d66d52d + a6738c7 commit 0267a18
Show file tree
Hide file tree
Showing 7 changed files with 146 additions and 84 deletions.
10 changes: 5 additions & 5 deletions math/src/circle/cfft.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ pub fn icfft(
/// This function permutes the slice [0, 2, 4, 6, 7, 5, 3, 1] into [0, 1, 2, 3, 4, 5, 6, 7].
/// TODO: This can be optimized by performing in-place value swapping (WIP).
pub fn order_cfft_result_naive(
input: &mut [FieldElement<Mersenne31Field>],
input: &[FieldElement<Mersenne31Field>],
) -> Vec<FieldElement<Mersenne31Field>> {
let mut result = Vec::new();
let length = input.len();
Expand Down Expand Up @@ -121,9 +121,9 @@ mod tests {
fn ordering_cfft_result_works_for_4_points() {
let expected_slice = [FE::from(0), FE::from(1), FE::from(2), FE::from(3)];

let mut slice = [FE::from(0), FE::from(2), FE::from(3), FE::from(1)];
let slice = [FE::from(0), FE::from(2), FE::from(3), FE::from(1)];

let res = order_cfft_result_naive(&mut slice);
let res = order_cfft_result_naive(&slice);

assert_eq!(res, expected_slice)
}
Expand All @@ -149,7 +149,7 @@ mod tests {
FE::from(15),
];

let mut slice = [
let slice = [
FE::from(0),
FE::from(2),
FE::from(4),
Expand All @@ -168,7 +168,7 @@ mod tests {
FE::from(1),
];

let res = order_cfft_result_naive(&mut slice);
let res = order_cfft_result_naive(&slice);

assert_eq!(res, expected_slice)
}
Expand Down
4 changes: 2 additions & 2 deletions math/src/circle/cosets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ use crate::circle::point::CirclePoint;
use crate::field::fields::mersenne31::field::Mersenne31Field;
use alloc::vec::Vec;

/// Given g_n, a generator of the subgroup <g_n> of the circle of size n,
/// and given a shift, that is a another point of the cirvle,
/// Given g_n, a generator of the subgroup of size n of the circle, i.e. <g_n>,
/// and given a shift, that is a another point of the circle,
/// we define the coset shift + <g_n> which is the set of all the points in
/// <g_n> plus the shift.
/// For example, if <g_4> = {p1, p2, p3, p4}, then g_8 + <g_4> = {g_8 + p1, g_8 + p2, g_8 + p3, g_8 + p4}.
Expand Down
4 changes: 4 additions & 0 deletions math/src/circle/errors.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#[derive(Debug)]
pub enum CircleError {
PointDoesntSatisfyCircleEquation,
}
1 change: 1 addition & 0 deletions math/src/circle/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
pub mod cfft;
pub mod cosets;
pub mod errors;
pub mod point;
pub mod polynomial;
pub mod twiddles;
187 changes: 115 additions & 72 deletions math/src/circle/point.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,82 @@
use super::errors::CircleError;
use crate::field::traits::IsField;
use crate::field::{
element::FieldElement,
fields::mersenne31::{extensions::Degree4ExtensionField, field::Mersenne31Field},
};
use core::ops::{Add, Mul};
use core::ops::{Add, AddAssign, Mul, MulAssign};

/// Given a Field F, we implement here the Group which consists of all the points (x, y) such as
/// x in F, y in F and x^2 + y^2 = 1, i.e. the Circle. The operation of the group will have
/// additive notation and is as follows:
/// (a, b) + (c, d) = (a * c - b * d, a * d + b * c)

#[derive(Debug, Clone)]
pub struct CirclePoint<F: IsField> {
pub x: FieldElement<F>,
pub y: FieldElement<F>,
}

#[derive(Debug)]
pub enum CircleError {
PointDoesntSatisfyCircleEquation,
impl<F: IsField + HasCircleParams<F>> CirclePoint<F> {
pub fn new(x: FieldElement<F>, y: FieldElement<F>) -> Result<Self, CircleError> {
if x.square() + y.square() == FieldElement::one() {
Ok(Self { x, y })
} else {
Err(CircleError::PointDoesntSatisfyCircleEquation)
}
}

/// Neutral element of the Circle group (with additive notation).
pub fn zero() -> Self {
Self::new(FieldElement::one(), FieldElement::zero()).unwrap()
}

/// Computes 2(x, y) = (2x^2 - 1, 2xy).
pub fn double(&self) -> Self {
Self::new(
self.x.square().double() - FieldElement::one(),
self.x.double() * self.y.clone(),
)
.unwrap()
}

/// Computes 2^n * (x, y).
pub fn repeated_double(self, n: u32) -> Self {
let mut res = self;
for _ in 0..n {
res = res.double();
}
res
}

/// Computes the inverse of the point.
/// We are using -(x, y) = (x, -y), i.e. the inverse of the group opertion is conjugation
/// because the norm of every point in the circle is one.
pub fn conjugate(self) -> Self {
Self {
x: self.x,
y: -self.y,
}
}

pub fn antipode(self) -> Self {
Self {
x: -self.x,
y: -self.y,
}
}

pub const GENERATOR: Self = Self {
x: F::CIRCLE_GENERATOR_X,
y: F::CIRCLE_GENERATOR_Y,
};

/// Returns the generator of the subgroup of order n = 2^log_2_size.
/// We are using that 2^k * g is a generator of the subgroup of order 2^{31 - k}.
pub fn get_generator_of_subgroup(log_2_size: u32) -> Self {
Self::GENERATOR.repeated_double(31 - log_2_size)
}

pub const ORDER: u128 = F::ORDER;
}

/// Parameters of the base field that we'll need to define its Circle.
Expand Down Expand Up @@ -71,96 +129,81 @@ impl<F: IsField + HasCircleParams<F>> PartialEq for CirclePoint<F> {
/// (a, b) + (c, d) = (a * c - b * d, a * d + b * c)
impl<F: IsField + HasCircleParams<F>> Add for &CirclePoint<F> {
type Output = CirclePoint<F>;

fn add(self, other: Self) -> Self::Output {
let x = &self.x * &other.x - &self.y * &other.y;
let y = &self.x * &other.y + &self.y * &other.x;
CirclePoint { x, y }
}
}

impl<F: IsField + HasCircleParams<F>> Add for CirclePoint<F> {
type Output = CirclePoint<F>;
fn add(self, rhs: CirclePoint<F>) -> Self::Output {
&self + &rhs
}
}
impl<F: IsField + HasCircleParams<F>> Add<CirclePoint<F>> for &CirclePoint<F> {
type Output = CirclePoint<F>;
fn add(self, rhs: CirclePoint<F>) -> Self::Output {
self + &rhs
}
}
impl<F: IsField + HasCircleParams<F>> Add<&CirclePoint<F>> for CirclePoint<F> {
type Output = CirclePoint<F>;
fn add(self, rhs: &CirclePoint<F>) -> Self::Output {
&self + rhs
}
}
impl<F: IsField + HasCircleParams<F>> AddAssign<&CirclePoint<F>> for CirclePoint<F> {
fn add_assign(&mut self, rhs: &CirclePoint<F>) {
*self = &*self + rhs;
}
}
impl<F: IsField + HasCircleParams<F>> AddAssign<CirclePoint<F>> for CirclePoint<F> {
fn add_assign(&mut self, rhs: CirclePoint<F>) {
*self += &rhs;
}
}
/// Multiplication between a point and a scalar (i.e. group operation repeatedly):
/// (x, y) * n = (x ,y) + ... + (x, y) n-times.
impl<F: IsField + HasCircleParams<F>> Mul<u128> for CirclePoint<F> {
impl<F: IsField + HasCircleParams<F>> Mul<u128> for &CirclePoint<F> {
type Output = CirclePoint<F>;

fn mul(self, mut scalar: u128) -> Self {
let mut res = Self::zero();
let mut cur = self;
fn mul(self, scalar: u128) -> Self::Output {
let mut scalar = scalar;
let mut res = CirclePoint::<F>::zero();
let mut cur = self.clone();
loop {
if scalar == 0 {
return res;
}
if scalar & 1 == 1 {
res = &res + &cur;
res += &cur;
}
cur = cur.double();
scalar >>= 1;
}
}
}

impl<F: IsField + HasCircleParams<F>> CirclePoint<F> {
pub fn new(x: FieldElement<F>, y: FieldElement<F>) -> Result<Self, CircleError> {
if x.square() + y.square() == FieldElement::one() {
Ok(Self { x, y })
} else {
Err(CircleError::PointDoesntSatisfyCircleEquation)
}
}

/// Neutral element of the Circle group (with additive notation).
pub fn zero() -> Self {
Self::new(FieldElement::one(), FieldElement::zero()).unwrap()
}

/// Computes 2(x, y) = (2x^2 - 1, 2xy).
pub fn double(self) -> Self {
Self::new(
self.x.square().double() - FieldElement::one(),
self.x.double() * self.y,
)
.unwrap()
}

/// Computes 2^n * (x, y).
pub fn repeated_double(self, n: u32) -> Self {
let mut res = self;
for _ in 0..n {
res = res.double();
}
res
}

/// Computes the inverse of the point.
/// We are using -(x, y) = (x, -y), i.e. the inverse of the group opertion is conjugation
/// because the norm of every point in the circle is one.
pub fn conjugate(self) -> Self {
Self {
x: self.x,
y: -self.y,
}
impl<F: IsField + HasCircleParams<F>> Mul<u128> for CirclePoint<F> {
type Output = CirclePoint<F>;
fn mul(self, scalar: u128) -> Self::Output {
&self * scalar
}

pub fn antipode(self) -> Self {
Self {
x: -self.x,
y: -self.y,
}
impl<F: IsField + HasCircleParams<F>> MulAssign<u128> for CirclePoint<F> {
fn mul_assign(&mut self, scalar: u128) {
let mut scalar = scalar;
let mut res = CirclePoint::<F>::zero();
loop {
if scalar == 0 {
*self = res.clone();
}
if scalar & 1 == 1 {
res += &*self;
}
*self = self.double();
scalar >>= 1;
}
}

pub const GENERATOR: Self = Self {
x: F::CIRCLE_GENERATOR_X,
y: F::CIRCLE_GENERATOR_Y,
};

/// Returns the generator of the subgroup of order n = 2^log_2_size.
/// We are using that 2^k * g is a generator of the subgroup of order 2^{31 - k}.
pub fn get_generator_of_subgroup(log_2_size: u32) -> Self {
Self::GENERATOR.repeated_double(31 - log_2_size)
}

pub const ORDER: u128 = F::ORDER;
}

#[cfg(test)]
Expand Down
22 changes: 17 additions & 5 deletions math/src/circle/polynomial.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@ use alloc::vec::Vec;
/// Note that coeff has to be a vector with length a power of two 2^n.
#[cfg(feature = "alloc")]
pub fn evaluate_cfft(
mut coeff: Vec<FieldElement<Mersenne31Field>>,
coeff: Vec<FieldElement<Mersenne31Field>>,
) -> Vec<FieldElement<Mersenne31Field>> {
let mut coeff = coeff;

// We get the twiddles for the Evaluation.
let domain_log_2_size: u32 = coeff.len().trailing_zeros();
let coset = Coset::new_standard(domain_log_2_size);
Expand All @@ -30,16 +32,24 @@ pub fn evaluate_cfft(
cfft(&mut coeff, twiddles);

// The cfft returns the evaluations in a certain order, so we permute them to get the natural order.
order_cfft_result_naive(&mut coeff)
order_cfft_result_naive(&coeff)
}

/// Interpolates the 2^n evaluations of a two-variables polynomial of degree 2^n - 1 on the points of the standard coset of size 2^n.
/// As a result we obtain the coefficients of the polynomial in the basis: {1, y, x, xy, 2xˆ2 -1, 2xˆ2y-y, 2xˆ3-x, 2xˆ3y-xy,...}
/// Note that eval has to be a vector of length a power of two 2^n.
/// If the vector of evaluations is empty, it returns an empty vector.
#[cfg(feature = "alloc")]
pub fn interpolate_cfft(
mut eval: Vec<FieldElement<Mersenne31Field>>,
eval: Vec<FieldElement<Mersenne31Field>>,
) -> Vec<FieldElement<Mersenne31Field>> {
let mut eval = eval;

if eval.is_empty() {
let poly: Vec<FieldElement<Mersenne31Field>> = Vec::new();
return poly;
}

// We get the twiddles for the interpolation.
let domain_log_2_size: u32 = eval.len().trailing_zeros();
let coset = Coset::new_standard(domain_log_2_size);
Expand All @@ -54,7 +64,8 @@ pub fn interpolate_cfft(
in_place_bit_reverse_permute::<FieldElement<Mersenne31Field>>(&mut eval_ordered);

// The icfft returns all the coefficients multiplied by 2^n, the length of the evaluations.
// So we multiply every element that outputs the icfft byt the inverse of 2^n to get the actual coefficients.
// So we multiply every element that outputs the icfft by the inverse of 2^n to get the actual coefficients.
// Note that this `unwrap` will never panic because eval.len() != 0.
let factor = (FieldElement::<Mersenne31Field>::from(eval.len() as u64))
.inv()
.unwrap();
Expand Down Expand Up @@ -176,8 +187,9 @@ mod tests {
expected_result.push(point_eval);
}

let input_vec = input.to_vec();
// We evaluate the polynomial using now the cfft.
let result = evaluate_cfft(input.to_vec());
let result = evaluate_cfft(input_vec);
let slice_result: &[FE] = &result;

assert_eq!(slice_result, expected_result);
Expand Down
2 changes: 2 additions & 0 deletions math/src/circle/twiddles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ pub fn get_twiddles(

if config == TwiddlesConfig::Interpolation {
// For the interpolation, we need to take the inverse element of each twiddle in the default order.
// We can take inverse being sure that the `unwrap` won't panic because the twiddles are coordinates
// of elements of the coset (or their squares) so they can't be zero.
twiddles.iter_mut().for_each(|x| {
FieldElement::<Mersenne31Field>::inplace_batch_inverse(x).unwrap();
});
Expand Down

0 comments on commit 0267a18

Please sign in to comment.