pub mod linear;
pub mod batchnormalization;
pub mod bias;
pub mod activation;
pub mod output;
pub mod input;
use std::marker::PhantomData;
use std::fmt::Debug;
use num_traits::FromPrimitive;
use rayon::prelude::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator};
#[cfg(feature = "cuda")]
use std::rc::Rc;
#[cfg(feature = "cuda")]
use rcublas::Context;
#[cfg(feature = "cuda")]
use rcublas_sys::{cublasDscal_v2, cublasHandle_t, cublasSscal_v2, cublasStatus_t};
#[cfg(feature = "cuda")]
use rcublas::api::PointerMode;
#[cfg(feature = "cuda")]
use rcudnn::{Cudnn};
#[cfg(feature = "cuda")]
use rcudnn_sys::cudnnHandle_t;
use crate::arr::{Arr, SerializedVecView};
use crate::error::{TrainingError, TypeConvertError};
use crate::error::EvaluateError::TypeCastError;
use crate::mem::AsRawSlice;
use crate::UnitValue;
use crate::error::{DeviceError};
#[cfg(feature = "cuda")]
use crate::layer::BatchSize;
#[cfg(feature = "cuda")]
use crate::cuda::{AsCudaMutPtr, AsMutPtr, AsPtr, CudaPtr, CudaTensor1dPtr, CudaTensor1dPtrView, CudaVecView, DataTypeInfo, Kernel, MemorySize};
#[cfg(feature = "cuda")]
use crate::cuda::allocator::CudaAllocator;
#[cfg(feature = "cuda")]
use crate::cuda::kernel::device::{ReduceLinearBatch, ReduceLinearBatchArgs};
pub trait Device<U>: Clone where U: UnitValue<U> {
}
pub trait DeviceReduce<T,R,U,const N:usize> where U: UnitValue<U> {
fn reduce<'a>(&self, input: &'a T) -> Result<R, TrainingError>;
}
pub trait DeviceBatchAveraging<T,U> where U: UnitValue<U> {
fn batch_averaging<'a>(&self, input: T,batch_size:usize) -> Result<T,TrainingError>;
}
pub struct DeviceCpu<U> where U: UnitValue<U> {
u:PhantomData<U>,
}
impl<U> DeviceCpu<U> where U: UnitValue<U> {
pub fn new() -> Result<DeviceCpu<U>,DeviceError> {
Ok(DeviceCpu {
u: PhantomData::<U>
})
}
}
impl<U> Device<U> for DeviceCpu<U> where U: UnitValue<U> {
}
impl<T,U,const N:usize> DeviceReduce<T,Arr<U,N>,U,N> for DeviceCpu<U>
where U: UnitValue<U> + Debug,
for<'a> SerializedVecView<'a,U,Arr<U,N>>: TryFrom<&'a T,Error=TypeConvertError> {
#[inline]
fn reduce<'a>(&self, input: &'a T) -> Result<Arr<U,N>, TrainingError> {
Ok(SerializedVecView::<'a,U,Arr<U,N>>::try_from(input)?.par_iter()
.map(|i| i.into())
.map(|i| Ok(i)).reduce(|| Ok(Arr::new()), |acc,i| {
acc.and_then(|acc| i.and_then(|i| {
acc.par_iter().cloned()
.zip(i.par_iter().cloned())
.map(|(acc, i)| acc + i).collect::<Vec<U>>().try_into()
}))
})?)
}
}
impl<T,U> DeviceBatchAveraging<T,U> for DeviceCpu<U>
where U: UnitValue<U> + Default + Clone + Send + FromPrimitive,
T: AsRawSlice<U> + TryFrom<Vec<U>,Error=TypeConvertError> {
#[inline]
fn batch_averaging<'a>(&self, input: T,batch_size:usize) -> Result<T,TrainingError> {
let batch_size = U::from_usize(batch_size).ok_or(TypeCastError(
format!("Failed to convert batch size type.")
))?;
Ok(input.as_raw_slice().par_iter().cloned().map(|i| i / batch_size).collect::<Vec<U>>().try_into()?)
}
}
impl<U> Clone for DeviceCpu<U> where U: UnitValue<U> {
fn clone(&self) -> Self {
DeviceCpu {
u:PhantomData::<U>
}
}
}
#[cfg(feature = "cuda")]
pub struct CublasContext {
raw:Rc<Context>
}
#[cfg(feature = "cuda")]
impl CublasContext {
pub fn new(pointer_mode:PointerMode) -> Result<CublasContext, rcublas::error::Error> {
let mut context = Context::new()?;
context.set_pointer_mode(pointer_mode)?;
Ok(CublasContext {
raw: Rc::new(context)
})
}
pub fn id_c(&self) -> &cublasHandle_t {
self.raw.id_c()
}
pub fn pointer_mode(&self) -> Result<PointerMode, rcublas::error::Error> {
self.raw.pointer_mode()
}
}
#[cfg(feature = "cuda")]
impl Clone for CublasContext {
fn clone(&self) -> Self {
CublasContext {
raw: Rc::clone(&self.raw)
}
}
}
#[cfg(feature = "cuda")]
pub struct CudnnContext {
raw:Rc<Cudnn>
}
#[cfg(feature = "cuda")]
impl CudnnContext {
pub fn new() -> Result<CudnnContext, rcudnn::Error> {
let cudnn = Cudnn::new()?;
Ok(CudnnContext {
raw: Rc::new(cudnn)
})
}
pub fn id_c(&self) -> &cudnnHandle_t {
self.raw.id_c()
}
}
#[cfg(feature = "cuda")]
impl Clone for CudnnContext {
fn clone(&self) -> Self {
CudnnContext {
raw: Rc::clone(&self.raw)
}
}
}
#[cfg(feature = "cuda")]
pub struct DeviceGpu<U,A> where A: CudaAllocator {
u:PhantomData<U>,
cublas:CublasContext,
cudnn:CudnnContext,
allocator:A
}
#[cfg(feature = "cuda")]
impl<U,A> DeviceGpu<U,A> where U: UnitValue<U>, A: CudaAllocator {
pub fn new(allocator:&A) -> Result<DeviceGpu<U,A>,DeviceError> {
let context = CublasContext::new(PointerMode::Device)?;
let cudnn = CudnnContext::new()?;
Ok(DeviceGpu {
u:PhantomData::<U>,
cublas:context,
cudnn:cudnn,
allocator:allocator.clone()
})
}
pub fn cublas(&self) -> &CublasContext {
&self.cublas
}
pub fn cudnn(&self) -> &CudnnContext {
&self.cudnn
}
}
#[cfg(feature = "cuda")]
pub trait DeviceAllocator<A: CudaAllocator> {
fn get_allocator(&self) -> &A;
}
#[cfg(feature = "cuda")]
impl<U,A> DeviceAllocator<A> for DeviceGpu<U,A> where A: CudaAllocator {
fn get_allocator(&self) -> &A {
&self.allocator
}
}
#[cfg(feature = "cuda")]
impl<A: CudaAllocator> Device<f32> for DeviceGpu<f32,A> {
}
#[cfg(feature = "cuda")]
impl<U,T,A,const N:usize> DeviceReduce<T,CudaTensor1dPtr<U,A,N>,U,N> for DeviceGpu<U,A>
where U: UnitValue<U> + DataTypeInfo,
T: BatchSize,
A: CudaAllocator,
for<'a> CudaVecView<'a,U,CudaTensor1dPtrView<'a,U,N>>: TryFrom<&'a T,Error=TypeConvertError>,
for<'a> ReduceLinearBatch::<'a,U,A,N>: Kernel<Args=ReduceLinearBatchArgs<'a,U,A,N>> {
#[inline]
fn reduce<'a>(&self, input: &'a T) -> Result<CudaTensor1dPtr<U,A,N>, TrainingError> {
let input_ptr = input.try_into()?;
let output_ptr = CudaTensor1dPtr::<U,A,N>::new(&self.allocator)?;
let mut args = ReduceLinearBatchArgs::new(&input_ptr,output_ptr,N,input.size());
let mut kernel = ReduceLinearBatch::<U,A,N>::new();
kernel.launch(&mut args)?;
Ok(args.output)
}
}
#[cfg(feature = "cuda")]
impl<T,A> DeviceBatchAveraging<T,f32> for DeviceGpu<f32,A>
where A: CudaAllocator,
T: MemorySize + AsCudaMutPtr<Pointee=f32,Allocator=A> {
fn batch_averaging<'a>(&self, input: T, batch_size: usize) -> Result<T, TrainingError> {
let batch_size = f32::from_usize(batch_size).ok_or(TypeCastError(
format!("Failed to convert batch size type.")
))?;
let mut input = input;
let alpha = CudaPtr::try_from(1. / batch_size)?;
let tensor_size = T::size();
match unsafe {
cublasSscal_v2(*self.cublas.id_c(),
tensor_size as ::libc::c_int,
alpha.as_ptr(),
input.as_mut_ptr(),
1
)
} {
cublasStatus_t::CUBLAS_STATUS_SUCCESS => Ok(input),
cublasStatus_t::CUBLAS_STATUS_NOT_INITIALIZED => {
return Err(TrainingError::CublasError(rcublas::Error::NotInitialized));
},
cublasStatus_t::CUBLAS_STATUS_INVALID_VALUE => {
return Err(TrainingError::CublasError(rcublas::Error::InvalidValue(
"Parameters m or n are less than 0, or incx or incy was specified as 0."
)));
},
cublasStatus_t::CUBLAS_STATUS_EXECUTION_FAILED => {
return Err(TrainingError::CublasError(rcublas::Error::ExecutionFailed));
},
status => {
return Err(TrainingError::CublasError(rcublas::Error::Unknown(
"Unable to get cuBLAS cublasSscal_v2",
status as i32 as u64
)));
}
}
}
}
#[cfg(feature = "cuda")]
impl<T,A> DeviceBatchAveraging<T,f64> for DeviceGpu<f64,A>
where A: CudaAllocator,
T: MemorySize + AsCudaMutPtr<Pointee=f64,Allocator=A> {
fn batch_averaging<'a>(&self, input: T, batch_size: usize) -> Result<T, TrainingError> {
let batch_size = f64::from_usize(batch_size).ok_or(TypeCastError(
format!("Failed to convert batch size type.")
))?;
let mut input = input;
let alpha = CudaPtr::try_from(1. / batch_size)?;
let tensor_size = T::size();
match unsafe {
cublasDscal_v2(*self.cublas.id_c(),
tensor_size as ::libc::c_int,
alpha.as_ptr(),
input.as_mut_ptr(),
1
)
} {
cublasStatus_t::CUBLAS_STATUS_SUCCESS => Ok(input),
cublasStatus_t::CUBLAS_STATUS_NOT_INITIALIZED => {
return Err(TrainingError::CublasError(rcublas::Error::NotInitialized));
},
cublasStatus_t::CUBLAS_STATUS_INVALID_VALUE => {
return Err(TrainingError::CublasError(rcublas::Error::InvalidValue(
"Parameters m or n are less than 0, or incx or incy was specified as 0."
)));
},
cublasStatus_t::CUBLAS_STATUS_EXECUTION_FAILED => {
return Err(TrainingError::CublasError(rcublas::Error::ExecutionFailed));
},
status => {
return Err(TrainingError::CublasError(rcublas::Error::Unknown(
"Unable to get cuBLAS cublasDscal_v2",
status as i32 as u64
)));
}
}
}
}
#[cfg(feature = "cuda")]
impl<A: CudaAllocator> Device<f64> for DeviceGpu<f64,A> {
}
#[cfg(feature = "cuda")]
impl<U,A> Clone for DeviceGpu<U,A> where U: UnitValue<U> + Debug, A: CudaAllocator {
fn clone(&self) -> Self {
DeviceGpu {
u:PhantomData::<U>,
cublas:self.cublas.clone(),
cudnn:self.cudnn.clone(),
allocator:self.allocator.clone()
}
}
}