concrete_integer/server_key/multi_crt/mod.rs
1#[cfg(test)]
2mod test;
3
4use crate::ciphertext::KeyId;
5use crate::{CrtMultiCiphertext, CrtMultiClientKey};
6use concrete_shortint::server_key::MaxDegree;
7use rayon::prelude::*;
8use serde::{Deserialize, Serialize};
9
10#[derive(Serialize, Deserialize, Debug, Clone)]
11pub struct CrtMultiServerKey {
12 pub(crate) keys: Vec<concrete_shortint::server_key::ServerKey>,
13 pub(crate) key_ids: Vec<KeyId>,
14}
15
16impl From<Vec<concrete_shortint::ServerKey>> for CrtMultiServerKey {
17 fn from(keys: Vec<concrete_shortint::ServerKey>) -> Self {
18 let key_ids = (0..keys.len()).map(KeyId).collect();
19
20 Self { keys, key_ids }
21 }
22}
23
24impl CrtMultiServerKey {
25 /// Allocates and generates a server key.
26 ///
27 /// # Example
28 ///
29 /// ```rust
30 /// use concrete_integer::{CrtMultiClientKey, CrtMultiServerKey};
31 /// use concrete_shortint::parameters::{PARAM_MESSAGE_1_CARRY_1, PARAM_MESSAGE_2_CARRY_2};
32 ///
33 /// // Generate the client key:
34 /// let cks = CrtMultiClientKey::new_many_keys(&[PARAM_MESSAGE_1_CARRY_1, PARAM_MESSAGE_2_CARRY_2]);
35 /// let sks = CrtMultiServerKey::new_many_keys(&cks);
36 /// ```
37 pub fn new_many_keys(cks: &CrtMultiClientKey) -> CrtMultiServerKey {
38 let mut vec_sks = Vec::with_capacity(cks.keys.len());
39 let mut vec_id = Vec::with_capacity(cks.keys.len());
40
41 for (key, id) in cks.keys.iter().zip(cks.key_ids.iter()) {
42 let max = (key.parameters.message_modulus.0 - 1) * key.parameters.carry_modulus.0 - 1;
43
44 let sks = concrete_shortint::ServerKey::new_with_max_degree(key, MaxDegree(max));
45 vec_sks.push(sks);
46 vec_id.push(*id);
47 }
48 CrtMultiServerKey {
49 keys: vec_sks,
50 key_ids: vec_id,
51 }
52 }
53
54 /// Computes an homomorphic addition.
55 ///
56 /// # Example
57 ///
58 ///```rust
59 /// use concrete_integer::{gen_key_id, gen_keys_multi_crt};
60 /// use concrete_shortint::parameters::{PARAM_MESSAGE_2_CARRY_2, PARAM_MESSAGE_3_CARRY_1};
61 ///
62 /// // Generate the client key and the server key:
63 /// let (cks, sks) = gen_keys_multi_crt(&[PARAM_MESSAGE_2_CARRY_2, PARAM_MESSAGE_3_CARRY_1]);
64 ///
65 /// let clear_1 = 14;
66 /// let clear_2 = 11;
67 ///
68 /// let basis = vec![2, 3, 7];
69 /// let keys_id = gen_key_id(&[0, 0, 1]);
70 ///
71 /// // Encrypt two messages
72 /// let mut ctxt_1 = cks.encrypt(&clear_1, &basis, &keys_id);
73 /// let mut ctxt_2 = cks.encrypt(&clear_2, &basis, &keys_id);
74 ///
75 /// sks.unchecked_add_crt_many_keys_assign(&mut ctxt_1, &mut ctxt_2);
76 ///
77 /// // Decrypt
78 /// let res = cks.decrypt(&ctxt_1);
79 /// assert_eq!((clear_1 + clear_2) % 30, res);
80 /// ```
81 pub fn unchecked_add_crt_many_keys_assign(
82 &self,
83 ct_left: &mut CrtMultiCiphertext,
84 ct_right: &mut CrtMultiCiphertext,
85 ) {
86 for ((ct_left, ct_right), key_id) in ct_left
87 .blocks
88 .iter_mut()
89 .zip(ct_right.blocks.iter_mut())
90 .zip(ct_left.key_ids.iter())
91 {
92 self.keys[key_id.0].unchecked_add_assign(ct_left, ct_right);
93 }
94 }
95
96 /// Computes an homomorphic addition.
97 ///
98 /// # Warning
99 ///
100 /// Multithreaded
101 ///
102 /// # Example
103 ///
104 ///```rust
105 /// use concrete_integer::{gen_key_id, gen_keys_multi_crt};
106 /// use concrete_shortint::parameters::{PARAM_MESSAGE_2_CARRY_2, PARAM_MESSAGE_3_CARRY_1};
107 ///
108 /// // Generate the client key and the server key:
109 /// let (cks, sks) = gen_keys_multi_crt(&[PARAM_MESSAGE_2_CARRY_2, PARAM_MESSAGE_3_CARRY_1]);
110 ///
111 /// let clear_1 = 14;
112 /// let clear_2 = 11;
113 ///
114 /// let basis = vec![2, 3, 7];
115 /// let keys_id = gen_key_id(&[0, 0, 1]);
116 ///
117 /// // Encrypt two messages
118 /// let mut ctxt_1 = cks.encrypt(&clear_1, &basis, &keys_id);
119 /// let mut ctxt_2 = cks.encrypt(&clear_2, &basis, &keys_id);
120 ///
121 /// sks.unchecked_add_crt_many_keys_assign_parallelized(&mut ctxt_1, &mut ctxt_2);
122 ///
123 /// // Decrypt
124 /// let res = cks.decrypt(&ctxt_1);
125 /// assert_eq!((clear_1 + clear_2) % 30, res);
126 /// ```
127 pub fn unchecked_add_crt_many_keys_assign_parallelized(
128 &self,
129 ct_left: &mut CrtMultiCiphertext,
130 ct_right: &mut CrtMultiCiphertext,
131 ) {
132 ct_left
133 .blocks
134 .par_iter_mut()
135 .zip(ct_right.blocks.par_iter_mut())
136 .zip(ct_left.key_ids.par_iter())
137 .for_each(|((block_left, block_right), key_id)| {
138 let key = &self.keys[key_id.0];
139 key.unchecked_add_assign(block_left, block_right);
140 });
141 }
142
143 /// Computes an homomorphic multiplication.
144 ///
145 /// # Example
146 ///
147 ///```rust
148 /// use concrete_integer::{gen_key_id, gen_keys_multi_crt};
149 /// use concrete_shortint::parameters::{
150 /// PARAM_MESSAGE_1_CARRY_1, PARAM_MESSAGE_2_CARRY_2, PARAM_MESSAGE_3_CARRY_3,
151 /// };
152 ///
153 /// // Generate the client key and the server key:
154 /// let (cks, sks) = gen_keys_multi_crt(&vec![
155 /// PARAM_MESSAGE_1_CARRY_1,
156 /// PARAM_MESSAGE_2_CARRY_2,
157 /// PARAM_MESSAGE_3_CARRY_3,
158 /// ]);
159 ///
160 /// let clear_1 = 13;
161 /// let clear_2 = 11;
162 ///
163 /// let basis = vec![2, 3, 5];
164 /// let keys_id = gen_key_id(&[0, 1, 2]);
165 ///
166 /// // Encrypt two messages
167 /// let mut ctxt_1 = cks.encrypt(&clear_1, &basis, &keys_id);
168 /// let mut ctxt_2 = cks.encrypt(&clear_2, &basis, &keys_id);
169 ///
170 /// sks.unchecked_mul_crt_many_keys_assign(&mut ctxt_1, &mut ctxt_2);
171 ///
172 /// // Decrypt
173 /// let res = cks.decrypt(&ctxt_1);
174 /// assert_eq!((clear_1 * clear_2) % 30, res);
175 /// ```
176 pub fn unchecked_mul_crt_many_keys_assign(
177 &self,
178 ct_left: &mut CrtMultiCiphertext,
179 ct_right: &mut CrtMultiCiphertext,
180 ) {
181 for ((block_left, block_right), id) in ct_left
182 .blocks
183 .iter_mut()
184 .zip(ct_right.blocks.iter_mut())
185 .zip(ct_left.key_ids.iter())
186 {
187 self.keys[id.0].unchecked_mul_lsb_assign(block_left, block_right);
188 }
189 }
190
191 /// Computes an homomorphic multiplication.
192 ///
193 /// # Warning
194 ///
195 /// Multithreaded
196 ///
197 /// # Example
198 ///
199 ///```rust
200 /// use concrete_integer::{gen_key_id, gen_keys_multi_crt};
201 /// use concrete_shortint::parameters::{
202 /// PARAM_MESSAGE_1_CARRY_1, PARAM_MESSAGE_2_CARRY_2, PARAM_MESSAGE_3_CARRY_3,
203 /// };
204 ///
205 /// // Generate the client key and the server key:
206 /// let (cks, sks) = gen_keys_multi_crt(&vec![
207 /// PARAM_MESSAGE_1_CARRY_1,
208 /// PARAM_MESSAGE_2_CARRY_2,
209 /// PARAM_MESSAGE_3_CARRY_3,
210 /// ]);
211 ///
212 /// let clear_1 = 13;
213 /// let clear_2 = 11;
214 ///
215 /// let basis = vec![2, 3, 5];
216 /// let keys_id = gen_key_id(&[0, 1, 2]);
217 ///
218 /// // Encrypt two messages
219 /// let mut ctxt_1 = cks.encrypt(&clear_1, &basis, &keys_id);
220 /// let mut ctxt_2 = cks.encrypt(&clear_2, &basis, &keys_id);
221 ///
222 /// sks.unchecked_mul_crt_many_keys_assign_parallelized(&mut ctxt_1, &mut ctxt_2);
223 ///
224 /// // Decrypt
225 /// let res = cks.decrypt(&ctxt_1);
226 /// assert_eq!((clear_1 * clear_2) % 30, res);
227 /// ```
228 pub fn unchecked_mul_crt_many_keys_assign_parallelized(
229 &self,
230 ct_left: &mut CrtMultiCiphertext,
231 ct_right: &mut CrtMultiCiphertext,
232 ) {
233 ct_left
234 .blocks
235 .par_iter_mut()
236 .zip(ct_right.blocks.par_iter_mut())
237 .zip(ct_left.key_ids.par_iter())
238 .for_each(|((block_left, block_right), id)| {
239 self.keys[id.0].unchecked_mul_lsb_assign(block_left, block_right);
240 });
241 }
242
243 /// Computes an homomorphic evaluation of an CRT-compliant univariate function.
244 ///
245 /// # Warning The function has to be CRT-compliant.
246 ///
247 /// # Example
248 ///
249 ///```rust
250 /// use concrete_integer::{gen_key_id, gen_keys_multi_crt};
251 /// use concrete_shortint::parameters::{
252 /// PARAM_MESSAGE_1_CARRY_1, PARAM_MESSAGE_2_CARRY_2, PARAM_MESSAGE_3_CARRY_3,
253 /// };
254 ///
255 /// // Generate the client key and the server key:
256 /// let (cks, sks) = gen_keys_multi_crt(&vec![
257 /// PARAM_MESSAGE_1_CARRY_1,
258 /// PARAM_MESSAGE_2_CARRY_2,
259 /// PARAM_MESSAGE_3_CARRY_3,
260 /// ]);
261 ///
262 /// let clear_1 = 14;
263 /// let clear_2 = 11;
264 ///
265 /// let basis = vec![2, 3, 5];
266 /// let keys_id = gen_key_id(&[0, 1, 2]);
267 ///
268 /// // Encrypt two messages
269 /// let mut ctxt_1 = cks.encrypt(&clear_1, &basis, &keys_id);
270 /// let mut ctxt_2 = cks.encrypt(&clear_2, &basis, &keys_id);
271 ///
272 /// sks.arithmetic_function_crt_many_keys_assign(&mut ctxt_1, |x| x * x);
273 ///
274 /// // Decrypt
275 /// let res = cks.decrypt(&ctxt_1);
276 /// assert_eq!((clear_1 * clear_1) % 30, res);
277 /// ```
278 pub fn arithmetic_function_crt_many_keys_assign<F>(&self, ct: &mut CrtMultiCiphertext, f: F)
279 where
280 F: Fn(u64) -> u64,
281 {
282 let basis = &ct.moduli;
283 let keys_id = &ct.key_ids;
284 for ((block, key_id), basis) in ct.blocks.iter_mut().zip(keys_id.iter()).zip(basis.iter()) {
285 let acc = self.keys[key_id.0].generate_accumulator(|x| f(x) % basis);
286 self.keys[key_id.0].keyswitch_programmable_bootstrap_assign(block, &acc);
287 }
288 }
289
290 /// Computes an homomorphic evaluation of an CRT-compliant univariate function.
291 ///
292 /// # Warning
293 ///
294 /// - Multithreaded
295 /// - The function has to be CRT-compliant.
296 ///
297 /// # Example
298 ///
299 ///```rust
300 /// use concrete_integer::{gen_key_id, gen_keys_multi_crt};
301 /// use concrete_shortint::parameters::{
302 /// PARAM_MESSAGE_1_CARRY_1, PARAM_MESSAGE_2_CARRY_2, PARAM_MESSAGE_3_CARRY_3,
303 /// };
304 ///
305 /// // Generate the client key and the server key:
306 /// let (cks, sks) = gen_keys_multi_crt(&vec![
307 /// PARAM_MESSAGE_1_CARRY_1,
308 /// PARAM_MESSAGE_2_CARRY_2,
309 /// PARAM_MESSAGE_3_CARRY_3,
310 /// ]);
311 ///
312 /// let clear_1 = 14;
313 /// let clear_2 = 11;
314 ///
315 /// let basis = vec![2, 3, 5];
316 /// let keys_id = gen_key_id(&[0, 1, 2]);
317 ///
318 /// // Encrypt two messages
319 /// let mut ctxt_1 = cks.encrypt(&clear_1, &basis, &keys_id);
320 /// let mut ctxt_2 = cks.encrypt(&clear_2, &basis, &keys_id);
321 ///
322 /// sks.arithmetic_function_crt_many_keys_assign_parallelized(&mut ctxt_1, |x| x * x);
323 ///
324 /// // Decrypt
325 /// let res = cks.decrypt(&ctxt_1);
326 /// assert_eq!((clear_1 * clear_1) % 30, res);
327 /// ```
328 pub fn arithmetic_function_crt_many_keys_assign_parallelized<F>(
329 &self,
330 ct: &mut CrtMultiCiphertext,
331 f: F,
332 ) where
333 F: Fn(u64) -> u64,
334 {
335 let basis = &ct.moduli;
336 let keys_id = &ct.key_ids;
337
338 let accumulators = keys_id
339 .iter()
340 .zip(basis.iter())
341 .map(|(key_id, basis)| self.keys[key_id.0].generate_accumulator(|x| f(x) % basis))
342 .collect::<Vec<_>>();
343
344 ct.blocks
345 .par_iter_mut()
346 .zip(keys_id)
347 .zip(&accumulators)
348 .for_each(|((block, key_id), acc)| {
349 self.keys[key_id.0].keyswitch_programmable_bootstrap_assign(block, acc);
350 });
351 }
352}