bellman_ce/plonk/transparent_engine/
mod.rs

1use crate::ff::*;
2
3use crate::pairing::Engine;
4
5#[macro_use]
6mod impl_macro;
7
8#[derive(PrimeField)]
9#[PrimeFieldModulus = "3618502788666131213697322783095070105623107215331596699973092056135872020481"]
10#[PrimeFieldGenerator = "3"]
11pub struct Fr(FrRepr);
12
13pub trait TransparentEngine: Engine {}
14
15pub trait PartialReductionField: PrimeField {
16    /// Adds another element by this element without reduction.
17    fn add_assign_unreduced(&mut self, other: &Self);
18
19    /// Subtracts another element by this element without reduction.
20    fn sub_assign_unreduced(&mut self, other: &Self);
21
22    /// Multiplies another element by this element without reduction.
23    fn mul_assign_unreduced(&mut self, other: &Self);
24
25    /// Reduces this element.
26    fn reduce_once(&mut self);
27
28    /// Reduces this element by max of three moduluses.
29    fn reduce_completely(&mut self);
30
31    fn overflow_factor(&self) -> usize;
32}
33
34pub trait PartialTwoBitReductionField: PartialReductionField {
35    /// Subtracts another element by this element without reduction.
36    fn sub_assign_twice_unreduced(&mut self, other: &Self);
37
38    /// Reduces this element by two moduluses.
39    fn reduce_twice(&mut self);
40
41    /// Reduces this element by max of three moduluses.
42    fn reduce_completely(&mut self);
43}
44
45pub mod engine {
46    use super::Fr;
47
48    use super::impl_macro::*;
49
50    transparent_engine_impl!{Transparent252, Fr}
51}
52
53pub use self::engine::Transparent252;
54
55pub(crate) mod proth;
56pub(crate) mod proth_engine;
57
58#[cfg(test)]
59mod test {
60    #[test]
61    fn test_bench_proth_lde() {
62        use rand::{XorShiftRng, SeedableRng, Rand, Rng};
63        use super::Fr as FrMontNaive;
64        use super::proth::Fr as FrOptimized;
65        use crate::plonk::polynomials::*;
66        use std::time::Instant;
67        use crate::worker::*;
68        use crate::plonk::commitments::transparent::utils::*;
69
70        let poly_sizes = vec![1_000_000, 2_000_000, 4_000_000];
71
72        let worker = Worker::new();
73
74        for poly_size in poly_sizes.into_iter() {
75            let res1 = {
76                let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
77                let coeffs = (0..poly_size).map(|_| FrMontNaive::rand(rng)).collect::<Vec<_>>();
78            
79                let poly = Polynomial::<FrMontNaive, _>::from_coeffs(coeffs).unwrap();
80                let start = Instant::now();
81                let eval_result = poly.lde(&worker, 16).unwrap();
82                println!("LDE with factor 16 for size {} with naive mont reduction taken {:?}", poly_size, start.elapsed());
83
84                eval_result.into_coeffs()
85            };
86
87            let res2 = {
88                let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
89                let coeffs = (0..poly_size).map(|_| FrOptimized::rand(rng)).collect::<Vec<_>>();
90            
91                let poly = Polynomial::<FrOptimized, _>::from_coeffs(coeffs).unwrap();
92                let start = Instant::now();
93                let eval_result = poly.lde(&worker, 16).unwrap();
94                println!("LDE with factor 16 for size {} with optimized mont reduction taken {:?}", poly_size, start.elapsed());
95
96                eval_result.into_coeffs()
97            };
98
99            assert_eq!(format!("{}", res1[0]), format!("{}", res2[0]));
100
101        }
102    }
103
104    #[test]
105    fn test_proth_field() {
106        use crate::ff::{Field, PrimeField, to_hex};
107        use super::Fr as FrMontNaive;
108        use super::proth::Fr as FrOptimized;
109
110        let one_naive = FrMontNaive::from_str("1").unwrap();
111        let one_optimized = FrOptimized::from_str("1").unwrap();
112
113        println!("{}", FrMontNaive::one());
114        println!("{}", FrOptimized::one());
115
116        println!("{}", one_naive.into_raw_repr());
117        println!("{}", one_optimized.into_raw_repr());
118
119        let mut tmp0 = one_naive;
120        tmp0.mul_assign(&one_naive);
121
122        let mut tmp1 = one_optimized;
123        tmp1.mul_assign(&one_optimized);
124
125        assert_eq!(to_hex(&tmp0), to_hex(&tmp1));
126
127        assert_eq!(to_hex(&FrMontNaive::multiplicative_generator()), to_hex(&FrOptimized::multiplicative_generator()));
128    }
129
130    #[test]
131    fn test_bench_precomputations_for_proth_fft() {
132        use rand::{XorShiftRng, SeedableRng, Rand, Rng};
133        use super::Fr as FrMontNaive;
134        use super::proth::Fr as FrOptimized;
135        use crate::plonk::polynomials::*;
136        use std::time::Instant;
137        use crate::worker::*;
138        use crate::plonk::commitments::transparent::utils::*;
139        use crate::plonk::fft::fft::best_fft;
140        use crate::plonk::fft::with_precomputation::FftPrecomputations;
141        use crate::plonk::fft::with_precomputation::fft::best_fft as best_fft_with_precomputations;
142        use crate::plonk::commitments::transparent::precomputations::*;
143        use crate::plonk::domains::Domain;
144        let poly_sizes = vec![32_000_000, 64_000_000];
145
146        let worker = Worker::new();
147
148        for poly_size in poly_sizes.into_iter() {
149            let domain = Domain::<FrOptimized>::new_for_size(poly_size).unwrap();
150            let precomp = PrecomputedOmegas::<FrOptimized>::new_for_domain_size(domain.size as usize);
151            let omega = domain.generator;
152            let log_n = domain.power_of_two as u32;
153
154            let poly_size = domain.size as usize;
155
156            let res1 = {
157                let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
158                let mut coeffs = (0..poly_size).map(|_| FrOptimized::rand(rng)).collect::<Vec<_>>();
159                let start = Instant::now();
160                best_fft(&mut coeffs, &worker, &omega, log_n, None);
161                println!("FFT for size {} without precomputation taken {:?}", poly_size, start.elapsed());
162
163                coeffs
164            };
165
166            let res2 = {
167                let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
168                let mut coeffs = (0..poly_size).map(|_| FrOptimized::rand(rng)).collect::<Vec<_>>();
169                let start = Instant::now();
170                best_fft_with_precomputations(&mut coeffs, &worker, &omega, log_n, None, &precomp);
171                println!("FFT for size {} with precomputation taken {:?}", poly_size, start.elapsed());
172
173                coeffs
174            };
175
176            assert!(res1 == res2);
177
178        }
179    }
180
181    #[test]
182    fn test_bench_precomputations_for_proth_lde() {
183        use rand::{XorShiftRng, SeedableRng, Rand, Rng};
184        use super::Fr as FrMontNaive;
185        use super::proth::Fr as FrOptimized;
186        use crate::plonk::polynomials::*;
187        use std::time::Instant;
188        use crate::worker::*;
189        use crate::plonk::commitments::transparent::utils::*;
190        use crate::plonk::fft::with_precomputation::FftPrecomputations;
191        use crate::plonk::commitments::transparent::precomputations::*;
192
193        let poly_sizes = vec![1_000_000, 2_000_000, 4_000_000];
194
195        let worker = Worker::new();
196
197        for poly_size in poly_sizes.into_iter() {
198            let res1 = {
199                let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
200                let coeffs = (0..poly_size).map(|_| FrMontNaive::rand(rng)).collect::<Vec<_>>();
201            
202                let poly = Polynomial::<FrMontNaive, _>::from_coeffs(coeffs).unwrap();
203                let start = Instant::now();
204                let eval_result = poly.lde(&worker, 16).unwrap();
205                println!("LDE with factor 16 for size {} with naive mont reduction taken {:?}", poly_size, start.elapsed());
206
207                eval_result.into_coeffs()
208            };
209
210            let res2 = {
211                let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
212                let coeffs = (0..poly_size).map(|_| FrOptimized::rand(rng)).collect::<Vec<_>>();
213            
214                let poly = Polynomial::<FrOptimized, _>::from_coeffs(coeffs).unwrap();
215                let precomp = PrecomputedOmegas::<FrOptimized>::new_for_domain_size(poly.size());
216                let start = Instant::now();
217                let eval_result = poly.lde_using_multiple_cosets_with_precomputation(&worker, 16, &precomp).unwrap();
218                println!("LDE with factor 16 for size {} with optimized mont reduction and precomputation taken {:?}", poly_size, start.elapsed());
219
220                eval_result.into_coeffs()
221            };
222
223            assert_eq!(format!("{}", res1[0]), format!("{}", res2[0]));
224
225        }
226    }
227
228    #[test]
229    fn test_bench_ct_ploth_lde() {
230        use rand::{XorShiftRng, SeedableRng, Rand, Rng};
231        use super::proth::Fr as Fr;
232        use crate::plonk::polynomials::*;
233        use std::time::Instant;
234        use crate::worker::*;
235        use crate::plonk::commitments::transparent::utils::*;
236        use crate::plonk::fft::cooley_tukey_ntt::{CTPrecomputations, BitReversedOmegas};
237
238        let poly_sizes = vec![1_000_000, 2_000_000, 4_000_000];
239
240        // let poly_sizes = vec![2];
241
242        let worker = Worker::new();
243
244        for poly_size in poly_sizes.into_iter() {
245            let poly_size = poly_size as usize;
246
247            let res1 = {
248                let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
249                let coeffs = (0..poly_size).map(|_| Fr::rand(rng)).collect::<Vec<_>>();
250            
251                let poly = Polynomial::<Fr, _>::from_coeffs(coeffs).unwrap();
252                let start = Instant::now();
253                let eval_result = poly.lde_using_multiple_cosets(&worker, 16).unwrap();
254                println!("LDE with factor 16 for size {} taken {:?}", poly_size, start.elapsed());
255
256                eval_result.into_coeffs()
257            };
258
259            let res2 = {
260                let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
261                let coeffs = (0..poly_size).map(|_| Fr::rand(rng)).collect::<Vec<_>>();
262            
263                let poly = Polynomial::<Fr, _>::from_coeffs(coeffs).unwrap();
264                let precomp = BitReversedOmegas::<Fr>::new_for_domain_size(poly.size());
265                let start = Instant::now();
266                let eval_result = poly.lde_using_bitreversed_ntt(&worker, 16, &precomp).unwrap();
267                println!("LDE with factor 16 for size {} with optimized ntt taken {:?}", poly_size, start.elapsed());
268
269                eval_result.into_coeffs()
270            };
271
272            assert_eq!(res1, res2);
273
274            assert!(res1 == res2);
275        }
276    }
277
278    // #[test]
279    // fn test_bench_noalloc_bit_reversed_ploth_lde() {
280    //     use rand::{XorShiftRng, SeedableRng, Rand, Rng};
281    //     use super::proth::Fr as Fr;
282    //     use crate::plonk::polynomials::*;
283    //     use std::time::Instant;
284    //     use crate::worker::*;
285    //     use crate::plonk::commitments::transparent::utils::*;
286    //     use crate::plonk::fft::cooley_tukey_ntt::{CTPrecomputations, BitReversedOmegas};
287
288    //     let poly_sizes = vec![32, 64,1_000_000, 2_000_000, 4_000_000];
289
290    //     let worker = Worker::new();
291
292    //     for poly_size in poly_sizes.into_iter() {
293    //         let poly_size = poly_size as usize;
294
295    //         let res1 = {
296    //             let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
297    //             let coeffs = (0..poly_size).map(|_| Fr::rand(rng)).collect::<Vec<_>>();
298            
299    //             let poly = Polynomial::<Fr, _>::from_coeffs(coeffs).unwrap();
300    //             let start = Instant::now();
301    //             let eval_result = poly.lde(&worker, 16).unwrap();
302    //             println!("LDE with factor 16 for size {} taken {:?}", poly_size, start.elapsed());
303
304    //             eval_result.into_coeffs()
305    //         };
306
307    //         let res2 = {
308    //             let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
309    //             let coeffs = (0..poly_size).map(|_| Fr::rand(rng)).collect::<Vec<_>>();
310            
311    //             let poly = Polynomial::<Fr, _>::from_coeffs(coeffs).unwrap();
312    //             let precomp = BitReversedOmegas::<Fr>::new_for_domain_size(poly.size());
313    //             let start = Instant::now();
314    //             let eval_result = poly.lde_using_bitreversed_ntt_no_allocations_lowest_bits_reversed(&worker, 16, &precomp).unwrap();
315    //             println!("LDE with factor 16 for size {} with optimized ntt taken {:?}", poly_size, start.elapsed());
316
317    //             eval_result.into_coeffs()
318    //         };
319
320    //         use crate::ff::PrimeField;
321
322    //         fn check_permutation<F: PrimeField>(one: &[F], two: &[F]) -> (bool, Vec<usize>) {
323    //             let mut permutation: Vec<usize> = (0..one.len()).collect();
324    //             let mut valid = true;
325
326    //             for (i, el) in one.iter().enumerate() {
327    //                 let mut idx = 0;
328    //                 let mut found = false;
329    //                 for (j, el2) in two.iter().enumerate() {
330    //                     if *el == *el2 {
331    //                         idx = j;
332    //                         found = true;
333    //                         break;
334    //                     }
335    //                 }
336    //                 if !found {
337    //                     println!("Not found for {}", i);
338    //                     valid = false;
339    //                     break;
340    //                 }
341    //                 permutation[i] = idx;
342    //             }
343
344    //             (valid, permutation)
345    //         }
346
347    //         if poly_size < 1000 {
348    //             let (valid, permutation) = check_permutation(&res1, &res2);
349
350    //             assert!(valid);
351    //         }
352    //     }
353    // }
354
355    #[test]
356    fn test_bench_partial_reduction_bitreversed_lde() {
357        use crate::ff::Field;
358        use crate::ff::PrimeField;
359        use rand::{XorShiftRng, SeedableRng, Rand, Rng};
360        use super::proth::Fr as Fr;
361        use super::PartialTwoBitReductionField;
362        use crate::plonk::polynomials::*;
363        use std::time::Instant;
364        use crate::worker::*;
365        use crate::plonk::commitments::transparent::utils::*;
366        use crate::plonk::fft::cooley_tukey_ntt::{CTPrecomputations, BitReversedOmegas};
367
368        let poly_sizes = vec![32, 64, 1_000_000, 2_000_000, 4_000_000];
369
370        let worker = Worker::new();
371
372        for poly_size in poly_sizes.into_iter() {
373            let poly_size = poly_size as usize;
374
375            let res1 = {
376                let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
377                let coeffs = (0..poly_size).map(|_| Fr::rand(rng)).collect::<Vec<_>>();
378            
379                let poly = Polynomial::<Fr, _>::from_coeffs(coeffs).unwrap();
380                let start = Instant::now();
381                let eval_result = poly.lde(&worker, 16).unwrap();
382                println!("LDE with factor 16 for size {} taken {:?}", poly_size, start.elapsed());
383
384                eval_result.into_coeffs()
385            };
386
387            let mut res2 = {
388                let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
389                let coeffs = (0..poly_size).map(|_| Fr::rand(rng)).collect::<Vec<_>>();
390            
391                let poly = Polynomial::<Fr, _>::from_coeffs(coeffs).unwrap();
392                let precomp = BitReversedOmegas::<Fr>::new_for_domain_size(poly.size());
393                let start = Instant::now();
394                let eval_result = poly.bitreversed_lde_using_bitreversed_ntt(&worker, 16, &precomp, &<Fr as Field>::one()).unwrap();
395                println!("LDE with factor 16 for size {} bitreversed {:?}", poly_size, start.elapsed());
396
397                eval_result
398            };
399
400            if poly_size < 1000 {
401                res2.bitreverse_enumeration(&worker);
402                assert!(res1 == res2.into_coeffs());
403            }
404        }
405    }
406
407    #[test]
408    fn test_bench_ct_proth_fft() {
409        use rand::{XorShiftRng, SeedableRng, Rand, Rng};
410        use super::proth::Fr as Fr;
411        use crate::plonk::polynomials::*;
412        use std::time::Instant;
413        use crate::worker::*;
414        use crate::plonk::commitments::transparent::utils::*;
415        use crate::plonk::fft::cooley_tukey_ntt::{CTPrecomputations, BitReversedOmegas, best_ct_ntt};
416        use crate::plonk::fft::fft::best_fft;
417        use crate::plonk::domains::Domain;
418
419        // let poly_sizes = vec![1_000_000, 2_000_000];
420        let poly_sizes = vec![1_000_000];
421
422        // let poly_sizes = vec![2];
423
424        let worker = Worker::new();
425
426        for poly_size in poly_sizes.into_iter() {
427            let poly_size = poly_size as usize;
428            let poly_size = poly_size.next_power_of_two();
429
430            let _res1 = {
431                let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
432                let mut coeffs = (0..poly_size).map(|_| Fr::rand(rng)).collect::<Vec<_>>();
433                let log_n = log2_floor(coeffs.len());
434                let domain = Domain::new_for_size(coeffs.len() as u64).unwrap();
435
436                let start = Instant::now();
437                best_fft(&mut coeffs, &worker, &domain.generator, log_n, Some(8));
438                println!("FFT for size {} taken {:?}", poly_size, start.elapsed());
439
440                coeffs
441            };
442
443            let (input, output) = {
444                let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
445                let mut coeffs = (0..poly_size).map(|_| Fr::rand(rng)).collect::<Vec<_>>();
446                let input = coeffs.clone();
447                let log_n = log2_floor(coeffs.len());
448                let precomp = BitReversedOmegas::<Fr>::new_for_domain_size(coeffs.len());
449
450                let start = Instant::now();
451                best_ct_ntt(&mut coeffs, &worker, log_n, Some(8), &precomp);
452                println!("CT FFT for size {} taken {:?}", poly_size, start.elapsed());
453
454                (input, coeffs)
455            };
456
457            let mut writer = std::io::BufWriter::with_capacity(
458                1<<24, 
459                std::fs::File::create("./fft_test_input.data").unwrap()  
460            );
461
462            use crate::pairing::ff::{Field, PrimeField};
463            use std::io::Write;
464            use crate::pairing::ff::PrimeFieldRepr;
465
466            let mut scratch = vec![];
467            for el in input.into_iter() {
468                let repr = el.into_raw_repr();
469                repr.write_le(&mut scratch).unwrap();
470                writer.write_all(&scratch).unwrap();
471                scratch.truncate(0);
472            }
473
474            println!("Results");
475            for el in output[0..16].iter() {
476                println!("{}", el.into_raw_repr());
477            }
478
479            let domain = Domain::<Fr>::new_for_size(poly_size as u64).unwrap();
480
481            println!("Omegas");
482
483            assert!(domain.generator.pow(&[1u64<<20]) == Fr::one());
484
485            for i in 0..=20 {
486                let pow = 1u64 << i;
487                println!("Omega^{} = {}", pow, domain.generator.pow(&[pow]).into_raw_repr());
488            }
489
490            println!("Idenity = {}", Fr::one().into_raw_repr());
491        }
492    }
493    
494}
495
496
497
498