poulpy/poulpy-hal/src/test_suite/convolution.rs
poulpy-hal/src/test_suite/convolution.rsRaw
1use crate::{
2 api::{
3 Convolution, ModuleN, ScratchOwnedAlloc, ScratchOwnedBorrow, ScratchTakeBasic, TakeSlice, VecZnxBigNormalize,
4 VecZnxDftAlloc, VecZnxDftApply, VecZnxIdftApplyConsume, VecZnxNormalizeInplace,
5 },
6 layouts::{
7 Backend, FillUniform, Scratch, ScratchOwned, VecZnx, VecZnxBig, VecZnxDft, VecZnxToMut, VecZnxToRef, ZnxInfos, ZnxView,
8 ZnxViewMut, ZnxZero,
9 },
10 source::Source,
11};
12
13pub fn test_convolution<M, BE: Backend>(module: &M)
14where
15 M: ModuleN
16 + Convolution<BE>
17 + VecZnxDftAlloc<BE>
18 + VecZnxDftApply<BE>
19 + VecZnxIdftApplyConsume<BE>
20 + VecZnxBigNormalize<BE>
21 + VecZnxNormalizeInplace<BE>,
22 Scratch<BE>: ScratchTakeBasic,
23 ScratchOwned<BE>: ScratchOwnedAlloc<BE> + ScratchOwnedBorrow<BE>,
24{
25 let mut source: Source = Source::new([0u8; 32]);
26
27 let base2k: usize = 12;
28
29 for a_cols in 1..3 {
30 for b_cols in 1..3 {
31 for a_size in 1..5 {
32 for b_size in 1..5 {
33 let mut a: VecZnx<Vec<u8>> = VecZnx::alloc(module.n(), a_cols, a_size);
34 let mut b: VecZnx<Vec<u8>> = VecZnx::alloc(module.n(), b_cols, b_size);
35
36 let mut c_want: VecZnx<Vec<u8>> = VecZnx::alloc(module.n(), a_cols + b_cols - 1, b_size + a_size);
37 let mut c_have: VecZnx<Vec<u8>> = VecZnx::alloc(module.n(), c_want.cols(), c_want.size());
38
39 let mut scratch: ScratchOwned<BE> = ScratchOwned::alloc(module.convolution_tmp_bytes(c_want.size()));
40
41 a.fill_uniform(base2k, &mut source);
42 b.fill_uniform(base2k, &mut source);
43
44 let mut b_dft: VecZnxDft<Vec<u8>, BE> = module.vec_znx_dft_alloc(b.cols(), b.size());
45
46 for i in 0..b.cols() {
47 module.vec_znx_dft_apply(1, 0, &mut b_dft, i, &b, i);
48 }
49
50 for mut res_scale in 0..2 * c_want.size() as i64 + 1 {
51 res_scale -= c_want.size() as i64;
52
53 let mut c_have_dft: VecZnxDft<Vec<u8>, BE> = module.vec_znx_dft_alloc(c_have.cols(), c_have.size());
54 module.convolution(&mut c_have_dft, res_scale, &a, &b_dft, scratch.borrow());
55
56 let c_have_big: VecZnxBig<Vec<u8>, BE> = module.vec_znx_idft_apply_consume(c_have_dft);
57
58 for i in 0..c_have.cols() {
59 module.vec_znx_big_normalize(
60 base2k,
61 &mut c_have,
62 i,
63 base2k,
64 &c_have_big,
65 i,
66 scratch.borrow(),
67 );
68 }
69
70 convolution_naive(
71 module,
72 base2k,
73 &mut c_want,
74 res_scale,
75 &a,
76 &b,
77 scratch.borrow(),
78 );
79
80 assert_eq!(c_want, c_have);
81 }
82 }
83 }
84 }
85 }
86}
87
88fn convolution_naive<R, A, B, M, BE: Backend>(
89 module: &M,
90 base2k: usize,
91 res: &mut R,
92 res_scale: i64,
93 a: &A,
94 b: &B,
95 scratch: &mut Scratch<BE>,
96) where
97 R: VecZnxToMut,
98 A: VecZnxToRef,
99 B: VecZnxToRef,
100 M: VecZnxNormalizeInplace<BE>,
101 Scratch<BE>: TakeSlice,
102{
103 let res: &mut VecZnx<&mut [u8]> = &mut res.to_mut();
104 let a: &VecZnx<&[u8]> = &a.to_ref();
105 let b: &VecZnx<&[u8]> = &b.to_ref();
106
107 assert!(res.cols() >= a.cols() + b.cols() - 1);
108
109 res.zero();
110
111 for a_col in 0..a.cols() {
112 for a_limb in 0..a.size() {
113 for b_col in 0..b.cols() {
114 for b_limb in 0..b.size() {
115 let res_scale_abs = res_scale.unsigned_abs() as usize;
116
117 let mut res_limb: usize = a_limb + b_limb + 1;
118
119 if res_scale <= 0 {
120 res_limb += res_scale_abs;
121
122 if res_limb < res.size() {
123 negacyclic_convolution_naive_add(
124 res.at_mut(a_col + b_col, res_limb),
125 a.at(a_col, a_limb),
126 b.at(b_col, b_limb),
127 );
128 }
129 } else if res_limb >= res_scale_abs {
130 res_limb -= res_scale_abs;
131
132 if res_limb < res.size() {
133 negacyclic_convolution_naive_add(
134 res.at_mut(a_col + b_col, res_limb),
135 a.at(a_col, a_limb),
136 b.at(b_col, b_limb),
137 );
138 }
139 }
140 }
141 }
142 }
143 }
144
145 for i in 0..res.cols() {
146 module.vec_znx_normalize_inplace(base2k, res, i, scratch);
147 }
148}
149
150fn negacyclic_convolution_naive_add(res: &mut [i64], a: &[i64], b: &[i64]) {
151 let n: usize = res.len();
152 for i in 0..n {
153 let ai: i64 = a[i];
154 let lim: usize = n - i;
155 for j in 0..lim {
156 res[i + j] += ai * b[j];
157 }
158 for j in lim..n {
159 res[i + j - n] -= ai * b[j];
160 }
161 }
162}