use ruda_core::tensor::DType;
pub use ruda_core::backtrace::BackTrace;
use alloc::string::String;
use enumset::{EnumSet, EnumSetType};
use crate::element::Element;
use crate::ops::*;
use crate::tensor::{BoolTensor, FloatTensor, IntTensor, QuantizedTensor};
use crate::{QTensorPrimitive, TensorData, TensorMetadata};
#[cfg(feature = "distributed")]
use crate::distributed::{DistributedParamId, DistributedParams};
use super::DeviceOps;
pub trait BackendTypes {
type Device: DeviceOps;
type FloatTensorPrimitive: TensorMetadata + 'static;
type FloatElem: Element;
type IntTensorPrimitive: TensorMetadata + 'static;
type IntElem: Element;
type BoolTensorPrimitive: TensorMetadata + 'static;
type BoolElem: Element;
type QuantizedTensorPrimitive: TensorMetadata + QTensorPrimitive + 'static;
}
#[cfg_attr(doc, doc = crate::doc_tensor!())]
#[cfg_attr(not(doc), doc = "`Tensor`")]
pub trait Backend:
BackendTypes
+ FloatTensorOps<Self>
+ BoolTensorOps<Self>
+ IntTensorOps<Self>
+ ModuleOps<Self>
+ ActivationOps<Self>
+ QTensorOps<Self>
+ TransactionOps<Self>
+ Clone
+ Default
+ Sized
+ Send
+ Sync
+ core::fmt::Debug
+ 'static
{
fn ad_enabled(_device: &Self::Device) -> bool {
false
}
fn autodiff_creation_context(device: &Self::Device) -> super::AutodiffContext {
if Self::ad_enabled(device) {
super::AutodiffContext::Enabled(super::GradientCheckpointingStrategy::Disabled)
} else { super::AutodiffContext::Disabled }
}
fn with_autodiff_creation_context<R>(device: &Self::Device, context: super::AutodiffContext, func: impl FnOnce() -> R) -> R {
assert_eq!(Self::autodiff_creation_context(device), context, "Backend does not support overriding the tensor creation context");
func()
}
#[allow(unused_variables)]
fn memory_persistent_allocations<
Output: Send,
Input: Send,
Func: Fn(Input) -> Output + Send,
>(
device: &Self::Device,
input: Input,
func: Func,
) -> Output {
func(input)
}
#[allow(unused_variables)]
fn memory_cleanup(device: &Self::Device) {}
fn name(device: &Self::Device) -> String;
fn seed(device: &Self::Device, seed: u64);
fn rng_state(_device: &Self::Device) -> Result<alloc::vec::Vec<u8>, ExecutionError> {
Err(ExecutionError::WithContext { reason: "This backend does not implement RNG checkpoints".into() })
}
fn restore_rng_state(_device: &Self::Device, _state: &[u8]) -> Result<(), ExecutionError> {
Err(ExecutionError::WithContext { reason: "This backend does not implement RNG restoration".into() })
}
fn sync(_device: &Self::Device) -> Result<(), ExecutionError> {
Ok(())
}
fn profile<O: Send + 'static>(device: &Self::Device, _options: super::ProfileOptions,
func: impl FnOnce() -> O + Send) -> Result<(O, super::ProfileDuration), ExecutionError> {
super::profile_system_time::<Self, O>(device, func)
}
fn profile_start(_device: &Self::Device) -> Result<Option<super::ProfileToken>, ExecutionError> { Ok(None) }
fn profile_end(_device: &Self::Device, _token: super::ProfileToken, _options: super::ProfileOptions)
-> Result<super::ProfileDuration, ExecutionError> { Err(super::profile::unsupported()) }
fn profile_abandon(device: &Self::Device, token: super::ProfileToken) {
let _ = Self::profile_end(device, token, super::ProfileOptions::default());
}
fn flush(_device: &Self::Device) -> Result<(), ExecutionError> {
Err(ExecutionError::WithContext {
reason: "This backend does not implement nonblocking stream submission".into(),
})
}
fn graph_prepare(_device: &Self::Device) -> Result<(), ExecutionError> {
Err(ExecutionError::WithContext { reason: "Hardware graph capture is unsupported".into() })
}
fn graph_start_capture(_device: &Self::Device) -> Result<(), ExecutionError> {
Err(ExecutionError::WithContext { reason: "Hardware graph capture is unsupported".into() })
}
fn graph_stop_capture(_device: &Self::Device) -> Result<super::BackendGraph, ExecutionError> {
Err(ExecutionError::WithContext { reason: "Hardware graph capture is unsupported".into() })
}
fn graph_abort_capture(_device: &Self::Device) {}
unsafe fn graph_replay(_device: &Self::Device, graph: &super::BackendGraph) -> Result<(), ExecutionError> {
unsafe { graph.replay() }
}
fn staging<'a, Iter>(_data: Iter, _device: &Self::Device)
where
Iter: Iterator<Item = &'a mut TensorData>,
{
}
fn supports_dtype(device: &Self::Device, dtype: DType) -> bool {
Self::dtype_usage(device, dtype).is_superset(DTypeUsage::general())
}
fn dtype_usage(device: &Self::Device, dtype: DType) -> DTypeUsageSet;
fn device_count(type_id: u16) -> usize;
}
pub use ruda_core::tensor::execution::ExecutionError;
pub trait AutodiffBackend: Backend {
type InnerBackend: Backend<Device = Self::Device, FloatElem = Self::FloatElem, IntElem = Self::IntElem>;
type Gradients: Send;
fn backward(tensor: FloatTensor<Self>) -> Self::Gradients;
fn grad(
tensor: &FloatTensor<Self>,
grads: &Self::Gradients,
) -> Option<FloatTensor<Self::InnerBackend>>;
fn grad_remove(
tensor: &FloatTensor<Self>,
grads: &mut Self::Gradients,
) -> Option<FloatTensor<Self::InnerBackend>>;
fn grad_replace(
tensor: &FloatTensor<Self>,
grads: &mut Self::Gradients,
grad: FloatTensor<Self::InnerBackend>,
);
fn q_grad(tensor: &QuantizedTensor<Self>, grads: &Self::Gradients) -> Option<FloatTensor<Self::InnerBackend>> {
if !Self::q_is_require_grad(tensor) { return None; }
let dtype = crate::get_device_settings::<Self>(&Self::q_device(tensor)).float_dtype;
Self::grad(&Self::dequantize(tensor.clone(), dtype), grads)
}
fn q_grad_remove(tensor: &QuantizedTensor<Self>, grads: &mut Self::Gradients) -> Option<FloatTensor<Self::InnerBackend>> {
if !Self::q_is_require_grad(tensor) { return None; }
let dtype = crate::get_device_settings::<Self>(&Self::q_device(tensor)).float_dtype;
Self::grad_remove(&Self::dequantize(tensor.clone(), dtype), grads)
}
fn q_grad_replace(tensor: &QuantizedTensor<Self>, grads: &mut Self::Gradients, grad: FloatTensor<Self::InnerBackend>) {
let dtype = crate::get_device_settings::<Self>(&Self::q_device(tensor)).float_dtype;
Self::grad_replace(&Self::dequantize(tensor.clone(), dtype), grads, grad)
}
fn inner(tensor: FloatTensor<Self>) -> FloatTensor<Self::InnerBackend>;
fn int_inner(tensor: IntTensor<Self>) -> IntTensor<Self::InnerBackend>;
fn bool_inner(tensor: BoolTensor<Self>) -> BoolTensor<Self::InnerBackend>;
fn q_inner(tensor: QuantizedTensor<Self>) -> QuantizedTensor<Self::InnerBackend>;
fn from_inner(tensor: FloatTensor<Self::InnerBackend>) -> FloatTensor<Self>;
fn int_from_inner(tensor: IntTensor<Self::InnerBackend>) -> IntTensor<Self>;
fn bool_from_inner(tensor: BoolTensor<Self::InnerBackend>) -> BoolTensor<Self>;
fn q_from_inner(tensor: QuantizedTensor<Self::InnerBackend>) -> QuantizedTensor<Self>;
#[cfg(feature = "distributed")]
fn set_distributed_params(
tensor: FloatTensor<Self>,
_param_id: DistributedParamId,
) -> FloatTensor<Self> {
tensor
}
#[cfg(feature = "distributed")]
fn distributed_params(_tensor: &FloatTensor<Self>) -> Option<DistributedParams> {
None
}
#[cfg(feature = "distributed")]
fn is_distributed(_tensor: &FloatTensor<Self>) -> bool {
false
}
}
#[derive(Debug, EnumSetType)]
pub enum DTypeUsage {
Storage,
Arithmetic,
Accelerated,
}
pub type DTypeUsageSet = EnumSet<DTypeUsage>;
impl DTypeUsage {
pub fn general() -> DTypeUsageSet {
DTypeUsage::Storage | DTypeUsage::Arithmetic
}
}