Files
tfhe-rs/tfhe/src/high_level_api/booleans/inner.rs
2025-01-14 18:30:04 +01:00

221 lines
7.4 KiB
Rust

use crate::backward_compatibility::booleans::InnerBooleanVersionedOwned;
use crate::high_level_api::details::MaybeCloned;
use crate::high_level_api::global_state;
#[cfg(feature = "gpu")]
use crate::high_level_api::global_state::with_thread_local_cuda_streams;
use crate::integer::BooleanBlock;
use crate::Device;
use serde::{Deserializer, Serializer};
use tfhe_versionable::{Unversionize, UnversionizeError, Versionize, VersionizeOwned};
/// Enum that manages the current inner representation of a boolean.
pub(in crate::high_level_api) enum InnerBoolean {
Cpu(BooleanBlock),
#[cfg(feature = "gpu")]
Cuda(crate::integer::gpu::ciphertext::boolean_value::CudaBooleanBlock),
}
impl Clone for InnerBoolean {
fn clone(&self) -> Self {
match self {
Self::Cpu(inner) => Self::Cpu(inner.clone()),
#[cfg(feature = "gpu")]
Self::Cuda(inner) => {
with_thread_local_cuda_streams(|streams| Self::Cuda(inner.duplicate(streams)))
}
}
}
}
impl serde::Serialize for InnerBoolean {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match self {
Self::Cpu(cpu_ct) => cpu_ct.serialize(serializer),
#[cfg(feature = "gpu")]
Self::Cuda(_) => self.on_cpu().serialize(serializer),
}
}
}
impl<'de> serde::Deserialize<'de> for InnerBoolean {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let mut deserialized = Self::Cpu(crate::integer::BooleanBlock::deserialize(deserializer)?);
deserialized.move_to_device_of_server_key_if_set();
Ok(deserialized)
}
}
// Only CPU data are serialized so we only versionize the CPU type.
#[derive(serde::Serialize, serde::Deserialize)]
#[cfg_attr(dylint_lib = "tfhe_lints", allow(serialize_without_versionize))]
pub(crate) struct InnerBooleanVersionOwned(
<crate::integer::BooleanBlock as VersionizeOwned>::VersionedOwned,
);
impl Versionize for InnerBoolean {
type Versioned<'vers> = InnerBooleanVersionedOwned;
fn versionize(&self) -> Self::Versioned<'_> {
let data = self.on_cpu();
let versioned = data.into_owned().versionize_owned();
InnerBooleanVersionedOwned::V0(InnerBooleanVersionOwned(versioned))
}
}
impl VersionizeOwned for InnerBoolean {
type VersionedOwned = InnerBooleanVersionedOwned;
fn versionize_owned(self) -> Self::VersionedOwned {
let cpu_data = self.on_cpu();
InnerBooleanVersionedOwned::V0(InnerBooleanVersionOwned(
cpu_data.into_owned().versionize_owned(),
))
}
}
impl Unversionize for InnerBoolean {
fn unversionize(versioned: Self::VersionedOwned) -> Result<Self, UnversionizeError> {
match versioned {
InnerBooleanVersionedOwned::V0(v0) => {
let mut unversioned = Self::Cpu(crate::integer::BooleanBlock::unversionize(v0.0)?);
unversioned.move_to_device_of_server_key_if_set();
Ok(unversioned)
}
}
}
}
impl From<BooleanBlock> for InnerBoolean {
fn from(value: BooleanBlock) -> Self {
Self::Cpu(value)
}
}
#[cfg(feature = "gpu")]
impl From<crate::integer::gpu::ciphertext::boolean_value::CudaBooleanBlock> for InnerBoolean {
fn from(value: crate::integer::gpu::ciphertext::boolean_value::CudaBooleanBlock) -> Self {
Self::Cuda(value)
}
}
impl InnerBoolean {
pub(crate) fn current_device(&self) -> Device {
match self {
Self::Cpu(_) => Device::Cpu,
#[cfg(feature = "gpu")]
Self::Cuda(_) => Device::CudaGpu,
}
}
/// Returns the inner cpu ciphertext if self is on the CPU, otherwise, returns a copy
/// that is on the CPU
pub(crate) fn on_cpu(&self) -> MaybeCloned<'_, BooleanBlock> {
match self {
Self::Cpu(ct) => MaybeCloned::Borrowed(ct),
#[cfg(feature = "gpu")]
Self::Cuda(ct) => with_thread_local_cuda_streams(|streams| {
MaybeCloned::Cloned(ct.to_boolean_block(streams))
}),
}
}
/// Returns the inner cpu ciphertext if self is on the CPU, otherwise, returns a copy
/// that is on the CPU
#[cfg(feature = "gpu")]
pub(crate) fn on_gpu(
&self,
) -> MaybeCloned<'_, crate::integer::gpu::ciphertext::CudaUnsignedRadixCiphertext> {
match self {
Self::Cpu(ct) => with_thread_local_cuda_streams(|streams| {
let ct_as_radix = crate::integer::RadixCiphertext::from(vec![ct.0.clone()]);
let cuda_ct =
crate::integer::gpu::ciphertext::CudaUnsignedRadixCiphertext::from_radix_ciphertext(
&ct_as_radix,
streams,
);
MaybeCloned::Cloned(cuda_ct)
}),
#[cfg(feature = "gpu")]
Self::Cuda(ct) => MaybeCloned::Borrowed(ct.as_ref()),
}
}
pub(crate) fn as_cpu_mut(&mut self) -> &mut BooleanBlock {
match self {
Self::Cpu(block) => block,
#[cfg(feature = "gpu")]
_ => {
self.move_to_device(Device::Cpu);
self.as_cpu_mut()
}
}
}
#[cfg(feature = "gpu")]
#[track_caller]
pub(crate) fn as_gpu_mut(
&mut self,
) -> &mut crate::integer::gpu::ciphertext::CudaUnsignedRadixCiphertext {
if let Self::Cuda(radix_ct) = self {
radix_ct.as_mut()
} else {
self.move_to_device(Device::CudaGpu);
self.as_gpu_mut()
}
}
#[cfg(feature = "gpu")]
pub(crate) fn into_gpu(
self,
) -> crate::integer::gpu::ciphertext::boolean_value::CudaBooleanBlock {
match self {
Self::Cpu(cpu_ct) => with_thread_local_cuda_streams(|streams| {
crate::integer::gpu::ciphertext::boolean_value::CudaBooleanBlock::from_boolean_block(
&cpu_ct, streams,
)
}),
Self::Cuda(ct) => ct,
}
}
#[allow(clippy::needless_pass_by_ref_mut)]
pub(crate) fn move_to_device(&mut self, device: Device) {
match (&self, device) {
(Self::Cpu(_), Device::Cpu) => {
// Nothing to do, we already are on the correct device
}
#[cfg(feature = "gpu")]
(Self::Cuda(_), Device::CudaGpu) => {
// Nothing to do, we already are on the correct device
}
#[cfg(feature = "gpu")]
(Self::Cpu(ct), Device::CudaGpu) => {
let new_inner = with_thread_local_cuda_streams(|streams| {
crate::integer::gpu::ciphertext::boolean_value::CudaBooleanBlock::from_boolean_block(
ct,
streams,
)
});
*self = Self::Cuda(new_inner);
}
#[cfg(feature = "gpu")]
(Self::Cuda(ct), Device::Cpu) => {
let new_inner =
with_thread_local_cuda_streams(|streams| ct.to_boolean_block(streams));
*self = Self::Cpu(new_inner);
}
}
}
#[inline]
pub(crate) fn move_to_device_of_server_key_if_set(&mut self) {
if let Some(device) = global_state::device_of_internal_keys() {
self.move_to_device(device);
}
}
}