use std::fmt::Debug;
use bytemuck::{NoUninit, Pod};
use cudarc::driver::{DeviceRepr, ValidAsZeroBits};
use half::f16;
use crate::io::device::GpuContext;
use crate::util::core::Tensor;
use crate::util::log::Error;
#[derive(Debug, PartialEq)]
pub enum Precision {
FP32,
FP16
}
impl Precision {
pub fn size_bytes(&self) -> usize {
match self {
Precision::FP32 => size_of::<f32>(),
Precision::FP16 => size_of::<f16>()
}
}
}
pub trait PrecisionType: private::Sealed + DeviceRepr + Copy + 'static + ValidAsZeroBits + Pod + NoUninit + Debug {
fn zero() -> Self;
fn precision() -> Precision;
fn from_f32(v: f32) -> Self;
fn to_f32(&self) -> f32;
fn from_f16(v: f16) -> Self;
fn to_f16(&self) -> f16;
}
mod private {
pub trait Sealed {}
impl Sealed for f32 {}
impl Sealed for half::f16 {}
}
impl PrecisionType for f32 {
fn zero() -> Self { 0.0 }
fn precision() -> Precision { Precision::FP32 }
fn from_f32(v: f32) -> Self { v }
fn to_f32(&self) -> f32 { *self }
fn from_f16(v: f16) -> Self { v.to_f32()}
fn to_f16(&self) -> f16 { f16::from_f32(*self) }
}
impl PrecisionType for f16 {
fn zero() -> Self { f16::ZERO }
fn precision() -> Precision { Precision::FP16 }
fn from_f32(v: f32) -> Self { f16::from_f32(v) }
fn to_f32(&self) -> f32 { f16::to_f32(*self) }
fn from_f16(v: f16) -> Self { v }
fn to_f16(&self) -> f16 { *self }
}
pub(crate) trait CastPrecision<Target> {
fn cast(self, context: &GpuContext) -> Result<Target, Error>;
}
impl<T: PrecisionType, U: PrecisionType> CastPrecision<Tensor<U>> for Tensor<T> {
fn cast(self, context: &GpuContext) -> Result<Tensor<U>, Error> {
let tensor = Tensor::<U>::zeros(context, &[self.rows(), self.cols()]);
context.gpu_cast_t(self.get_data(), tensor.get_data())?;
Ok(tensor)
}
}