poulpy/core/src/blind_rotation/lut.rs
core/src/blind_rotation/lut.rsRaw
1use backend::hal::{
2 api::{
3 ScratchOwnedAlloc, ScratchOwnedBorrow, VecZnxAlloc, VecZnxCopy, VecZnxNormalizeInplace, VecZnxNormalizeTmpBytes,
4 VecZnxRotateInplace, VecZnxSwithcDegree, ZnxInfos, ZnxViewMut,
5 },
6 layouts::{Backend, Module, ScratchOwned, VecZnx},
7 oep::{ScratchOwnedAllocImpl, ScratchOwnedBorrowImpl},
8};
9
10pub struct LookUpTable {
11 pub(crate) data: Vec<VecZnx<Vec<u8>>>,
12 pub(crate) basek: usize,
13 pub(crate) k: usize,
14}
15
16impl LookUpTable {
17 pub fn alloc<B: Backend>(module: &Module<B>, basek: usize, k: usize, extension_factor: usize) -> Self
18 where
19 Module<B>: VecZnxAlloc,
20 {
21 #[cfg(debug_assertions)]
22 {
23 assert!(
24 extension_factor & (extension_factor - 1) == 0,
25 "extension_factor must be a power of two but is: {}",
26 extension_factor
27 );
28 }
29 let size: usize = k.div_ceil(basek);
30 let mut data: Vec<VecZnx<Vec<u8>>> = Vec::with_capacity(extension_factor);
31 (0..extension_factor).for_each(|_| {
32 data.push(module.vec_znx_alloc(1, size));
33 });
34 Self { data, basek, k }
35 }
36
37 pub fn log_extension_factor(&self) -> usize {
38 (usize::BITS - (self.extension_factor() - 1).leading_zeros()) as _
39 }
40
41 pub fn extension_factor(&self) -> usize {
42 self.data.len()
43 }
44
45 pub fn domain_size(&self) -> usize {
46 self.data.len() * self.data[0].n()
47 }
48
49 pub fn set<B: Backend>(&mut self, module: &Module<B>, f: &Vec<i64>, k: usize)
50 where
51 Module<B>: VecZnxRotateInplace + VecZnxNormalizeInplace<B> + VecZnxNormalizeTmpBytes + VecZnxSwithcDegree + VecZnxCopy,
52 B: ScratchOwnedAllocImpl<B> + ScratchOwnedBorrowImpl<B>,
53 {
54 assert!(f.len() <= module.n());
55
56 let basek: usize = self.basek;
57
58 // Get the number minimum limb to store the message modulus
59 let limbs: usize = k.div_ceil(1 << basek);
60
61 #[cfg(debug_assertions)]
62 {
63 assert!(limbs <= self.data[0].size());
64 }
65
66 // Scaling factor
67 let scale: i64 = 1 << (k % basek) as i64;
68
69 // #elements in lookup table
70 let f_len: usize = f.len();
71
72 // If LUT size > module.n()
73 let domain_size: usize = self.domain_size();
74
75 let size: usize = self.k.div_ceil(self.basek);
76
77 // Equivalent to AUTO([f(0), -f(n-1), -f(n-2), ..., -f(1)], -1)
78 let mut lut_full: VecZnx<Vec<u8>> = VecZnx::alloc::<i64>(domain_size, 1, size);
79
80 let lut_at: &mut [i64] = lut_full.at_mut(0, limbs - 1);
81
82 f.iter().enumerate().for_each(|(i, fi)| {
83 let start: usize = (i * domain_size).div_round(f_len);
84 let end: usize = ((i + 1) * domain_size).div_round(f_len);
85 lut_at[start..end].fill(fi * scale);
86 });
87
88 // Rotates half the step to the left
89 let half_step: usize = domain_size.div_round(f_len << 1);
90
91 module.vec_znx_rotate_inplace(-(half_step as i64), &mut lut_full, 0);
92
93 let n_large: usize = lut_full.n();
94
95 module.vec_znx_normalize_inplace(
96 self.basek,
97 &mut lut_full,
98 0,
99 ScratchOwned::alloc(module.vec_znx_normalize_tmp_bytes(n_large)).borrow(),
100 );
101
102 if self.extension_factor() > 1 {
103 (0..self.extension_factor()).for_each(|i| {
104 module.vec_znx_switch_degree(&mut self.data[i], 0, &lut_full, 0);
105 if i < self.extension_factor() {
106 module.vec_znx_rotate_inplace(-1, &mut lut_full, 0);
107 }
108 });
109 } else {
110 module.vec_znx_copy(&mut self.data[0], 0, &lut_full, 0);
111 }
112 }
113
114 #[allow(dead_code)]
115 pub(crate) fn rotate<B: Backend>(&mut self, module: &Module<B>, k: i64)
116 where
117 Module<B>: VecZnxRotateInplace,
118 {
119 let extension_factor: usize = self.extension_factor();
120 let two_n: usize = 2 * self.data[0].n();
121 let two_n_ext: usize = two_n * extension_factor;
122
123 let k_pos: usize = ((k + two_n_ext as i64) % two_n_ext as i64) as usize;
124
125 let k_hi: usize = k_pos / extension_factor;
126 let k_lo: usize = k_pos % extension_factor;
127
128 (0..extension_factor - k_lo).for_each(|i| {
129 module.vec_znx_rotate_inplace(k_hi as i64, &mut self.data[i], 0);
130 });
131
132 (extension_factor - k_lo..extension_factor).for_each(|i| {
133 module.vec_znx_rotate_inplace(k_hi as i64 + 1, &mut self.data[i], 0);
134 });
135
136 self.data.rotate_right(k_lo as usize);
137 }
138}
139
140pub(crate) trait DivRound {
141 fn div_round(self, rhs: Self) -> Self;
142}
143
144impl DivRound for usize {
145 #[inline]
146 fn div_round(self, rhs: Self) -> Self {
147 (self + rhs / 2) / rhs
148 }
149}