poulpy/poulpy-schemes/src/tfhe/bdd_arithmetic/blind_retrieval.rs
poulpy-schemes/src/tfhe/bdd_arithmetic/blind_retrieval.rsRaw
1use itertools::Itertools;
2use poulpy_core::{
3 GLWECopy, ScratchTakeCore,
4 layouts::{GGSWInfos, GGSWPrepared, GLWE, GLWEInfos, GLWEToMut, GLWEToRef},
5};
6use poulpy_hal::layouts::{Backend, Module, Scratch};
7
8use crate::tfhe::bdd_arithmetic::{Cmux, Cswap, GetGGSWBit};
9
10pub struct GLWEBlindRetriever {
11 accumulators: Vec<Accumulator>,
12 counter: usize,
13}
14
15impl GLWEBlindRetriever {
16 pub fn alloc<A>(infos: &A, size: usize) -> Self
17 where
18 A: GLWEInfos,
19 {
20 let log2_max_address: usize = (u32::BITS - (size as u32 - 1).leading_zeros()) as usize;
21 Self {
22 accumulators: (0..log2_max_address)
23 .map(|_| Accumulator::alloc(infos))
24 .collect_vec(),
25 counter: 0,
26 }
27 }
28
29 pub fn retrieve<M, R, A, S, BE: Backend>(
30 &mut self,
31 module: &M,
32 res: &mut R,
33 data: &[A],
34 selector: &S,
35 scratch: &mut Scratch<BE>,
36 ) where
37 M: GLWECopy + Cmux<BE>,
38 R: GLWEToMut,
39 A: GLWEToRef,
40 S: GetGGSWBit<BE>,
41 Scratch<BE>: ScratchTakeCore<BE>,
42 {
43 self.reset();
44
45 for ct in data {
46 self.add(module, ct, selector, scratch);
47 }
48 self.flush(module, res, selector, scratch);
49 }
50
51 pub fn add<A, S, M, BE: Backend>(&mut self, module: &M, a: &A, selector: &S, scratch: &mut Scratch<BE>)
52 where
53 A: GLWEToRef,
54 S: GetGGSWBit<BE>,
55 M: GLWECopy + Cmux<BE>,
56 Scratch<BE>: ScratchTakeCore<BE>,
57 {
58 assert!(
59 (self.counter as u32) < 1 << self.accumulators.len(),
60 "Accumulating limit of {} reached",
61 1 << self.accumulators.len()
62 );
63
64 add_core(module, a, &mut self.accumulators, 0, selector, scratch);
65 self.counter += 1;
66 }
67
68 pub fn flush<R, M, S, BE: Backend>(&mut self, module: &M, res: &mut R, selector: &S, scratch: &mut Scratch<BE>)
69 where
70 R: GLWEToMut,
71 S: GetGGSWBit<BE>,
72 M: GLWECopy + Cmux<BE>,
73 Scratch<BE>: ScratchTakeCore<BE>,
74 {
75 for i in 0..self.accumulators.len() - 1 {
76 let (acc_prev, acc_next) = self.accumulators.split_at_mut(i + 1);
77 if acc_prev[i].num != 0 {
78 add_core(
79 module,
80 &acc_prev[i].data,
81 acc_next,
82 i + 1,
83 selector,
84 scratch,
85 );
86 acc_prev[0].num = 0
87 }
88 }
89 module.glwe_copy(res, &self.accumulators.last().unwrap().data);
90 self.reset()
91 }
92
93 fn reset(&mut self) {
94 for acc in self.accumulators.iter_mut() {
95 acc.num = 0;
96 }
97 }
98}
99
100struct Accumulator {
101 data: GLWE<Vec<u8>>,
102 num: usize, // Number of accumulated values
103}
104
105impl Accumulator {
106 pub fn alloc<A>(infos: &A) -> Self
107 where
108 A: GLWEInfos,
109 {
110 Self {
111 data: GLWE::alloc_from_infos(infos),
112 num: 0,
113 }
114 }
115}
116
117fn add_core<A, S, M, BE: Backend>(
118 module: &M,
119 a: &A,
120 accumulators: &mut [Accumulator],
121 i: usize,
122 selector: &S,
123 scratch: &mut Scratch<BE>,
124) where
125 A: GLWEToRef,
126 S: GetGGSWBit<BE>,
127 M: GLWECopy + Cmux<BE>,
128 Scratch<BE>: ScratchTakeCore<BE>,
129{
130 // Isolate the first accumulator
131 let (acc_prev, acc_next) = accumulators.split_at_mut(1);
132
133 match acc_prev[0].num {
134 0 => {
135 module.glwe_copy(&mut acc_prev[0].data, a);
136 acc_prev[0].num = 1;
137 }
138 1 => {
139 module.cmux_inplace_neg(&mut acc_prev[0].data, a, &selector.get_bit(i), scratch);
140
141 if !acc_next.is_empty() {
142 add_core(
143 module,
144 &acc_prev[0].data,
145 acc_next,
146 i + 1,
147 selector,
148 scratch,
149 );
150 }
151
152 acc_prev[0].num = 0
153 }
154 _ => {
155 panic!("something went wrong")
156 }
157 }
158}
159
160impl<BE: Backend> GLWEBlindRetrieval<BE> for Module<BE> where Self: GLWECopy + Cmux<BE> + Cswap<BE> {}
161
162pub trait GLWEBlindRetrieval<BE: Backend>
163where
164 Self: GLWECopy + Cmux<BE> + Cswap<BE>,
165{
166 fn glwe_blind_retrieval_tmp_bytes<R, K>(&self, res_infos: &R, k_infos: &K) -> usize
167 where
168 R: GLWEInfos,
169 K: GGSWInfos,
170 {
171 self.cswap_tmp_bytes(res_infos, res_infos, k_infos)
172 }
173
174 fn glwe_blind_retrieval_statefull<R, K>(
175 &self,
176 res: &mut Vec<R>,
177 bits: &K,
178 bit_rsh: usize,
179 bit_mask: usize,
180 scratch: &mut Scratch<BE>,
181 ) where
182 R: GLWEToMut + GLWEInfos,
183 K: GetGGSWBit<BE>,
184 Scratch<BE>: ScratchTakeCore<BE>,
185 {
186 for i in 0..bit_mask {
187 let t: usize = 1 << (bit_mask - i - 1);
188 let bit: &GGSWPrepared<&[u8], BE> = &bits.get_bit(bit_rsh + bit_mask - i - 1); // MSB -> LSB traversal
189 for j in 0..t {
190 if j + t < res.len() {
191 let (lo, hi) = res.split_at_mut(j + t);
192 self.cswap(&mut lo[j], &mut hi[0], bit, scratch);
193 }
194 }
195 }
196 }
197
198 fn glwe_blind_retrieval_statefull_rev<R, K>(
199 &self,
200 res: &mut Vec<R>,
201 bits: &K,
202 bit_rsh: usize,
203 bit_mask: usize,
204 scratch: &mut Scratch<BE>,
205 ) where
206 R: GLWEToMut + GLWEInfos,
207 K: GetGGSWBit<BE>,
208 Scratch<BE>: ScratchTakeCore<BE>,
209 {
210 for i in (0..bit_mask).rev() {
211 let t: usize = 1 << (bit_mask - i - 1);
212 let bit: &GGSWPrepared<&[u8], BE> = &bits.get_bit(bit_rsh + bit_mask - i - 1); // MSB -> LSB traversal
213 for j in 0..t {
214 if j < res.len() && j + t < res.len() {
215 let (lo, hi) = res.split_at_mut(j + t);
216 self.cswap(&mut lo[j], &mut hi[0], bit, scratch);
217 }
218 }
219 }
220 }
221}