stwo_gpu/core/fields/
mod.rs1use 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
43fn 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 for i in 1..n {
56 dst[i] = dst[i - 1].clone() * column[i].clone();
57 }
58
59 let mut curr_inverse = dst[n - 1].inverse();
61
62 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
70pub 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 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 let mut tail_inverses: [F; WIDTH] = array::from_fn(|_| F::one());
93 batch_inverse_classic(&dst[n - WIDTH..], &mut tail_inverses);
94
95 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 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#[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 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}