use crate::config::{
CompareDir, DotGeneralConfig, GatherConfig, PadConfig, ScatterConfig, SliceConfig,
};
use crate::types::{TensorRank, TypedTensor, TypedTensorView, TypedTensorViewMut};
use crate::validate::validate_convert_dtype;
use crate::{RuntimeCacheControl, Tensor, TensorRead, TensorValue, TensorWrite};
fn read_boundary_error(op: &'static str) -> crate::Error {
crate::Error::backend_failure(
op,
"backend does not accept borrowed tensor views at this execution boundary",
)
}
fn read_tensor<'a>(op: &'static str, input: TensorRead<'a>) -> crate::Result<&'a Tensor> {
input.as_tensor().ok_or_else(|| read_boundary_error(op))
}
fn validate_axis_list(
op: &'static str,
role: &'static str,
axes: &[usize],
rank: usize,
) -> crate::Result<()> {
let mut seen = vec![false; rank];
for &axis in axes {
if axis >= rank {
return Err(crate::Error::AxisOutOfBounds { op, axis, rank });
}
if seen[axis] {
return Err(crate::Error::DuplicateAxis { op, axis, role });
}
seen[axis] = true;
}
Ok(())
}
fn validate_role_disjoint(
op: &'static str,
first_role: &'static str,
first_axes: &[usize],
second_role: &'static str,
second_axes: &[usize],
) -> crate::Result<()> {
for &axis in first_axes {
if second_axes.contains(&axis) {
return Err(crate::Error::AxisRoleConflict {
op,
axis,
first_role,
second_role,
});
}
}
Ok(())
}
#[doc(hidden)]
pub fn dot_general_output_shape(
lhs_shape: &[usize],
rhs_shape: &[usize],
config: &DotGeneralConfig,
op: &'static str,
) -> crate::Result<Vec<usize>> {
if config.lhs_contracting_dims.len() != config.rhs_contracting_dims.len() {
return Err(crate::Error::InvalidConfig {
op,
message: "lhs/rhs contracting dim counts differ".into(),
});
}
if config.lhs_batch_dims.len() != config.rhs_batch_dims.len() {
return Err(crate::Error::InvalidConfig {
op,
message: "lhs/rhs batch dim counts differ".into(),
});
}
let lhs_rank = lhs_shape.len();
let rhs_rank = rhs_shape.len();
validate_axis_list(
op,
"lhs_contracting",
&config.lhs_contracting_dims,
lhs_rank,
)?;
validate_axis_list(
op,
"rhs_contracting",
&config.rhs_contracting_dims,
rhs_rank,
)?;
validate_axis_list(op, "lhs_batch", &config.lhs_batch_dims, lhs_rank)?;
validate_axis_list(op, "rhs_batch", &config.rhs_batch_dims, rhs_rank)?;
validate_role_disjoint(
op,
"lhs_contracting",
&config.lhs_contracting_dims,
"lhs_batch",
&config.lhs_batch_dims,
)?;
validate_role_disjoint(
op,
"rhs_contracting",
&config.rhs_contracting_dims,
"rhs_batch",
&config.rhs_batch_dims,
)?;
for (&lhs_axis, &rhs_axis) in config
.lhs_contracting_dims
.iter()
.zip(&config.rhs_contracting_dims)
{
if lhs_shape[lhs_axis] != rhs_shape[rhs_axis] {
return Err(crate::Error::ShapeMismatch {
op,
lhs: lhs_shape.to_vec(),
rhs: rhs_shape.to_vec(),
});
}
}
for (&lhs_axis, &rhs_axis) in config.lhs_batch_dims.iter().zip(&config.rhs_batch_dims) {
if lhs_shape[lhs_axis] != rhs_shape[rhs_axis] {
return Err(crate::Error::ShapeMismatch {
op,
lhs: lhs_shape.to_vec(),
rhs: rhs_shape.to_vec(),
});
}
}
let lhs_free = (0..lhs_rank)
.filter(|axis| {
!config.lhs_contracting_dims.contains(axis) && !config.lhs_batch_dims.contains(axis)
})
.map(|axis| lhs_shape[axis]);
let rhs_free = (0..rhs_rank)
.filter(|axis| {
!config.rhs_contracting_dims.contains(axis) && !config.rhs_batch_dims.contains(axis)
})
.map(|axis| rhs_shape[axis]);
let batch = config.lhs_batch_dims.iter().map(|&axis| lhs_shape[axis]);
Ok(lhs_free.chain(rhs_free).chain(batch).collect())
}
#[doc(hidden)]
pub fn validate_dot_general_read_into(
lhs: &TensorRead<'_>,
rhs: &TensorRead<'_>,
config: &DotGeneralConfig,
out: &TensorWrite<'_>,
op: &'static str,
) -> crate::Result<Vec<usize>> {
if lhs.dtype() != rhs.dtype() {
return Err(crate::Error::DTypeMismatch {
op,
lhs: lhs.dtype(),
rhs: rhs.dtype(),
});
}
if out.dtype() != lhs.dtype() {
return Err(crate::Error::DTypeMismatch {
op,
lhs: out.dtype(),
rhs: lhs.dtype(),
});
}
let expected = dot_general_output_shape(lhs.shape(), rhs.shape(), config, op)?;
if out.shape() != expected.as_slice() {
return Err(crate::Error::ShapeMismatch {
op,
lhs: out.shape().to_vec(),
rhs: expected.clone(),
});
}
Ok(expected)
}
#[doc(hidden)]
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct ElementwiseFusionPlan {
dtype: crate::DType,
input_count: usize,
outputs: Vec<usize>,
ops: Vec<ElementwiseFusionInst>,
}
#[doc(hidden)]
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct ElementwiseFusionInst {
op: ElementwiseFusionOp,
inputs: Vec<usize>,
}
tenferro_core_ops::define_elementwise_fusion_op!();
impl ElementwiseFusionPlan {
pub fn new(
dtype: crate::DType,
input_count: usize,
outputs: Vec<usize>,
ops: Vec<ElementwiseFusionInst>,
) -> Self {
Self {
dtype,
input_count,
outputs,
ops,
}
}
pub fn dtype(&self) -> crate::DType {
self.dtype
}
pub fn input_count(&self) -> usize {
self.input_count
}
pub fn outputs(&self) -> &[usize] {
&self.outputs
}
pub fn ops(&self) -> &[ElementwiseFusionInst] {
&self.ops
}
}
impl ElementwiseFusionInst {
pub fn new(op: ElementwiseFusionOp, inputs: Vec<usize>) -> Self {
Self { op, inputs }
}
pub fn op(&self) -> ElementwiseFusionOp {
self.op
}
pub fn inputs(&self) -> &[usize] {
&self.inputs
}
}
pub trait TensorElementwise {
fn add(&mut self, lhs: &Tensor, rhs: &Tensor) -> crate::Result<Tensor>;
fn add_read(&mut self, lhs: TensorRead<'_>, rhs: TensorRead<'_>) -> crate::Result<Tensor> {
self.add(read_tensor("add", lhs)?, read_tensor("add", rhs)?)
}
fn mul(&mut self, lhs: &Tensor, rhs: &Tensor) -> crate::Result<Tensor>;
fn mul_read(&mut self, lhs: TensorRead<'_>, rhs: TensorRead<'_>) -> crate::Result<Tensor> {
self.mul(read_tensor("mul", lhs)?, read_tensor("mul", rhs)?)
}
fn neg(&mut self, input: &Tensor) -> crate::Result<Tensor>;
fn neg_read(&mut self, input: TensorRead<'_>) -> crate::Result<Tensor> {
self.neg(read_tensor("neg", input)?)
}
fn conj(&mut self, input: &Tensor) -> crate::Result<Tensor>;
fn conj_read(&mut self, input: TensorRead<'_>) -> crate::Result<Tensor> {
self.conj(read_tensor("conj", input)?)
}
fn div(&mut self, lhs: &Tensor, rhs: &Tensor) -> crate::Result<Tensor>;
fn div_read(&mut self, lhs: TensorRead<'_>, rhs: TensorRead<'_>) -> crate::Result<Tensor> {
self.div(read_tensor("div", lhs)?, read_tensor("div", rhs)?)
}
fn abs(&mut self, input: &Tensor) -> crate::Result<Tensor>;
fn abs_read(&mut self, input: TensorRead<'_>) -> crate::Result<Tensor> {
self.abs(read_tensor("abs", input)?)
}
fn sign(&mut self, input: &Tensor) -> crate::Result<Tensor>;
fn sign_read(&mut self, input: TensorRead<'_>) -> crate::Result<Tensor> {
self.sign(read_tensor("sign", input)?)
}
fn maximum(&mut self, lhs: &Tensor, rhs: &Tensor) -> crate::Result<Tensor>;
fn maximum_read(&mut self, lhs: TensorRead<'_>, rhs: TensorRead<'_>) -> crate::Result<Tensor> {
self.maximum(read_tensor("maximum", lhs)?, read_tensor("maximum", rhs)?)
}
fn minimum(&mut self, lhs: &Tensor, rhs: &Tensor) -> crate::Result<Tensor>;
fn minimum_read(&mut self, lhs: TensorRead<'_>, rhs: TensorRead<'_>) -> crate::Result<Tensor> {
self.minimum(read_tensor("minimum", lhs)?, read_tensor("minimum", rhs)?)
}
fn compare(&mut self, lhs: &Tensor, rhs: &Tensor, dir: &CompareDir) -> crate::Result<Tensor>;
fn compare_read(
&mut self,
lhs: TensorRead<'_>,
rhs: TensorRead<'_>,
dir: &CompareDir,
) -> crate::Result<Tensor> {
self.compare(
read_tensor("compare", lhs)?,
read_tensor("compare", rhs)?,
dir,
)
}
fn select(
&mut self,
pred: &Tensor,
on_true: &Tensor,
on_false: &Tensor,
) -> crate::Result<Tensor>;
fn select_read(
&mut self,
pred: TensorRead<'_>,
on_true: TensorRead<'_>,
on_false: TensorRead<'_>,
) -> crate::Result<Tensor> {
self.select(
read_tensor("select", pred)?,
read_tensor("select", on_true)?,
read_tensor("select", on_false)?,
)
}
fn clamp(&mut self, input: &Tensor, lower: &Tensor, upper: &Tensor) -> crate::Result<Tensor>;
fn clamp_read(
&mut self,
input: TensorRead<'_>,
lower: TensorRead<'_>,
upper: TensorRead<'_>,
) -> crate::Result<Tensor> {
self.clamp(
read_tensor("clamp", input)?,
read_tensor("clamp", lower)?,
read_tensor("clamp", upper)?,
)
}
}
pub trait TensorAnalytic {
fn exp(&mut self, input: &Tensor) -> crate::Result<Tensor>;
fn exp_read(&mut self, input: TensorRead<'_>) -> crate::Result<Tensor> {
self.exp(read_tensor("exp", input)?)
}
fn log(&mut self, input: &Tensor) -> crate::Result<Tensor>;
fn log_read(&mut self, input: TensorRead<'_>) -> crate::Result<Tensor> {
self.log(read_tensor("log", input)?)
}
fn sin(&mut self, input: &Tensor) -> crate::Result<Tensor>;
fn sin_read(&mut self, input: TensorRead<'_>) -> crate::Result<Tensor> {
self.sin(read_tensor("sin", input)?)
}
fn cos(&mut self, input: &Tensor) -> crate::Result<Tensor>;
fn cos_read(&mut self, input: TensorRead<'_>) -> crate::Result<Tensor> {
self.cos(read_tensor("cos", input)?)
}
fn tanh(&mut self, input: &Tensor) -> crate::Result<Tensor>;
fn tanh_read(&mut self, input: TensorRead<'_>) -> crate::Result<Tensor> {
self.tanh(read_tensor("tanh", input)?)
}
fn sqrt(&mut self, input: &Tensor) -> crate::Result<Tensor>;
fn sqrt_read(&mut self, input: TensorRead<'_>) -> crate::Result<Tensor> {
self.sqrt(read_tensor("sqrt", input)?)
}
fn rsqrt(&mut self, input: &Tensor) -> crate::Result<Tensor>;
fn rsqrt_read(&mut self, input: TensorRead<'_>) -> crate::Result<Tensor> {
self.rsqrt(read_tensor("rsqrt", input)?)
}
fn pow(&mut self, lhs: &Tensor, rhs: &Tensor) -> crate::Result<Tensor>;
fn pow_read(&mut self, lhs: TensorRead<'_>, rhs: TensorRead<'_>) -> crate::Result<Tensor> {
self.pow(read_tensor("pow", lhs)?, read_tensor("pow", rhs)?)
}
fn expm1(&mut self, input: &Tensor) -> crate::Result<Tensor>;
fn expm1_read(&mut self, input: TensorRead<'_>) -> crate::Result<Tensor> {
self.expm1(read_tensor("expm1", input)?)
}
fn log1p(&mut self, input: &Tensor) -> crate::Result<Tensor>;
fn log1p_read(&mut self, input: TensorRead<'_>) -> crate::Result<Tensor> {
self.log1p(read_tensor("log1p", input)?)
}
}
pub trait TensorStructural {
fn transpose(&mut self, input: &Tensor, perm: &[usize]) -> crate::Result<Tensor>;
fn transpose_read(&mut self, input: TensorRead<'_>, perm: &[usize]) -> crate::Result<Tensor> {
self.transpose(read_tensor("transpose", input)?, perm)
}
fn reshape(&mut self, input: &Tensor, shape: &[usize]) -> crate::Result<Tensor>;
fn reshape_read(&mut self, input: TensorRead<'_>, shape: &[usize]) -> crate::Result<Tensor> {
self.reshape(read_tensor("reshape", input)?, shape)
}
fn broadcast_in_dim(
&mut self,
input: &Tensor,
shape: &[usize],
dims: &[usize],
) -> crate::Result<Tensor>;
fn broadcast_in_dim_read(
&mut self,
input: TensorRead<'_>,
shape: &[usize],
dims: &[usize],
) -> crate::Result<Tensor> {
self.broadcast_in_dim(read_tensor("broadcast_in_dim", input)?, shape, dims)
}
fn cast(&mut self, input: &Tensor, to: crate::DType) -> crate::Result<Tensor>;
fn convert(&mut self, input: &Tensor, to: crate::DType) -> crate::Result<Tensor> {
validate_convert_dtype("convert", input.dtype(), to)?;
self.cast(input, to)
}
fn extract_diagonal(
&mut self,
input: &Tensor,
axis_a: usize,
axis_b: usize,
) -> crate::Result<Tensor>;
fn embed_diagonal(
&mut self,
input: &Tensor,
axis_a: usize,
axis_b: usize,
) -> crate::Result<Tensor>;
fn tril(&mut self, input: &Tensor, k: i64) -> crate::Result<Tensor>;
fn triu(&mut self, input: &Tensor, k: i64) -> crate::Result<Tensor>;
}
pub trait TensorReduction {
fn reduce_sum(&mut self, input: &Tensor, axes: &[usize]) -> crate::Result<Tensor>;
fn reduce_sum_read(&mut self, input: TensorRead<'_>, axes: &[usize]) -> crate::Result<Tensor> {
match input.as_tensor() {
Some(input) => self.reduce_sum(input, axes),
None => Err(crate::Error::backend_failure(
"reduce_sum",
"backend does not accept borrowed tensor views at this execution boundary",
)),
}
}
fn reduce_prod(&mut self, input: &Tensor, axes: &[usize]) -> crate::Result<Tensor>;
fn reduce_prod_read(&mut self, input: TensorRead<'_>, axes: &[usize]) -> crate::Result<Tensor> {
match input.as_tensor() {
Some(input) => self.reduce_prod(input, axes),
None => Err(crate::Error::backend_failure(
"reduce_prod",
"backend does not accept borrowed tensor views at this execution boundary",
)),
}
}
fn reduce_max(&mut self, input: &Tensor, axes: &[usize]) -> crate::Result<Tensor>;
fn reduce_max_read(&mut self, input: TensorRead<'_>, axes: &[usize]) -> crate::Result<Tensor> {
match input.as_tensor() {
Some(input) => self.reduce_max(input, axes),
None => Err(crate::Error::backend_failure(
"reduce_max",
"backend does not accept borrowed tensor views at this execution boundary",
)),
}
}
fn reduce_min(&mut self, input: &Tensor, axes: &[usize]) -> crate::Result<Tensor>;
fn reduce_min_read(&mut self, input: TensorRead<'_>, axes: &[usize]) -> crate::Result<Tensor> {
match input.as_tensor() {
Some(input) => self.reduce_min(input, axes),
None => Err(crate::Error::backend_failure(
"reduce_min",
"backend does not accept borrowed tensor views at this execution boundary",
)),
}
}
}
pub trait TensorDot: TensorElementwise {
fn dot_general(
&mut self,
lhs: &Tensor,
rhs: &Tensor,
config: &DotGeneralConfig,
) -> crate::Result<Tensor>;
#[doc(hidden)]
fn dot_general_read(
&mut self,
lhs: TensorRead<'_>,
rhs: TensorRead<'_>,
config: &DotGeneralConfig,
) -> crate::Result<Tensor> {
match (lhs.as_tensor(), rhs.as_tensor()) {
(Some(lhs), Some(rhs)) => self.dot_general(lhs, rhs, config),
_ => {
let lhs = lhs.to_tensor()?;
let rhs = rhs.to_tensor()?;
self.dot_general(&lhs, &rhs, config)
}
}
}
#[doc(hidden)]
fn dot_general_read_into(
&mut self,
lhs: TensorRead<'_>,
rhs: TensorRead<'_>,
config: &DotGeneralConfig,
mut out: TensorWrite<'_>,
) -> crate::Result<()> {
validate_dot_general_read_into(&lhs, &rhs, config, &out, "dot_general")?;
let result = self.dot_general_read(lhs, rhs, config)?;
out.copy_from_tensor(&result)
}
#[doc(hidden)]
fn dot_general_with_conj(
&mut self,
lhs: &Tensor,
rhs: &Tensor,
config: &DotGeneralConfig,
lhs_conj: bool,
rhs_conj: bool,
) -> crate::Result<Tensor> {
if !lhs_conj && !rhs_conj {
return self.dot_general(lhs, rhs, config);
}
let lhs_tmp;
let lhs_ref = if lhs_conj {
lhs_tmp = self.conj(lhs)?;
&lhs_tmp
} else {
lhs
};
let rhs_tmp;
let rhs_ref = if rhs_conj {
rhs_tmp = self.conj(rhs)?;
&rhs_tmp
} else {
rhs
};
self.dot_general(lhs_ref, rhs_ref, config)
}
#[allow(clippy::too_many_arguments)]
#[doc(hidden)]
fn dot_general_with_conj_read(
&mut self,
lhs: TensorRead<'_>,
rhs: TensorRead<'_>,
config: &DotGeneralConfig,
lhs_conj: bool,
rhs_conj: bool,
) -> crate::Result<Tensor> {
if !lhs_conj && !rhs_conj {
return self.dot_general_read(lhs, rhs, config);
}
let lhs_tmp;
let lhs_ref = if let Some(tensor) = lhs.as_tensor() {
tensor
} else {
lhs_tmp = lhs.to_tensor()?;
&lhs_tmp
};
let rhs_tmp;
let rhs_ref = if let Some(tensor) = rhs.as_tensor() {
tensor
} else {
rhs_tmp = rhs.to_tensor()?;
&rhs_tmp
};
self.dot_general_with_conj(lhs_ref, rhs_ref, config, lhs_conj, rhs_conj)
}
}
pub trait SessionCachedDot: TensorDot {
#[doc(hidden)]
fn dot_general_cached(
&mut self,
_cache_slot: Option<usize>,
lhs: &Tensor,
rhs: &Tensor,
config: &DotGeneralConfig,
) -> crate::Result<Tensor> {
self.dot_general(lhs, rhs, config)
}
#[doc(hidden)]
fn dot_general_read_cached(
&mut self,
cache_slot: Option<usize>,
lhs: TensorRead<'_>,
rhs: TensorRead<'_>,
config: &DotGeneralConfig,
) -> crate::Result<Tensor> {
match (lhs.as_tensor(), rhs.as_tensor()) {
(Some(lhs), Some(rhs)) => self.dot_general_cached(cache_slot, lhs, rhs, config),
_ => {
let lhs = lhs.to_tensor()?;
let rhs = rhs.to_tensor()?;
self.dot_general_cached(cache_slot, &lhs, &rhs, config)
}
}
}
#[allow(clippy::too_many_arguments)]
#[doc(hidden)]
fn dot_general_with_conj_cached(
&mut self,
_cache_slot: Option<usize>,
lhs: &Tensor,
rhs: &Tensor,
config: &DotGeneralConfig,
lhs_conj: bool,
rhs_conj: bool,
) -> crate::Result<Tensor> {
self.dot_general_with_conj(lhs, rhs, config, lhs_conj, rhs_conj)
}
#[allow(clippy::too_many_arguments)]
#[doc(hidden)]
fn dot_general_with_conj_read_cached(
&mut self,
cache_slot: Option<usize>,
lhs: TensorRead<'_>,
rhs: TensorRead<'_>,
config: &DotGeneralConfig,
lhs_conj: bool,
rhs_conj: bool,
) -> crate::Result<Tensor> {
if !lhs_conj && !rhs_conj {
return self.dot_general_read_cached(cache_slot, lhs, rhs, config);
}
let lhs_tmp;
let lhs_ref = if let Some(tensor) = lhs.as_tensor() {
tensor
} else {
lhs_tmp = lhs.to_tensor()?;
&lhs_tmp
};
let rhs_tmp;
let rhs_ref = if let Some(tensor) = rhs.as_tensor() {
tensor
} else {
rhs_tmp = rhs.to_tensor()?;
&rhs_tmp
};
self.dot_general_with_conj_cached(cache_slot, lhs_ref, rhs_ref, config, lhs_conj, rhs_conj)
}
}
pub trait TensorIndexing {
fn gather(
&mut self,
operand: &Tensor,
start_indices: &Tensor,
config: &GatherConfig,
) -> crate::Result<Tensor>;
fn scatter(
&mut self,
operand: &Tensor,
scatter_indices: &Tensor,
updates: &Tensor,
config: &ScatterConfig,
) -> crate::Result<Tensor>;
fn slice(&mut self, input: &Tensor, config: &SliceConfig) -> crate::Result<Tensor>;
fn dynamic_slice(
&mut self,
input: &Tensor,
starts: &Tensor,
slice_sizes: &[usize],
) -> crate::Result<Tensor>;
fn dynamic_update_slice(
&mut self,
operand: &Tensor,
update: &Tensor,
starts: &Tensor,
) -> crate::Result<Tensor>;
fn pad(&mut self, input: &Tensor, config: &PadConfig) -> crate::Result<Tensor>;
fn concatenate(&mut self, inputs: &[&Tensor], axis: usize) -> crate::Result<Tensor>;
fn reverse(&mut self, input: &Tensor, axes: &[usize]) -> crate::Result<Tensor>;
}
pub trait TensorViewCanonicalization<T: Clone + 'static, R: TensorRank> {
fn to_contiguous(
&mut self,
view: &TypedTensorView<'_, T, R>,
) -> crate::Result<TypedTensor<T, R>>;
fn copy_from_contiguous(
&mut self,
src: &TypedTensor<T, R>,
dst: &mut TypedTensorViewMut<'_, T, R>,
) -> crate::Result<()>;
}
pub trait TensorFusion {
#[doc(hidden)]
fn execute_elementwise_fusion(
&mut self,
_inputs: &[&Tensor],
_plan: &ElementwiseFusionPlan,
) -> crate::Result<Option<Vec<Tensor>>> {
Ok(None)
}
#[doc(hidden)]
#[allow(clippy::too_many_arguments)]
fn execute_broadcast_multiply(
&mut self,
_lhs: TensorRead<'_>,
_lhs_shape: &[usize],
_lhs_dims: &[usize],
_rhs: TensorRead<'_>,
_rhs_shape: &[usize],
_rhs_dims: &[usize],
) -> crate::Result<Option<Tensor>> {
Ok(None)
}
#[doc(hidden)]
#[allow(clippy::too_many_arguments)]
fn execute_broadcast_multiply_value(
&mut self,
lhs: TensorRead<'_>,
lhs_shape: &[usize],
lhs_dims: &[usize],
rhs: TensorRead<'_>,
rhs_shape: &[usize],
rhs_dims: &[usize],
) -> crate::Result<Option<TensorValue>> {
self.execute_broadcast_multiply(lhs, lhs_shape, lhs_dims, rhs, rhs_shape, rhs_dims)
.map(|tensor| tensor.map(TensorValue::from_tensor))
}
}
pub trait TensorBuffer {
fn reclaim_buffer(&mut self, _tensor: Tensor) {}
}
pub trait TensorDeviceTransfer {
fn download_to_host(&mut self, tensor: &Tensor) -> crate::Result<Tensor> {
Ok(tensor.clone())
}
fn upload_host_tensor(&mut self, tensor: &Tensor) -> crate::Result<Tensor> {
Ok(tensor.clone())
}
}
pub trait BackendRuntimeCache {
#[doc(hidden)]
type RuntimeCache: RuntimeCacheControl + Send + Sync + 'static;
}
pub trait BackendCachedDot: BackendRuntimeCache + TensorDot {
#[doc(hidden)]
fn dot_general_cached(
&mut self,
_cache: &mut Self::RuntimeCache,
_cache_slot: Option<usize>,
lhs: &Tensor,
rhs: &Tensor,
config: &DotGeneralConfig,
) -> crate::Result<Tensor> {
self.dot_general(lhs, rhs, config)
}
#[doc(hidden)]
fn dot_general_read_cached(
&mut self,
cache: &mut Self::RuntimeCache,
cache_slot: Option<usize>,
lhs: TensorRead<'_>,
rhs: TensorRead<'_>,
config: &DotGeneralConfig,
) -> crate::Result<Tensor> {
match (lhs.as_tensor(), rhs.as_tensor()) {
(Some(lhs), Some(rhs)) => self.dot_general_cached(cache, cache_slot, lhs, rhs, config),
_ => {
let lhs = lhs.to_tensor()?;
let rhs = rhs.to_tensor()?;
self.dot_general_cached(cache, cache_slot, &lhs, &rhs, config)
}
}
}
#[allow(clippy::too_many_arguments)]
#[doc(hidden)]
fn dot_general_with_conj_cached(
&mut self,
_cache: &mut Self::RuntimeCache,
_cache_slot: Option<usize>,
lhs: &Tensor,
rhs: &Tensor,
config: &DotGeneralConfig,
lhs_conj: bool,
rhs_conj: bool,
) -> crate::Result<Tensor> {
self.dot_general_with_conj(lhs, rhs, config, lhs_conj, rhs_conj)
}
#[allow(clippy::too_many_arguments)]
#[doc(hidden)]
fn dot_general_with_conj_read_cached(
&mut self,
cache: &mut Self::RuntimeCache,
cache_slot: Option<usize>,
lhs: TensorRead<'_>,
rhs: TensorRead<'_>,
config: &DotGeneralConfig,
lhs_conj: bool,
rhs_conj: bool,
) -> crate::Result<Tensor> {
if !lhs_conj && !rhs_conj {
return self.dot_general_read_cached(cache, cache_slot, lhs, rhs, config);
}
let lhs_tmp;
let lhs_ref = if let Some(tensor) = lhs.as_tensor() {
tensor
} else {
lhs_tmp = lhs.to_tensor()?;
&lhs_tmp
};
let rhs_tmp;
let rhs_ref = if let Some(tensor) = rhs.as_tensor() {
tensor
} else {
rhs_tmp = rhs.to_tensor()?;
&rhs_tmp
};
self.dot_general_with_conj_cached(
cache, cache_slot, lhs_ref, rhs_ref, config, lhs_conj, rhs_conj,
)
}
}
pub trait BackendSessionHost: BackendRuntimeCache {
fn with_backend_session<R: Send>(
&mut self,
f: impl FnOnce(&mut dyn BackendSession) -> R + Send,
) -> R
where
Self: TensorBackend + Sized,
{
default_backend_session(self, f)
}
#[doc(hidden)]
fn with_backend_session_cached<R: Send>(
&mut self,
_cache: &mut Self::RuntimeCache,
f: impl FnOnce(&mut dyn BackendSession) -> R + Send,
) -> R
where
Self: TensorBackend + Sized,
{
self.with_backend_session(f)
}
}
#[doc(hidden)]
pub trait TensorBackendOps:
TensorElementwise
+ TensorAnalytic
+ TensorStructural
+ TensorReduction
+ TensorIndexing
+ TensorDot
+ TensorFusion
+ TensorBuffer
{
}
impl<T> TensorBackendOps for T where
T: TensorElementwise
+ TensorAnalytic
+ TensorStructural
+ TensorReduction
+ TensorIndexing
+ TensorDot
+ TensorFusion
+ TensorBuffer
+ ?Sized
{
}
pub trait BackendSession: TensorBackendOps + SessionCachedDot {}
impl<T> BackendSession for T where T: TensorBackendOps + SessionCachedDot + ?Sized {}
pub trait TensorBackend:
BackendRuntimeCache
+ TensorBackendOps
+ BackendCachedDot
+ TensorDeviceTransfer
+ BackendSessionHost
{
}
impl<T> SessionCachedDot for T where T: TensorBackend + ?Sized {}
pub fn default_backend_session<B: TensorBackend, R: Send>(
backend: &mut B,
f: impl FnOnce(&mut dyn BackendSession) -> R + Send,
) -> R {
f(backend)
}