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}