Skip to main content

stwo_gpu/core/fields/
mod.rs

1use core::array;
2use core::fmt::{Debug, Display};
3use core::iter::{Product, Sum};
4use core::ops::{Mul, MulAssign, Neg};
5
6use num_traits::{NumAssign, NumAssignOps, NumOps, One};
7#[cfg(feature = "parallel")]
8use rayon::prelude::*;
9use std_shims::Vec;
10
11use super::utils;
12
13pub mod cm31;
14pub mod m31;
15pub mod qm31;
16
17pub trait FieldExpOps: Mul<Output = Self> + MulAssign + Sized + One + Clone {
18    fn square(&self) -> Self {
19        self.clone() * self.clone()
20    }
21
22    fn pow(&self, exp: u128) -> Self {
23        let mut res = Self::one();
24        let mut base = self.clone();
25        let mut exp = exp;
26        while exp > 0 {
27            if exp & 1 == 1 {
28                res *= base.clone();
29            }
30            base = base.square();
31            exp >>= 1;
32        }
33        res
34    }
35
36    fn inverse(&self) -> Self;
37
38    fn batch_inverse(column: &[Self]) -> Vec<Self> {
39        batch_inverse(column)
40    }
41}
42
43/// Assumes dst is initialized and of the same length as column.
44fn batch_inverse_classic<T: FieldExpOps>(column: &[T], dst: &mut [T]) {
45    let n = column.len();
46    debug_assert!(dst.len() >= n);
47
48    if let Some(first) = column.first() {
49        dst[0] = first.clone();
50    } else {
51        return;
52    }
53
54    // First pass.
55    for i in 1..n {
56        dst[i] = dst[i - 1].clone() * column[i].clone();
57    }
58
59    // Inverse cumulative product.
60    let mut curr_inverse = dst[n - 1].inverse();
61
62    // Second pass.
63    for i in (1..n).rev() {
64        dst[i] = dst[i - 1].clone() * curr_inverse.clone();
65        curr_inverse *= column[i].clone();
66    }
67    dst[0] = curr_inverse;
68}
69
70/// Inverts a batch of elements using Montgomery's trick.
71pub fn batch_inverse_in_place<F: FieldExpOps>(column: &[F], dst: &mut [F]) {
72    const WIDTH: usize = 4;
73    let n = column.len();
74    debug_assert!(dst.len() >= n);
75
76    if n <= WIDTH || !n.is_multiple_of(WIDTH) {
77        batch_inverse_classic(column, dst);
78        return;
79    }
80
81    // First pass. Compute 'WIDTH' cumulative products in an interleaving fashion, reducing
82    // instruction dependency and allowing better pipelining.
83    let mut cum_prod: [F; WIDTH] = array::from_fn(|_| F::one());
84    dst[..WIDTH].clone_from_slice(&cum_prod);
85    for i in 0..n {
86        cum_prod[i % WIDTH] *= column[i].clone();
87        dst[i] = cum_prod[i % WIDTH].clone();
88    }
89
90    // Inverse cumulative products.
91    // Use classic batch inversion.
92    let mut tail_inverses: [F; WIDTH] = array::from_fn(|_| F::one());
93    batch_inverse_classic(&dst[n - WIDTH..], &mut tail_inverses);
94
95    // Second pass.
96    for i in (WIDTH..n).rev() {
97        dst[i] = dst[i - WIDTH].clone() * tail_inverses[i % WIDTH].clone();
98        tail_inverses[i % WIDTH] *= column[i].clone();
99    }
100    dst[0..WIDTH].clone_from_slice(&tail_inverses);
101}
102
103pub fn batch_inverse<F: FieldExpOps>(column: &[F]) -> Vec<F> {
104    let mut dst = unsafe { utils::uninit_vec(column.len()) };
105    batch_inverse_in_place(column, &mut dst);
106    dst
107}
108
109pub fn batch_inverse_chunked<T: FieldExpOps + Send + Sync>(
110    column: &[T],
111    dst: &mut [T],
112    chunk_size: usize,
113) {
114    assert!(column.len() <= dst.len());
115
116    #[cfg(not(feature = "parallel"))]
117    let iter = dst.chunks_mut(chunk_size).zip(column.chunks(chunk_size));
118
119    #[cfg(feature = "parallel")]
120    let iter = dst
121        .par_chunks_mut(chunk_size)
122        .zip(column.par_chunks(chunk_size));
123
124    iter.for_each(|(dst, column)| {
125        batch_inverse_in_place(column, dst);
126    });
127}
128
129pub trait Field:
130    NumAssign
131    + Neg<Output = Self>
132    + ComplexConjugate
133    + Copy
134    + Default
135    + Debug
136    + Display
137    + PartialOrd
138    + Ord
139    + Send
140    + Sync
141    + Sized
142    + FieldExpOps
143    + Product
144    + for<'a> Product<&'a Self>
145    + Sum
146    + for<'a> Sum<&'a Self>
147{
148    fn double(&self) -> Self {
149        *self + *self
150    }
151}
152
153pub trait ComplexConjugate {
154    /// # Example
155    ///
156    /// ```
157    /// use stwo::core::fields::m31::P;
158    /// use stwo::core::fields::qm31::QM31;
159    /// use stwo::core::fields::ComplexConjugate;
160    ///
161    /// let x = QM31::from_u32_unchecked(1, 2, 3, 4);
162    /// assert_eq!(
163    ///     x.complex_conjugate(),
164    ///     QM31::from_u32_unchecked(1, 2, P - 3, P - 4)
165    /// );
166    /// ```
167    fn complex_conjugate(&self) -> Self;
168}
169
170pub trait ExtensionOf<F: Field>: Field + From<F> + NumOps<F> + NumAssignOps<F> {
171    const EXTENSION_DEGREE: usize;
172}
173
174impl<F: Field> ExtensionOf<F> for F {
175    const EXTENSION_DEGREE: usize = 1;
176}
177
178#[macro_export]
179macro_rules! impl_field {
180    ($field_name: ty, $field_size: ident) => {
181        use core::iter::{Product, Sum};
182
183        use num_traits::{Num, One, Zero};
184        use $crate::core::fields::Field;
185
186        impl Num for $field_name {
187            type FromStrRadixErr = std_shims::Box<dyn core::error::Error>;
188
189            fn from_str_radix(_str: &str, _radix: u32) -> Result<Self, Self::FromStrRadixErr> {
190                unimplemented!(
191                    "Num::from_str_radix is not implemented for {}",
192                    stringify!($field_name)
193                );
194            }
195        }
196
197        impl Field for $field_name {}
198
199        impl AddAssign for $field_name {
200            fn add_assign(&mut self, rhs: Self) {
201                *self = *self + rhs;
202            }
203        }
204
205        impl SubAssign for $field_name {
206            fn sub_assign(&mut self, rhs: Self) {
207                *self = *self - rhs;
208            }
209        }
210
211        impl MulAssign for $field_name {
212            fn mul_assign(&mut self, rhs: Self) {
213                *self = *self * rhs;
214            }
215        }
216
217        impl Div for $field_name {
218            type Output = Self;
219
220            #[allow(clippy::suspicious_arithmetic_impl)]
221            fn div(self, rhs: Self) -> Self::Output {
222                self * rhs.inverse()
223            }
224        }
225
226        impl DivAssign for $field_name {
227            fn div_assign(&mut self, rhs: Self) {
228                *self = *self / rhs;
229            }
230        }
231
232        impl Rem for $field_name {
233            type Output = Self;
234
235            fn rem(self, _rhs: Self) -> Self::Output {
236                unimplemented!("Rem is not implemented for {}", stringify!($field_name));
237            }
238        }
239
240        impl RemAssign for $field_name {
241            fn rem_assign(&mut self, _rhs: Self) {
242                unimplemented!(
243                    "RemAssign is not implemented for {}",
244                    stringify!($field_name)
245                );
246            }
247        }
248
249        impl Product for $field_name {
250            fn product<I>(mut iter: I) -> Self
251            where
252                I: Iterator<Item = Self>,
253            {
254                let first = iter.next().unwrap_or_else(Self::one);
255                iter.fold(first, |a, b| a * b)
256            }
257        }
258
259        impl<'a> Product<&'a Self> for $field_name {
260            fn product<I>(iter: I) -> Self
261            where
262                I: Iterator<Item = &'a Self>,
263            {
264                iter.map(|&v| v).product()
265            }
266        }
267
268        impl Sum for $field_name {
269            fn sum<I>(mut iter: I) -> Self
270            where
271                I: Iterator<Item = Self>,
272            {
273                let first = iter.next().unwrap_or_else(Self::zero);
274                iter.fold(first, |a, b| a + b)
275            }
276        }
277
278        impl<'a> Sum<&'a Self> for $field_name {
279            fn sum<I>(iter: I) -> Self
280            where
281                I: Iterator<Item = &'a Self>,
282            {
283                iter.map(|&v| v).sum()
284            }
285        }
286    };
287}
288
289/// Used to extend a field (with characteristic M31) by 2.
290#[macro_export]
291macro_rules! impl_extension_field {
292    ($field_name: ident, $extended_field_name: ty) => {
293        use rand::distributions::{Distribution, Standard};
294        use $crate::core::fields::ExtensionOf;
295
296        impl ExtensionOf<M31> for $field_name {
297            const EXTENSION_DEGREE: usize =
298                <$extended_field_name as ExtensionOf<M31>>::EXTENSION_DEGREE * 2;
299        }
300
301        impl Add for $field_name {
302            type Output = Self;
303
304            fn add(self, rhs: Self) -> Self::Output {
305                Self(self.0 + rhs.0, self.1 + rhs.1)
306            }
307        }
308
309        impl Neg for $field_name {
310            type Output = Self;
311
312            fn neg(self) -> Self::Output {
313                Self(-self.0, -self.1)
314            }
315        }
316
317        impl Sub for $field_name {
318            type Output = Self;
319
320            fn sub(self, rhs: Self) -> Self::Output {
321                Self(self.0 - rhs.0, self.1 - rhs.1)
322            }
323        }
324
325        impl One for $field_name {
326            fn one() -> Self {
327                Self(
328                    <$extended_field_name>::one(),
329                    <$extended_field_name>::zero(),
330                )
331            }
332        }
333
334        impl Zero for $field_name {
335            fn zero() -> Self {
336                Self(
337                    <$extended_field_name>::zero(),
338                    <$extended_field_name>::zero(),
339                )
340            }
341
342            fn is_zero(&self) -> bool {
343                *self == Self::zero()
344            }
345        }
346
347        impl Add<M31> for $field_name {
348            type Output = Self;
349
350            fn add(self, rhs: M31) -> Self::Output {
351                Self(self.0 + rhs, self.1)
352            }
353        }
354
355        impl Add<$field_name> for M31 {
356            type Output = $field_name;
357
358            fn add(self, rhs: $field_name) -> Self::Output {
359                rhs + self
360            }
361        }
362
363        impl Sub<M31> for $field_name {
364            type Output = Self;
365
366            fn sub(self, rhs: M31) -> Self::Output {
367                Self(self.0 - rhs, self.1)
368            }
369        }
370
371        impl Sub<$field_name> for M31 {
372            type Output = $field_name;
373
374            fn sub(self, rhs: $field_name) -> Self::Output {
375                -rhs + self
376            }
377        }
378
379        impl Mul<M31> for $field_name {
380            type Output = Self;
381
382            fn mul(self, rhs: M31) -> Self::Output {
383                Self(self.0 * rhs, self.1 * rhs)
384            }
385        }
386
387        impl Mul<$field_name> for M31 {
388            type Output = $field_name;
389
390            fn mul(self, rhs: $field_name) -> Self::Output {
391                rhs * self
392            }
393        }
394
395        impl Div<M31> for $field_name {
396            type Output = Self;
397
398            fn div(self, rhs: M31) -> Self::Output {
399                Self(self.0 / rhs, self.1 / rhs)
400            }
401        }
402
403        impl Div<$field_name> for M31 {
404            type Output = $field_name;
405
406            #[allow(clippy::suspicious_arithmetic_impl)]
407            fn div(self, rhs: $field_name) -> Self::Output {
408                rhs.inverse() * self
409            }
410        }
411
412        impl ComplexConjugate for $field_name {
413            fn complex_conjugate(&self) -> Self {
414                Self(self.0, -self.1)
415            }
416        }
417
418        impl From<M31> for $field_name {
419            fn from(x: M31) -> Self {
420                Self(x.into(), <$extended_field_name>::zero())
421            }
422        }
423
424        impl AddAssign<M31> for $field_name {
425            fn add_assign(&mut self, rhs: M31) {
426                *self = *self + rhs;
427            }
428        }
429
430        impl SubAssign<M31> for $field_name {
431            fn sub_assign(&mut self, rhs: M31) {
432                *self = *self - rhs;
433            }
434        }
435
436        impl MulAssign<M31> for $field_name {
437            fn mul_assign(&mut self, rhs: M31) {
438                *self = *self * rhs;
439            }
440        }
441
442        impl DivAssign<M31> for $field_name {
443            fn div_assign(&mut self, rhs: M31) {
444                *self = *self / rhs;
445            }
446        }
447
448        impl Rem<M31> for $field_name {
449            type Output = Self;
450
451            fn rem(self, _rhs: M31) -> Self::Output {
452                unimplemented!("Rem is not implemented for {}", stringify!($field_name));
453            }
454        }
455
456        impl RemAssign<M31> for $field_name {
457            fn rem_assign(&mut self, _rhs: M31) {
458                unimplemented!(
459                    "RemAssign is not implemented for {}",
460                    stringify!($field_name)
461                );
462            }
463        }
464
465        impl Distribution<$field_name> for Standard {
466            // Not intended for cryptographic use. Should only be used in tests and benchmarks.
467            fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> $field_name {
468                $field_name(rng.gen(), rng.gen())
469            }
470        }
471    };
472}
473
474#[cfg(test)]
475mod tests {
476    use num_traits::Zero;
477    use rand::rngs::SmallRng;
478    use rand::{Rng, SeedableRng};
479    use std_shims::Vec;
480
481    use super::batch_inverse_in_place;
482    use crate::core::fields::m31::M31;
483    use crate::core::fields::{batch_inverse, batch_inverse_chunked};
484    use crate::core::utils;
485
486    #[test]
487    fn test_batch_inverse() {
488        let mut rng = SmallRng::seed_from_u64(0);
489        let elements: [M31; 16] = rng.gen();
490        let expected = elements.iter().map(|e| e.inverse()).collect::<Vec<_>>();
491
492        let actual = batch_inverse(&elements);
493
494        assert_eq!(expected, actual);
495    }
496
497    #[test]
498    #[should_panic]
499    fn test_slice_batch_inverse_wrong_dst_size() {
500        let mut rng = SmallRng::seed_from_u64(0);
501        let elements: [M31; 16] = rng.gen();
502        let mut dst = [M31::zero(); 15];
503
504        batch_inverse_in_place(&elements, &mut dst);
505    }
506
507    #[test]
508    fn test_batch_inverse_chunked() {
509        let mut rng = SmallRng::seed_from_u64(0);
510        let elements: [M31; 16] = rng.gen();
511        let chunk_size = 4;
512        let expected = batch_inverse(&elements);
513
514        let mut result = unsafe { utils::uninit_vec(elements.len()) };
515        batch_inverse_chunked(&elements, &mut result, chunk_size);
516
517        assert_eq!(expected, result);
518    }
519}