1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
use crate::core_crypto::gpu::CudaStreams;
use crate::core_crypto::prelude::LweBskGroupingFactor;
use crate::integer::gpu::ciphertext::CudaIntegerRadixCiphertext;
use crate::integer::gpu::server_key::{
CudaBootstrappingKey, CudaDynamicKeyswitchingKey, CudaServerKey,
};
use crate::integer::gpu::{cuda_backend_unchecked_signed_abs_assign, PBSType};
impl CudaServerKey {
pub fn unchecked_abs_assign<T>(&self, ct: &mut T, streams: &CudaStreams)
where
T: CudaIntegerRadixCiphertext,
{
let num_blocks = ct.as_ref().d_blocks.lwe_ciphertext_count().0 as u32;
let CudaDynamicKeyswitchingKey::Standard(computing_ks_key) = &self.key_switching_key else {
panic!("Only the standard atomic pattern is supported on GPU")
};
unsafe {
match &self.bootstrapping_key {
CudaBootstrappingKey::Classic(d_bsk) => {
cuda_backend_unchecked_signed_abs_assign(
streams,
ct.as_mut(),
&d_bsk.d_vec,
&computing_ks_key.d_vec,
self.message_modulus,
self.carry_modulus,
d_bsk.glwe_dimension,
d_bsk.polynomial_size,
computing_ks_key.input_key_lwe_size().to_lwe_dimension(),
computing_ks_key.output_key_lwe_size().to_lwe_dimension(),
computing_ks_key.decomposition_level_count(),
computing_ks_key.decomposition_base_log(),
d_bsk.decomp_level_count,
d_bsk.decomp_base_log,
num_blocks,
PBSType::Classical,
LweBskGroupingFactor(0),
d_bsk.ms_noise_reduction_configuration.as_ref(),
);
}
CudaBootstrappingKey::MultiBit(d_multibit_bsk) => {
cuda_backend_unchecked_signed_abs_assign(
streams,
ct.as_mut(),
&d_multibit_bsk.d_vec,
&computing_ks_key.d_vec,
self.message_modulus,
self.carry_modulus,
d_multibit_bsk.glwe_dimension,
d_multibit_bsk.polynomial_size,
computing_ks_key.input_key_lwe_size().to_lwe_dimension(),
computing_ks_key.output_key_lwe_size().to_lwe_dimension(),
computing_ks_key.decomposition_level_count(),
computing_ks_key.decomposition_base_log(),
d_multibit_bsk.decomp_level_count,
d_multibit_bsk.decomp_base_log,
num_blocks,
PBSType::MultiBit,
d_multibit_bsk.grouping_factor,
None,
);
}
}
}
}
pub fn unchecked_abs<T>(&self, ct: &T, streams: &CudaStreams) -> T
where
T: CudaIntegerRadixCiphertext,
{
let mut res = ct.duplicate(streams);
if T::IS_SIGNED {
self.unchecked_abs_assign(&mut res, streams);
}
res
}
/// Computes homomorphically an absolute value of ciphertext encrypting integer
/// values.
///
/// This function, like all "default" operations (i.e. not smart, checked or unchecked), will
/// check that the input ciphertext block carries are empty and clears them if it's not the
/// case and the operation requires it. It outputs a ciphertext whose block carries are always
/// empty.
///
/// # Warning
///
/// - Multithreaded
///
/// # Example
///
/// ```rust
/// use tfhe::core_crypto::gpu::CudaStreams;
/// use tfhe::core_crypto::gpu::vec::GpuIndex;
/// use tfhe::integer::gpu::ciphertext::CudaSignedRadixCiphertext;
/// use tfhe::integer::gpu::gen_keys_radix_gpu;
/// use tfhe::shortint::parameters::PARAM_GPU_MULTI_BIT_GROUP_4_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128;
///
/// let gpu_index = 0;
/// let streams = CudaStreams::new_single_gpu(GpuIndex::new(gpu_index));
///
/// // Generate the client key and the server key:
/// let num_blocks = 4;
/// let (cks, sks) = gen_keys_radix_gpu(PARAM_GPU_MULTI_BIT_GROUP_4_MESSAGE_2_CARRY_2_KS_PBS_TUNIFORM_2M128, num_blocks, &streams);
///
/// let msg = -14i32;
///
/// let ct = cks.encrypt_signed(msg);
///
/// // Copy to GPU
/// let d_ct = CudaSignedRadixCiphertext::from_signed_radix_ciphertext(&ct, &streams);
///
/// // Compute homomorphically an absolute value:
/// let d_ct_res = sks.abs(&d_ct, &streams);
///
/// let ct_res = d_ct_res.to_signed_radix_ciphertext(&streams);
///
/// // Decrypt:
/// let dec_result: i32 = cks.decrypt_signed(&ct_res);
///
/// let abs_msg = if msg < 0 { -msg } else { msg };
/// assert_eq!(dec_result, abs_msg );
/// ```
pub fn abs<T>(&self, ct: &T, streams: &CudaStreams) -> T
where
T: CudaIntegerRadixCiphertext,
{
let mut res = ct.duplicate(streams);
if !ct.block_carries_are_empty() {
self.full_propagate_assign(&mut res, streams);
}
if T::IS_SIGNED {
self.unchecked_abs_assign(&mut res, streams);
}
res
}
}