tfhe/high_level_api/array/dynamic/
booleans.rs1use super::super::cpu::{CpuFheBoolArrayBackend, FheBoolId};
4use super::super::helpers::{create_sub_mut_slice_with_bound, create_sub_slice_with_bound};
5use super::super::traits::{
6 ArrayBackend, BackendDataContainer, BackendDataContainerMut, BitwiseArrayBackend,
7 ClearBitwiseArrayBackend,
8};
9use super::super::{FheBackendArray, FheBackendArraySlice, FheBackendArraySliceMut};
10
11use crate::array::traits::TensorSlice;
12use crate::integer::BooleanBlock;
13use crate::prelude::{FheDecrypt, FheTryEncrypt};
14use crate::{ClientKey, Device};
15use std::borrow::{Borrow, Cow};
16use std::ops::RangeBounds;
17
18pub type FheBoolArray = FheBackendArray<DynFheBoolArrayBackend, FheBoolId>;
19pub type FheBoolSlice<'a> = FheBackendArraySlice<'a, DynFheBoolArrayBackend, FheBoolId>;
20pub type FheBoolSliceMut<'a> = FheBackendArraySliceMut<'a, DynFheBoolArrayBackend, FheBoolId>;
21
22pub struct DynFheBoolArrayBackend;
23
24impl ArrayBackend for DynFheBoolArrayBackend {
25 type Slice<'a>
26 = InnerBoolSlice<'a>
27 where
28 Self: 'a;
29 type SliceMut<'a>
30 = InnerBoolSliceMut<'a>
31 where
32 Self: 'a;
33 type Owned = InnerBoolArray;
34}
35
36impl BitwiseArrayBackend for DynFheBoolArrayBackend {
37 fn bitand<'a>(
38 lhs: TensorSlice<'_, Self::Slice<'a>>,
39 rhs: TensorSlice<'_, Self::Slice<'a>>,
40 ) -> Self::Owned {
41 dispatch_binary_op(&lhs, &rhs, CpuFheBoolArrayBackend::bitand)
42 }
43
44 fn bitor<'a>(
45 lhs: TensorSlice<'_, Self::Slice<'a>>,
46 rhs: TensorSlice<'_, Self::Slice<'a>>,
47 ) -> Self::Owned {
48 dispatch_binary_op(&lhs, &rhs, CpuFheBoolArrayBackend::bitor)
49 }
50
51 fn bitxor<'a>(
52 lhs: TensorSlice<'_, Self::Slice<'a>>,
53 rhs: TensorSlice<'_, Self::Slice<'a>>,
54 ) -> Self::Owned {
55 dispatch_binary_op(&lhs, &rhs, CpuFheBoolArrayBackend::bitxor)
56 }
57
58 fn bitnot(lhs: TensorSlice<'_, Self::Slice<'_>>) -> Self::Owned {
59 dispatch_unary_op(&lhs, CpuFheBoolArrayBackend::bitnot)
60 }
61}
62
63impl ClearBitwiseArrayBackend<bool> for DynFheBoolArrayBackend {
64 fn bitand_slice(
65 lhs: TensorSlice<'_, Self::Slice<'_>>,
66 rhs: TensorSlice<'_, &'_ [bool]>,
67 ) -> Self::Owned {
68 dispatch_binary_scalar_op(&lhs, &rhs, CpuFheBoolArrayBackend::bitand_slice)
69 }
70
71 fn bitor_slice(
72 lhs: TensorSlice<'_, Self::Slice<'_>>,
73 rhs: TensorSlice<'_, &'_ [bool]>,
74 ) -> Self::Owned {
75 dispatch_binary_scalar_op(&lhs, &rhs, CpuFheBoolArrayBackend::bitor_slice)
76 }
77
78 fn bitxor_slice(
79 lhs: TensorSlice<'_, Self::Slice<'_>>,
80 rhs: TensorSlice<'_, &'_ [bool]>,
81 ) -> Self::Owned {
82 dispatch_binary_scalar_op(&lhs, &rhs, CpuFheBoolArrayBackend::bitxor_slice)
83 }
84}
85
86#[inline]
87fn dispatch_binary_op<CpuFn>(
88 lhs: &TensorSlice<'_, InnerBoolSlice<'_>>,
89 rhs: &TensorSlice<'_, InnerBoolSlice<'_>>,
90 cpu_fn: CpuFn,
91) -> InnerBoolArray
92where
93 CpuFn: for<'a> Fn(
94 TensorSlice<'_, &'a [BooleanBlock]>,
95 TensorSlice<'_, &'a [BooleanBlock]>,
96 ) -> Vec<BooleanBlock>,
97{
98 match crate::high_level_api::global_state::device_of_internal_keys() {
99 Some(Device::Cpu) => {
100 let lhs_cpu_cow = lhs.slice.on_cpu();
101 let rhs_cpu_cow = rhs.slice.on_cpu();
102
103 let lhs_cpu_slice: &[BooleanBlock] = lhs_cpu_cow.borrow();
104 let rhs_cpu_slice: &[BooleanBlock] = rhs_cpu_cow.borrow();
105
106 let result = cpu_fn(
107 TensorSlice::new(lhs_cpu_slice, lhs.dims),
108 TensorSlice::new(rhs_cpu_slice, rhs.dims),
109 );
110 InnerBoolArray::Cpu(result)
111 }
112 #[cfg(feature = "gpu")]
113 Some(Device::CudaGpu) => {
114 panic!("Not supported by Cuda devices")
115 }
116 #[cfg(feature = "hpu")]
117 Some(Device::Hpu) => {
118 panic!("Not supported by Hpu devices")
119 }
120 None => {
121 panic!("{}", crate::high_level_api::errors::UninitializedServerKey);
122 }
123 }
124}
125
126#[inline]
127fn dispatch_unary_op<CpuFn>(
128 lhs: &TensorSlice<'_, InnerBoolSlice<'_>>,
129 cpu_fn: CpuFn,
130) -> InnerBoolArray
131where
132 CpuFn: for<'a> Fn(TensorSlice<'_, &'a [BooleanBlock]>) -> Vec<BooleanBlock>,
133{
134 match crate::high_level_api::global_state::device_of_internal_keys() {
135 Some(Device::Cpu) => {
136 let lhs_cpu_cow = lhs.slice.on_cpu();
137
138 let lhs_cpu_slice: &[BooleanBlock] = lhs_cpu_cow.borrow();
139
140 let result = cpu_fn(TensorSlice::new(lhs_cpu_slice, lhs.dims));
141 InnerBoolArray::Cpu(result)
142 }
143 #[cfg(feature = "gpu")]
144 Some(Device::CudaGpu) => {
145 panic!("Not supported by Cuda devices")
146 }
147 #[cfg(feature = "hpu")]
148 Some(Device::Hpu) => {
149 panic!("Not supported by Hpu devices")
150 }
151 None => {
152 panic!("{}", crate::high_level_api::errors::UninitializedServerKey);
153 }
154 }
155}
156
157#[inline]
158fn dispatch_binary_scalar_op<CpuFn>(
159 lhs: &TensorSlice<'_, InnerBoolSlice<'_>>,
160 rhs: &TensorSlice<'_, &'_ [bool]>,
161 cpu_fn: CpuFn,
162) -> InnerBoolArray
163where
164 CpuFn: for<'a> Fn(
165 TensorSlice<'_, &'a [BooleanBlock]>,
166 TensorSlice<'_, &'a [bool]>,
167 ) -> Vec<BooleanBlock>,
168{
169 match crate::high_level_api::global_state::device_of_internal_keys() {
170 Some(Device::Cpu) => {
171 let lhs_cpu_cow = lhs.slice.on_cpu();
172
173 let lhs_cpu_slice: &[BooleanBlock] = lhs_cpu_cow.borrow();
174
175 let result = cpu_fn(
176 TensorSlice::new(lhs_cpu_slice, lhs.dims),
177 TensorSlice::new(rhs.slice, rhs.dims),
178 );
179 InnerBoolArray::Cpu(result)
180 }
181 #[cfg(feature = "gpu")]
182 Some(Device::CudaGpu) => {
183 panic!("Not supported by Cuda devices")
184 }
185 #[cfg(feature = "hpu")]
186 Some(Device::Hpu) => {
187 panic!("Not supported by Hpu devices")
188 }
189 None => {
190 panic!("{}", crate::high_level_api::errors::UninitializedServerKey);
191 }
192 }
193}
194
195#[derive(Clone)]
196pub enum InnerBoolArray {
197 Cpu(Vec<BooleanBlock>),
198}
199
200impl BackendDataContainer for InnerBoolArray {
201 type Backend = DynFheBoolArrayBackend;
202
203 fn len(&self) -> usize {
204 match self {
205 Self::Cpu(cpu_array) => cpu_array.len(),
206 }
207 }
208
209 fn as_sub_slice(
210 &self,
211 range: impl RangeBounds<usize>,
212 ) -> <Self::Backend as ArrayBackend>::Slice<'_> {
213 match self {
214 Self::Cpu(cpu_vec) => {
215 InnerBoolSlice::Cpu(create_sub_slice_with_bound(cpu_vec.as_slice(), range))
216 }
217 }
218 }
219
220 fn into_owned(self) -> <Self::Backend as ArrayBackend>::Owned {
221 self
222 }
223}
224
225impl BackendDataContainerMut for InnerBoolArray {
226 fn as_sub_slice_mut(
227 &mut self,
228 range: impl RangeBounds<usize>,
229 ) -> <Self::Backend as ArrayBackend>::SliceMut<'_> {
230 match self {
231 Self::Cpu(cpu_vec) => InnerBoolSliceMut::Cpu(create_sub_mut_slice_with_bound(
232 cpu_vec.as_mut_slice(),
233 range,
234 )),
235 }
236 }
237}
238
239#[derive(Copy, Clone)]
240pub enum InnerBoolSlice<'a> {
241 Cpu(&'a [BooleanBlock]),
242}
243
244impl InnerBoolSlice<'_> {
245 fn on_cpu(&self) -> Cow<'_, [BooleanBlock]> {
246 match self {
247 InnerBoolSlice::Cpu(cpu_slice) => Cow::Borrowed(cpu_slice),
248 }
249 }
250}
251
252impl BackendDataContainer for InnerBoolSlice<'_> {
253 type Backend = DynFheBoolArrayBackend;
254
255 fn len(&self) -> usize {
256 match self {
257 InnerBoolSlice::Cpu(cpu_slice) => cpu_slice.len(),
258 }
259 }
260
261 fn as_sub_slice(
262 &self,
263 range: impl RangeBounds<usize>,
264 ) -> <Self::Backend as ArrayBackend>::Slice<'_> {
265 match self {
266 InnerBoolSlice::Cpu(cpu_slice) => {
267 InnerBoolSlice::Cpu(create_sub_slice_with_bound(*cpu_slice, range))
268 }
269 }
270 }
271
272 fn into_owned(self) -> <Self::Backend as ArrayBackend>::Owned {
273 match self {
274 InnerBoolSlice::Cpu(cpu_slice) => InnerBoolArray::Cpu(cpu_slice.to_vec()),
275 }
276 }
277}
278
279pub enum InnerBoolSliceMut<'a> {
280 Cpu(&'a mut [BooleanBlock]),
281}
282
283impl BackendDataContainer for InnerBoolSliceMut<'_> {
284 type Backend = DynFheBoolArrayBackend;
285
286 fn len(&self) -> usize {
287 match self {
288 InnerBoolSliceMut::Cpu(cpu_slice) => cpu_slice.len(),
289 }
290 }
291
292 fn as_sub_slice(
293 &self,
294 range: impl RangeBounds<usize>,
295 ) -> <Self::Backend as ArrayBackend>::Slice<'_> {
296 match self {
297 Self::Cpu(cpu_slice) => {
298 InnerBoolSlice::Cpu(create_sub_slice_with_bound(*cpu_slice, range))
299 }
300 }
301 }
302
303 fn into_owned(self) -> <Self::Backend as ArrayBackend>::Owned {
304 match self {
305 Self::Cpu(cpu_slice) => InnerBoolArray::Cpu(cpu_slice.to_vec()),
306 }
307 }
308}
309
310impl BackendDataContainerMut for InnerBoolSliceMut<'_> {
311 fn as_sub_slice_mut(
312 &mut self,
313 range: impl RangeBounds<usize>,
314 ) -> <Self::Backend as ArrayBackend>::SliceMut<'_> {
315 match self {
316 InnerBoolSliceMut::Cpu(cpu_slice) => {
317 InnerBoolSliceMut::Cpu(create_sub_mut_slice_with_bound(*cpu_slice, range))
318 }
319 }
320 }
321}
322
323impl<'a> FheTryEncrypt<&'a [bool], ClientKey> for FheBoolArray {
324 type Error = crate::Error;
325
326 fn try_encrypt(value: &'a [bool], key: &ClientKey) -> Result<Self, Self::Error> {
327 let cpu_array = crate::CpuFheBoolArray::try_encrypt(value, key)?;
328 let inner = InnerBoolArray::Cpu(cpu_array.into_container());
329 Ok(Self::new(inner, vec![value.len()]))
331 }
332}
333
334impl FheDecrypt<Vec<bool>> for FheBoolArray {
335 fn decrypt(&self, key: &ClientKey) -> Vec<bool> {
336 let slice = self.elems.as_slice();
337 let cpu_cow = slice.on_cpu();
338 let cpu_slice = cpu_cow.as_ref();
339
340 crate::CpuFheBoolSlice::<'_>::new(cpu_slice, self.dims.clone()).decrypt(key)
341 }
342}