poulpy/poulpy-backend/src/cpu_spqlios/spqlios-arithmetic/test/spqlios_vec_rnx_test.cpp
poulpy-backend/src/cpu_spqlios/spqlios-arithmetic/test/spqlios_vec_rnx_test.cppRaw
1#include <gtest/gtest.h>
2
3#include "spqlios/arithmetic/vec_rnx_arithmetic_private.h"
4#include "spqlios/reim/reim_fft.h"
5#include "testlib/vec_rnx_layout.h"
6
7// disabling this test by default, since it depicts on purpose wrong accesses
8#if 0
9TEST(rnx_layout, valgrind_antipattern_test) {
10 uint64_t n = 4;
11 rnx_vec_f64_layout v(n, 7, 13);
12 // this should be ok
13 v.set(0, rnx_f64::zero(n));
14 // this should abort (wrong ring dimension)
15 ASSERT_DEATH(v.set(3, rnx_f64::zero(2 * n)), "");
16 // this should abort (out of bounds)
17 ASSERT_DEATH(v.set(8, rnx_f64::zero(n)), "");
18 // this should be ok
19 ASSERT_EQ(v.get_copy_zext(0), rnx_f64::zero(n));
20 // should be an uninit read
21 ASSERT_TRUE(!(v.get_copy_zext(2) == rnx_f64::zero(n))); // should be uninit
22 // should be an invalid read (inter-slice)
23 ASSERT_NE(v.data()[4], 0);
24 ASSERT_EQ(v.data()[2], 0); // should be ok
25 // should be an uninit read
26 ASSERT_NE(v.data()[13], 0); // should be uninit
27}
28#endif
29
30// test of binary operations
31
32// test for out of place calls
33template <typename ACTUAL_FCN, typename EXPECT_FCN>
34void test_vec_rnx_elemw_binop_outplace(ACTUAL_FCN binop, EXPECT_FCN ref_binop) {
35 for (uint64_t n : {2, 4, 8, 128}) {
36 RNX_MODULE_TYPE mtype = FFT64;
37 MOD_RNX* mod = new_rnx_module_info(n, mtype);
38 for (uint64_t sa : {7, 13, 15}) {
39 for (uint64_t sb : {7, 13, 15}) {
40 for (uint64_t sc : {7, 13, 15}) {
41 uint64_t a_sl = uniform_u64_bits(3) * 5 + n;
42 uint64_t b_sl = uniform_u64_bits(3) * 5 + n;
43 uint64_t c_sl = uniform_u64_bits(3) * 5 + n;
44 rnx_vec_f64_layout la(n, sa, a_sl);
45 rnx_vec_f64_layout lb(n, sb, b_sl);
46 rnx_vec_f64_layout lc(n, sc, c_sl);
47 std::vector<rnx_f64> expect(sc);
48 for (uint64_t i = 0; i < sa; ++i) {
49 la.set(i, rnx_f64::random_log2bound(n, 1.));
50 }
51 for (uint64_t i = 0; i < sb; ++i) {
52 lb.set(i, rnx_f64::random_log2bound(n, 1.));
53 }
54 for (uint64_t i = 0; i < sc; ++i) {
55 expect[i] = ref_binop(la.get_copy_zext(i), lb.get_copy_zext(i));
56 }
57 binop(mod, // N
58 lc.data(), sc, c_sl, // res
59 la.data(), sa, a_sl, // a
60 lb.data(), sb, b_sl);
61 for (uint64_t i = 0; i < sc; ++i) {
62 ASSERT_EQ(lc.get_copy_zext(i), expect[i]);
63 }
64 }
65 }
66 }
67 delete_rnx_module_info(mod);
68 }
69}
70// test for inplace1 calls
71template <typename ACTUAL_FCN, typename EXPECT_FCN>
72void test_vec_rnx_elemw_binop_inplace1(ACTUAL_FCN binop, EXPECT_FCN ref_binop) {
73 for (uint64_t n : {2, 4, 64}) {
74 RNX_MODULE_TYPE mtype = FFT64;
75 MOD_RNX* mod = new_rnx_module_info(n, mtype);
76 for (uint64_t sa : {3, 9, 12}) {
77 for (uint64_t sb : {3, 9, 12}) {
78 uint64_t a_sl = uniform_u64_bits(3) * 5 + n;
79 uint64_t b_sl = uniform_u64_bits(3) * 5 + n;
80 rnx_vec_f64_layout la(n, sa, a_sl);
81 rnx_vec_f64_layout lb(n, sb, b_sl);
82 std::vector<rnx_f64> expect(sa);
83 for (uint64_t i = 0; i < sa; ++i) {
84 la.set(i, rnx_f64::random_log2bound(n, 1.));
85 }
86 for (uint64_t i = 0; i < sb; ++i) {
87 lb.set(i, rnx_f64::random_log2bound(n, 1.));
88 }
89 for (uint64_t i = 0; i < sa; ++i) {
90 expect[i] = ref_binop(la.get_copy_zext(i), lb.get_copy_zext(i));
91 }
92 binop(mod, // N
93 la.data(), sa, a_sl, // res
94 la.data(), sa, a_sl, // a
95 lb.data(), sb, b_sl);
96 for (uint64_t i = 0; i < sa; ++i) {
97 ASSERT_EQ(la.get_copy_zext(i), expect[i]);
98 }
99 }
100 }
101 delete_rnx_module_info(mod);
102 }
103}
104// test for inplace2 calls
105template <typename ACTUAL_FCN, typename EXPECT_FCN>
106void test_vec_rnx_elemw_binop_inplace2(ACTUAL_FCN binop, EXPECT_FCN ref_binop) {
107 for (uint64_t n : {4, 32, 64}) {
108 RNX_MODULE_TYPE mtype = FFT64;
109 MOD_RNX* mod = new_rnx_module_info(n, mtype);
110 for (uint64_t sa : {3, 9, 12}) {
111 for (uint64_t sb : {3, 9, 12}) {
112 uint64_t a_sl = uniform_u64_bits(3) * 5 + n;
113 uint64_t b_sl = uniform_u64_bits(3) * 5 + n;
114 rnx_vec_f64_layout la(n, sa, a_sl);
115 rnx_vec_f64_layout lb(n, sb, b_sl);
116 std::vector<rnx_f64> expect(sb);
117 for (uint64_t i = 0; i < sa; ++i) {
118 la.set(i, rnx_f64::random_log2bound(n, 1.));
119 }
120 for (uint64_t i = 0; i < sb; ++i) {
121 lb.set(i, rnx_f64::random_log2bound(n, 1.));
122 }
123 for (uint64_t i = 0; i < sb; ++i) {
124 expect[i] = ref_binop(la.get_copy_zext(i), lb.get_copy_zext(i));
125 }
126 binop(mod, // N
127 lb.data(), sb, b_sl, // res
128 la.data(), sa, a_sl, // a
129 lb.data(), sb, b_sl);
130 for (uint64_t i = 0; i < sb; ++i) {
131 ASSERT_EQ(lb.get_copy_zext(i), expect[i]);
132 }
133 }
134 }
135 delete_rnx_module_info(mod);
136 }
137}
138// test for inplace3 calls
139template <typename ACTUAL_FCN, typename EXPECT_FCN>
140void test_vec_rnx_elemw_binop_inplace3(ACTUAL_FCN binop, EXPECT_FCN ref_binop) {
141 for (uint64_t n : {2, 16, 1024}) {
142 RNX_MODULE_TYPE mtype = FFT64;
143 MOD_RNX* mod = new_rnx_module_info(n, mtype);
144 for (uint64_t sa : {2, 6, 11}) {
145 uint64_t a_sl = uniform_u64_bits(3) * 5 + n;
146 rnx_vec_f64_layout la(n, sa, a_sl);
147 std::vector<rnx_f64> expect(sa);
148 for (uint64_t i = 0; i < sa; ++i) {
149 la.set(i, rnx_f64::random_log2bound(n, 1.));
150 }
151 for (uint64_t i = 0; i < sa; ++i) {
152 expect[i] = ref_binop(la.get_copy_zext(i), la.get_copy_zext(i));
153 }
154 binop(mod, // N
155 la.data(), sa, a_sl, // res
156 la.data(), sa, a_sl, // a
157 la.data(), sa, a_sl);
158 for (uint64_t i = 0; i < sa; ++i) {
159 ASSERT_EQ(la.get_copy_zext(i), expect[i]);
160 }
161 }
162 delete_rnx_module_info(mod);
163 }
164}
165template <typename ACTUAL_FCN, typename EXPECT_FCN>
166void test_vec_rnx_elemw_binop(ACTUAL_FCN binop, EXPECT_FCN ref_binop) {
167 test_vec_rnx_elemw_binop_outplace(binop, ref_binop);
168 test_vec_rnx_elemw_binop_inplace1(binop, ref_binop);
169 test_vec_rnx_elemw_binop_inplace2(binop, ref_binop);
170 test_vec_rnx_elemw_binop_inplace3(binop, ref_binop);
171}
172
173static rnx_f64 poly_add(const rnx_f64& a, const rnx_f64& b) { return a + b; }
174static rnx_f64 poly_sub(const rnx_f64& a, const rnx_f64& b) { return a - b; }
175TEST(vec_rnx, vec_rnx_add) { test_vec_rnx_elemw_binop(vec_rnx_add, poly_add); }
176TEST(vec_rnx, vec_rnx_add_ref) { test_vec_rnx_elemw_binop(vec_rnx_add_ref, poly_add); }
177#ifdef __x86_64__
178TEST(vec_rnx, vec_rnx_add_avx) { test_vec_rnx_elemw_binop(vec_rnx_add_avx, poly_add); }
179#endif
180TEST(vec_rnx, vec_rnx_sub) { test_vec_rnx_elemw_binop(vec_rnx_sub, poly_sub); }
181TEST(vec_rnx, vec_rnx_sub_ref) { test_vec_rnx_elemw_binop(vec_rnx_sub_ref, poly_sub); }
182#ifdef __x86_64__
183TEST(vec_rnx, vec_rnx_sub_avx) { test_vec_rnx_elemw_binop(vec_rnx_sub_avx, poly_sub); }
184#endif
185
186// test for out of place calls
187template <typename ACTUAL_FCN, typename EXPECT_FCN>
188void test_vec_rnx_elemw_unop_param_outplace(ACTUAL_FCN test_mul_xp_minus_one, EXPECT_FCN ref_mul_xp_minus_one,
189 int64_t (*param_gen)()) {
190 for (uint64_t n : {2, 4, 8, 128}) {
191 MOD_RNX* mod = new_rnx_module_info(n, FFT64);
192 for (uint64_t sa : {7, 13, 15}) {
193 for (uint64_t sb : {7, 13, 15}) {
194 {
195 int64_t p = param_gen();
196 uint64_t a_sl = uniform_u64_bits(3) * 5 + n;
197 uint64_t b_sl = uniform_u64_bits(3) * 4 + n;
198 rnx_vec_f64_layout la(n, sa, a_sl);
199 rnx_vec_f64_layout lb(n, sb, b_sl);
200 std::vector<rnx_f64> expect(sb);
201 for (uint64_t i = 0; i < sa; ++i) {
202 la.set(i, rnx_f64::random_log2bound(n, 0));
203 }
204 for (uint64_t i = 0; i < sb; ++i) {
205 expect[i] = ref_mul_xp_minus_one(p, la.get_copy_zext(i));
206 }
207 test_mul_xp_minus_one(mod, //
208 p, //
209 lb.data(), sb, b_sl, //
210 la.data(), sa, a_sl //
211 );
212 for (uint64_t i = 0; i < sb; ++i) {
213 ASSERT_EQ(lb.get_copy_zext(i), expect[i]) << n << " " << sa << " " << sb << " " << i;
214 }
215 }
216 }
217 }
218 delete_rnx_module_info(mod);
219 }
220}
221
222// test for inplace calls
223template <typename ACTUAL_FCN, typename EXPECT_FCN>
224void test_vec_rnx_elemw_unop_param_inplace(ACTUAL_FCN actual_function, EXPECT_FCN ref_function,
225 int64_t (*param_gen)()) {
226 for (uint64_t n : {2, 16, 1024}) {
227 MOD_RNX* mod = new_rnx_module_info(n, FFT64);
228 for (uint64_t sa : {2, 6, 11}) {
229 {
230 int64_t p = param_gen();
231 uint64_t a_sl = uniform_u64_bits(3) * 5 + n;
232 rnx_vec_f64_layout la(n, sa, a_sl);
233 std::vector<rnx_f64> expect(sa);
234 for (uint64_t i = 0; i < sa; ++i) {
235 la.set(i, rnx_f64::random_log2bound(n, 0));
236 }
237 for (uint64_t i = 0; i < sa; ++i) {
238 expect[i] = ref_function(p, la.get_copy_zext(i));
239 }
240 actual_function(mod, // N
241 p, //;
242 la.data(), sa, a_sl, // res
243 la.data(), sa, a_sl // a
244 );
245 for (uint64_t i = 0; i < sa; ++i) {
246 ASSERT_EQ(la.get_copy_zext(i), expect[i]) << n << " " << sa << " " << i;
247 }
248 }
249 }
250 delete_rnx_module_info(mod);
251 }
252}
253
254static int64_t random_mul_xp_minus_one_param() { return uniform_i64(); }
255static int64_t random_automorphism_param() { return 2 * uniform_i64() + 1; }
256static int64_t random_rotation_param() { return uniform_i64(); }
257
258template <typename ACTUAL_FCN, typename EXPECT_FCN>
259void test_vec_rnx_elemw_mul_xp_minus_one(ACTUAL_FCN binop, EXPECT_FCN ref_binop) {
260 test_vec_rnx_elemw_unop_param_outplace(binop, ref_binop, random_mul_xp_minus_one_param);
261 test_vec_rnx_elemw_unop_param_inplace(binop, ref_binop, random_mul_xp_minus_one_param);
262}
263template <typename ACTUAL_FCN, typename EXPECT_FCN>
264void test_vec_rnx_elemw_rotate(ACTUAL_FCN binop, EXPECT_FCN ref_binop) {
265 test_vec_rnx_elemw_unop_param_outplace(binop, ref_binop, random_rotation_param);
266 test_vec_rnx_elemw_unop_param_inplace(binop, ref_binop, random_rotation_param);
267}
268template <typename ACTUAL_FCN, typename EXPECT_FCN>
269void test_vec_rnx_elemw_automorphism(ACTUAL_FCN binop, EXPECT_FCN ref_binop) {
270 test_vec_rnx_elemw_unop_param_outplace(binop, ref_binop, random_automorphism_param);
271 test_vec_rnx_elemw_unop_param_inplace(binop, ref_binop, random_automorphism_param);
272}
273
274static rnx_f64 poly_mul_xp_minus_one(const int64_t p, const rnx_f64& a) {
275 uint64_t n = a.nn();
276 rnx_f64 res(n);
277 for (uint64_t i = 0; i < n; ++i) {
278 res.set_coeff(i, a.get_coeff(i - p) - a.get_coeff(i));
279 }
280 return res;
281}
282static rnx_f64 poly_rotate(const int64_t p, const rnx_f64& a) {
283 uint64_t n = a.nn();
284 rnx_f64 res(n);
285 for (uint64_t i = 0; i < n; ++i) {
286 res.set_coeff(i, a.get_coeff(i - p));
287 }
288 return res;
289}
290static rnx_f64 poly_automorphism(const int64_t p, const rnx_f64& a) {
291 uint64_t n = a.nn();
292 rnx_f64 res(n);
293 for (uint64_t i = 0; i < n; ++i) {
294 res.set_coeff(i * p, a.get_coeff(i));
295 }
296 return res;
297}
298
299TEST(vec_rnx, vec_rnx_mul_xp_minus_one) {
300 test_vec_rnx_elemw_mul_xp_minus_one(vec_rnx_mul_xp_minus_one, poly_mul_xp_minus_one);
301}
302TEST(vec_rnx, vec_rnx_mul_xp_minus_one_ref) {
303 test_vec_rnx_elemw_mul_xp_minus_one(vec_rnx_mul_xp_minus_one_ref, poly_mul_xp_minus_one);
304}
305
306TEST(vec_rnx, vec_rnx_rotate) { test_vec_rnx_elemw_rotate(vec_rnx_rotate, poly_rotate); }
307TEST(vec_rnx, vec_rnx_rotate_ref) { test_vec_rnx_elemw_rotate(vec_rnx_rotate_ref, poly_rotate); }
308TEST(vec_rnx, vec_rnx_automorphism) { test_vec_rnx_elemw_automorphism(vec_rnx_automorphism, poly_automorphism); }
309TEST(vec_rnx, vec_rnx_automorphism_ref) {
310 test_vec_rnx_elemw_automorphism(vec_rnx_automorphism_ref, poly_automorphism);
311}
312
313// test for out of place calls
314template <typename ACTUAL_FCN, typename EXPECT_FCN>
315void test_vec_rnx_elemw_unop_outplace(ACTUAL_FCN actual_function, EXPECT_FCN ref_function) {
316 for (uint64_t n : {2, 4, 8, 128}) {
317 MOD_RNX* mod = new_rnx_module_info(n, FFT64);
318 for (uint64_t sa : {7, 13, 15}) {
319 for (uint64_t sb : {7, 13, 15}) {
320 {
321 uint64_t a_sl = uniform_u64_bits(3) * 5 + n;
322 uint64_t b_sl = uniform_u64_bits(3) * 4 + n;
323 rnx_vec_f64_layout la(n, sa, a_sl);
324 rnx_vec_f64_layout lb(n, sb, b_sl);
325 std::vector<rnx_f64> expect(sb);
326 for (uint64_t i = 0; i < sa; ++i) {
327 la.set(i, rnx_f64::random_log2bound(n, 0));
328 }
329 for (uint64_t i = 0; i < sb; ++i) {
330 expect[i] = ref_function(la.get_copy_zext(i));
331 }
332 actual_function(mod, //
333 lb.data(), sb, b_sl, //
334 la.data(), sa, a_sl //
335 );
336 for (uint64_t i = 0; i < sb; ++i) {
337 ASSERT_EQ(lb.get_copy_zext(i), expect[i]) << n << " " << sa << " " << sb << " " << i;
338 }
339 }
340 }
341 }
342 delete_rnx_module_info(mod);
343 }
344}
345
346// test for inplace calls
347template <typename ACTUAL_FCN, typename EXPECT_FCN>
348void test_vec_rnx_elemw_unop_inplace(ACTUAL_FCN actual_function, EXPECT_FCN ref_function) {
349 for (uint64_t n : {2, 16, 1024}) {
350 MOD_RNX* mod = new_rnx_module_info(n, FFT64);
351 for (uint64_t sa : {2, 6, 11}) {
352 {
353 uint64_t a_sl = uniform_u64_bits(3) * 5 + n;
354 rnx_vec_f64_layout la(n, sa, a_sl);
355 std::vector<rnx_f64> expect(sa);
356 for (uint64_t i = 0; i < sa; ++i) {
357 la.set(i, rnx_f64::random_log2bound(n, 0));
358 }
359 for (uint64_t i = 0; i < sa; ++i) {
360 expect[i] = ref_function(la.get_copy_zext(i));
361 }
362 actual_function(mod, // N
363 la.data(), sa, a_sl, // res
364 la.data(), sa, a_sl // a
365 );
366 for (uint64_t i = 0; i < sa; ++i) {
367 ASSERT_EQ(la.get_copy_zext(i), expect[i]) << n << " " << sa << " " << i;
368 }
369 }
370 }
371 delete_rnx_module_info(mod);
372 }
373}
374template <typename ACTUAL_FCN, typename EXPECT_FCN>
375void test_vec_rnx_elemw_unop(ACTUAL_FCN unnop, EXPECT_FCN ref_unnop) {
376 test_vec_rnx_elemw_unop_outplace(unnop, ref_unnop);
377 test_vec_rnx_elemw_unop_inplace(unnop, ref_unnop);
378}
379
380static rnx_f64 poly_copy(const rnx_f64& a) { return a; }
381static rnx_f64 poly_negate(const rnx_f64& a) { return -a; }
382
383TEST(vec_rnx, vec_rnx_copy) { test_vec_rnx_elemw_unop(vec_rnx_copy, poly_copy); }
384TEST(vec_rnx, vec_rnx_copy_ref) { test_vec_rnx_elemw_unop(vec_rnx_copy_ref, poly_copy); }
385TEST(vec_rnx, vec_rnx_negate) { test_vec_rnx_elemw_unop(vec_rnx_negate, poly_negate); }
386TEST(vec_rnx, vec_rnx_negate_ref) { test_vec_rnx_elemw_unop(vec_rnx_negate_ref, poly_negate); }
387#ifdef __x86_64__
388TEST(vec_rnx, vec_rnx_negate_avx) { test_vec_rnx_elemw_unop(vec_rnx_negate_avx, poly_negate); }
389#endif
390
391// test for inplace calls
392void test_vec_rnx_zero(VEC_RNX_ZERO_F actual_function) {
393 for (uint64_t n : {2, 16, 1024}) {
394 MOD_RNX* mod = new_rnx_module_info(n, FFT64);
395 for (uint64_t sa : {2, 6, 11}) {
396 {
397 uint64_t a_sl = uniform_u64_bits(3) * 5 + n;
398 rnx_vec_f64_layout la(n, sa, a_sl);
399 const rnx_f64 ZERO = rnx_f64::zero(n);
400 for (uint64_t i = 0; i < sa; ++i) {
401 la.set(i, rnx_f64::random_log2bound(n, 0));
402 }
403 actual_function(mod, // N
404 la.data(), sa, a_sl // res
405 );
406 for (uint64_t i = 0; i < sa; ++i) {
407 ASSERT_EQ(la.get_copy_zext(i), ZERO) << n << " " << sa << " " << i;
408 }
409 }
410 }
411 delete_rnx_module_info(mod);
412 }
413}
414
415TEST(vec_rnx, vec_rnx_zero) { test_vec_rnx_zero(vec_rnx_zero); }
416
417TEST(vec_rnx, vec_rnx_zero_ref) { test_vec_rnx_zero(vec_rnx_zero_ref); }