concrete_integer/server_key/radix_parallel/mul.rs
1use std::sync::Mutex;
2
3use crate::ciphertext::RadixCiphertext;
4use crate::ServerKey;
5use rayon::prelude::*;
6
7impl ServerKey {
8 /// Computes homomorphically a multiplication between a ciphertext encrypting an integer value
9 /// and another encrypting a shortint value.
10 ///
11 /// This function computes the operation without checking if it exceeds the capacity of the
12 /// ciphertext.
13 ///
14 /// The result is assigned to the `ct_left` ciphertext.
15 ///
16 /// # Warning
17 ///
18 /// - Multithreaded
19 ///
20 /// # Example
21 ///
22 ///```rust
23 /// use concrete_integer::gen_keys_radix;
24 /// use concrete_shortint::parameters::PARAM_MESSAGE_2_CARRY_2;
25 ///
26 /// // Generate the client key and the server key:
27 /// let num_blocks = 4;
28 /// let (cks, sks) = gen_keys_radix(&PARAM_MESSAGE_2_CARRY_2, num_blocks);
29 ///
30 /// let clear_1 = 170;
31 /// let clear_2 = 3;
32 ///
33 /// // Encrypt two messages
34 /// let mut ct_left = cks.encrypt(clear_1);
35 /// let ct_right = cks.encrypt_one_block(clear_2);
36 ///
37 /// // Compute homomorphically a multiplication
38 /// sks.unchecked_block_mul_assign_parallelized(&mut ct_left, &ct_right, 0);
39 ///
40 /// // Decrypt
41 /// let res = cks.decrypt(&ct_left);
42 /// assert_eq!((clear_1 * clear_2) % 256, res);
43 /// ```
44 pub fn unchecked_block_mul_assign_parallelized(
45 &self,
46 ct_left: &mut RadixCiphertext,
47 ct_right: &concrete_shortint::Ciphertext,
48 index: usize,
49 ) {
50 *ct_left = self.unchecked_block_mul_parallelized(ct_left, ct_right, index);
51 }
52
53 /// Computes homomorphically a multiplication between a ciphertexts encrypting an integer
54 /// value and another encrypting a shortint value.
55 ///
56 /// This function computes the operation without checking if it exceeds the capacity of the
57 /// ciphertext.
58 ///
59 /// The result is returned as a new ciphertext.
60 ///
61 /// # Warning
62 ///
63 /// - Multithreaded
64 ///
65 /// # Example
66 ///
67 ///```rust
68 /// use concrete_integer::gen_keys_radix;
69 /// use concrete_shortint::parameters::PARAM_MESSAGE_2_CARRY_2;
70 ///
71 /// // Generate the client key and the server key:
72 /// let num_blocks = 4;
73 /// let (cks, sks) = gen_keys_radix(&PARAM_MESSAGE_2_CARRY_2, num_blocks);
74 ///
75 /// let clear_1 = 55;
76 /// let clear_2 = 3;
77 ///
78 /// // Encrypt two messages
79 /// let ct_left = cks.encrypt(clear_1);
80 /// let ct_right = cks.encrypt_one_block(clear_2);
81 ///
82 /// // Compute homomorphically a multiplication
83 /// let ct_res = sks.unchecked_block_mul_parallelized(&ct_left, &ct_right, 0);
84 ///
85 /// // Decrypt
86 /// let res = cks.decrypt(&ct_res);
87 /// assert_eq!((clear_1 * clear_2) % 256, res);
88 /// ```
89 pub fn unchecked_block_mul_parallelized(
90 &self,
91 ct1: &RadixCiphertext,
92 ct2: &concrete_shortint::Ciphertext,
93 index: usize,
94 ) -> RadixCiphertext {
95 let shifted_ct = self.blockshift(ct1, index);
96
97 let mut result_lsb = shifted_ct.clone();
98 let mut result_msb = shifted_ct;
99 self.unchecked_block_mul_lsb_msb_parallelized(&mut result_lsb, &mut result_msb, ct2, index);
100 result_msb = self.blockshift(&result_msb, 1);
101
102 self.unchecked_add(&result_lsb, &result_msb)
103 }
104
105 /// Computes homomorphically a multiplication between a ciphertext encrypting integer value
106 /// and another encrypting a shortint value.
107 ///
108 /// The result is returned as a new ciphertext.
109 ///
110 /// # Warning
111 ///
112 /// - Multithreaded
113 ///
114 /// # Example
115 ///
116 ///```rust
117 /// use concrete_integer::gen_keys_radix;
118 /// use concrete_shortint::parameters::PARAM_MESSAGE_2_CARRY_2;
119 ///
120 /// // Generate the client key and the server key:
121 /// let num_blocks = 4;
122 /// let (cks, sks) = gen_keys_radix(&PARAM_MESSAGE_2_CARRY_2, num_blocks);
123 ///
124 /// let clear_1 = 170;
125 /// let clear_2 = 3;
126 ///
127 /// // Encrypt two messages
128 /// let mut ctxt_1 = cks.encrypt(clear_1);
129 /// let ctxt_2 = cks.encrypt_one_block(clear_2);
130 ///
131 /// // Compute homomorphically a multiplication
132 /// let ct_res = sks.smart_block_mul_parallelized(&mut ctxt_1, &ctxt_2, 0);
133 ///
134 /// // Decrypt
135 /// let res = cks.decrypt(&ct_res);
136 /// assert_eq!((clear_1 * clear_2) % 256, res);
137 /// ```
138 pub fn smart_block_mul_parallelized(
139 &self,
140 ct1: &mut RadixCiphertext,
141 ct2: &concrete_shortint::Ciphertext,
142 index: usize,
143 ) -> RadixCiphertext {
144 //Makes sure we can do the multiplications
145 self.full_propagate_parallelized(ct1);
146
147 let shifted_ct = self.blockshift(ct1, index);
148
149 let mut result_lsb = shifted_ct.clone();
150 let mut result_msb = shifted_ct;
151 self.unchecked_block_mul_lsb_msb_parallelized(&mut result_lsb, &mut result_msb, ct2, index);
152 result_msb = self.blockshift(&result_msb, 1);
153
154 self.smart_add_parallelized(&mut result_lsb, &mut result_msb)
155 }
156
157 fn unchecked_block_mul_lsb_msb_parallelized(
158 &self,
159 result_lsb: &mut RadixCiphertext,
160 result_msb: &mut RadixCiphertext,
161 ct2: &concrete_shortint::Ciphertext,
162 index: usize,
163 ) {
164 let len = result_msb.blocks.len() - 1;
165 rayon::join(
166 || {
167 result_lsb.blocks[index..]
168 .par_iter_mut()
169 .for_each(|res_lsb_i| {
170 self.key.unchecked_mul_lsb_assign(res_lsb_i, ct2);
171 });
172 },
173 || {
174 result_msb.blocks[index..len]
175 .par_iter_mut()
176 .for_each(|res_msb_i| {
177 self.key.unchecked_mul_msb_assign(res_msb_i, ct2);
178 });
179 },
180 );
181 }
182
183 pub fn smart_block_mul_assign_parallelized(
184 &self,
185 ct1: &mut RadixCiphertext,
186 ct2: &concrete_shortint::Ciphertext,
187 index: usize,
188 ) {
189 *ct1 = self.smart_block_mul_parallelized(ct1, ct2, index);
190 }
191
192 /// Computes homomorphically a multiplication between two ciphertexts encrypting integer values.
193 ///
194 /// This function computes the operation without checking if it exceeds the capacity of the
195 /// ciphertext.
196 ///
197 /// The result is assigned to the `ct_left` ciphertext.
198 ///
199 /// # Warning
200 ///
201 /// - Multithreaded
202 ///
203 /// # Example
204 ///
205 /// ```rust
206 /// use concrete_integer::gen_keys_radix;
207 /// use concrete_shortint::parameters::PARAM_MESSAGE_2_CARRY_2;
208 ///
209 /// // Generate the client key and the server key:
210 /// let num_blocks = 4;
211 /// let (cks, sks) = gen_keys_radix(&PARAM_MESSAGE_2_CARRY_2, num_blocks);
212 ///
213 /// let clear_1 = 255;
214 /// let clear_2 = 143;
215 ///
216 /// // Encrypt two messages
217 /// let mut ctxt_1 = cks.encrypt(clear_1);
218 /// let ctxt_2 = cks.encrypt(clear_2);
219 ///
220 /// // Compute homomorphically a multiplication
221 /// let ct_res = sks.unchecked_mul_parallelized(&mut ctxt_1, &ctxt_2);
222 ///
223 /// // Decrypt
224 /// let res = cks.decrypt(&ct_res);
225 /// assert_eq!((clear_1 * clear_2) % 256, res);
226 /// ```
227 pub fn unchecked_mul_assign_parallelized(
228 &self,
229 ct1: &mut RadixCiphertext,
230 ct2: &RadixCiphertext,
231 ) {
232 *ct1 = self.unchecked_mul_parallelized(ct1, ct2);
233 }
234
235 /// Computes homomorphically a multiplication between two ciphertexts encrypting integer values.
236 ///
237 /// This function computes the operation without checking if it exceeds the capacity of the
238 /// ciphertext.
239 ///
240 /// The result is returned as a new ciphertext.
241 ///
242 /// # Warning
243 ///
244 /// - Multithreaded
245 pub fn unchecked_mul_parallelized(
246 &self,
247 ct1: &mut RadixCiphertext,
248 ct2: &RadixCiphertext,
249 ) -> RadixCiphertext {
250 let mut result = self.create_trivial_zero_radix(ct1.blocks.len());
251
252 let terms = Mutex::new(Vec::new());
253
254 ct2.blocks.par_iter().enumerate().for_each(|(i, ct2_i)| {
255 let term = self.unchecked_block_mul_parallelized(ct1, ct2_i, i);
256 terms.lock().unwrap().push(term);
257 });
258
259 let terms = terms.into_inner().unwrap();
260
261 for term in terms {
262 self.unchecked_add_assign(&mut result, &term);
263 }
264
265 result
266 }
267
268 /// Computes homomorphically a multiplication between two ciphertexts encrypting integer values.
269 ///
270 /// The result is assigned to the `ct_left` ciphertext.
271 ///
272 /// # Warning
273 ///
274 /// - Multithreaded
275 ///
276 /// # Example
277 ///
278 /// ```rust
279 /// use concrete_integer::gen_keys_radix;
280 /// use concrete_shortint::parameters::PARAM_MESSAGE_2_CARRY_2;
281 ///
282 /// // Generate the client key and the server key:
283 /// let num_blocks = 4;
284 /// let (cks, sks) = gen_keys_radix(&PARAM_MESSAGE_2_CARRY_2, num_blocks);
285 ///
286 /// let clear_1 = 170;
287 /// let clear_2 = 6;
288 ///
289 /// // Encrypt two messages
290 /// let mut ctxt_1 = cks.encrypt(clear_1);
291 /// let mut ctxt_2 = cks.encrypt(clear_2);
292 ///
293 /// // Compute homomorphically a multiplication
294 /// let ct_res = sks.smart_mul_parallelized(&mut ctxt_1, &mut ctxt_2);
295 /// // Decrypt
296 /// let res = cks.decrypt(&ct_res);
297 /// assert_eq!((clear_1 * clear_2) % 256, res);
298 /// ```
299 pub fn smart_mul_assign_parallelized(
300 &self,
301 ct1: &mut RadixCiphertext,
302 ct2: &mut RadixCiphertext,
303 ) {
304 *ct1 = self.smart_mul_parallelized(ct1, ct2);
305 }
306
307 /// Computes homomorphically a multiplication between two ciphertexts encrypting integer values.
308 ///
309 /// The result is returned as a new ciphertext.
310 ///
311 /// # Warning
312 ///
313 /// - Multithreaded
314 pub fn smart_mul_parallelized(
315 &self,
316 ct1: &mut RadixCiphertext,
317 ct2: &mut RadixCiphertext,
318 ) -> RadixCiphertext {
319 rayon::join(
320 || self.full_propagate_parallelized(ct1),
321 || self.full_propagate_parallelized(ct2),
322 );
323
324 let terms = Mutex::new(Vec::new());
325 ct2.blocks.par_iter().enumerate().for_each(|(i, ct2_i)| {
326 let term = self.unchecked_block_mul_parallelized(ct1, ct2_i, i);
327 terms.lock().unwrap().push(term);
328 });
329 let mut terms = terms.into_inner().unwrap();
330
331 self.smart_binary_op_seq_parallelized(&mut terms, ServerKey::smart_add_parallelized)
332 .unwrap_or_else(|| self.create_trivial_zero_radix(ct1.blocks.len()))
333 }
334}