#![allow(private_bounds)]
use std::iter::zip;
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
use crate::tensor::backend::Backend;
use crate::tensor::errors::OpError;
use crate::tensor::graph::NodeKind;
use crate::tensor::mem_formats::layout::Layout;
use crate::tensor::mem_formats::slice::SliceRange;
use crate::tensor::ops::capabilities::{CanMatMul, FloatLike, NumericOp};
use crate::tensor::ops::compute_layout;
use crate::tensor::ops::def_op::{OpKind, OpKindScalar};
use crate::tensor::skeleton::{
BakedPromise, BinaryResult, Clean, SkeletonPromise, SkeletonSlot, Tainting, UnaryResult,
};
use crate::tensor::traits::{Dimension, Numeric, Operand};
use crate::tensor::{CachedTensorPromise, Tensor, TensorPromise};
struct NodeWithLayout<T: Numeric, B: Backend> {
node: NodeKind<T, B>,
layout: Layout,
}
impl<T: Numeric, B: Backend> Dimension for NodeWithLayout<T, B> {
fn layout(&self) -> &Layout {
&self.layout
}
}
impl<T: Numeric, B: Backend> Operand<T, B> for NodeWithLayout<T, B> {
fn to_node(&self) -> NodeKind<T, B> {
self.node.clone()
}
}
impl<T: Numeric, B: Backend> Tainting for NodeWithLayout<T, B> {
type Mark = Clean;
}
#[inline]
fn find_broadcast_target(l1: &Layout, l2: &Layout) -> Vec<usize> {
let (largest, smallest) = if l1.shape().len() >= l2.shape().len() {
(l1, l2)
} else {
(l2, l1)
};
let largest_size = largest.shape().len();
debug_assert!(
largest.shape().len() >= smallest.shape().len(),
"broadcast helper precondition violated"
);
let diff = largest.shape().len() - smallest.shape().len();
let mut new_shape = vec![0_usize; largest_size];
for (i, (&dim1, &dim2)) in zip(l1.shape().iter().rev(), l2.shape().iter().rev()).enumerate() {
new_shape[largest_size - i - 1] = dim1.max(dim2);
}
new_shape[..diff].copy_from_slice(&largest.shape()[..diff]);
new_shape
}
#[inline]
fn find_broadcast_target_until_batch(l1: &Layout, l2: &Layout) -> Option<(Vec<usize>, Vec<usize>)> {
let (largest, smallest) = if l1.shape().len() >= l2.shape().len() {
(l1, l2)
} else {
(l2, l1)
};
let largest_size = largest.shape().len();
debug_assert!(
largest.shape().len() >= smallest.shape().len(),
"matmul-batch broadcast helper precondition violated"
);
let diff = largest.shape().len() - smallest.shape().len();
let smallest_diff: usize = 2.min(smallest.shape().len());
if largest_size <= 2 {
return None;
}
let mut new_l1_shape = vec![0_usize; largest_size];
let mut new_l2_shape = vec![0_usize; largest_size];
for dim in 0..smallest_diff {
new_l1_shape[largest_size - dim - 1] = l1.shape()[l1.shape().len() - dim - 1];
new_l2_shape[largest_size - dim - 1] = l2.shape()[l2.shape().len() - dim - 1];
}
for (i, (&dim1, &dim2)) in zip(l1.shape().iter().rev(), l2.shape().iter().rev())
.enumerate()
.skip(smallest_diff)
{
let max = dim1.max(dim2);
new_l1_shape[largest_size - i - 1] = max;
new_l2_shape[largest_size - i - 1] = max;
}
for dim in 0..diff {
let n = largest.shape()[dim];
new_l1_shape[dim] = n;
new_l2_shape[dim] = n;
}
Some((new_l1_shape, new_l2_shape))
}
#[inline]
fn is_blas_ready<T, B, D>(source: &D) -> bool
where
B: Backend,
D: Operand<T, B>,
{
let layout = source.layout();
if B::SUPPORTS_NON_CONTIGUOUS_MATMUL {
let last_axis = layout.stride().len() - 1;
return layout.stride()[last_axis] != 0 && layout.stride()[last_axis - 1] != 0;
}
B::SUPPORTS_2D_TRANSPOSED_MATMUL && layout.is_last_axes_transposed()
}
type NodeTransform<Output, Backend> = Result<
(
NodeWithLayout<Output, Backend>,
NodeWithLayout<Output, Backend>,
Layout,
),
OpError,
>;
#[inline]
fn apply_transform_to_pair<T, B, D1, D2, F, N1, N2, L>(
lhs: &D1,
rhs: &D2,
filter: F,
transform_l: N1,
transform_r: N2,
compute_output_layout: L,
) -> NodeTransform<T, B>
where
T: Numeric,
B: Backend,
D1: Operand<T, B>,
D2: Operand<T, B>,
F: FnOnce(&D1, &D2) -> (bool, bool),
N1: FnOnce(&D1) -> Result<TensorPromise<T, B>, OpError>,
N2: FnOnce(&D2) -> Result<TensorPromise<T, B>, OpError>,
L: FnOnce(&Layout, &Layout) -> Result<Layout, OpError>,
{
let (apply_l, apply_r) = filter(lhs, rhs);
let (node1, layout1, node2, layout2) = match (apply_l, apply_r) {
(false, false) => (
lhs.to_node(),
lhs.layout().clone(),
rhs.to_node(),
rhs.layout().clone(),
),
(true, false) => {
let temp = transform_l(lhs)?;
let layout = temp.layout().clone();
(
NodeKind::Node(temp.graph),
layout,
rhs.to_node(),
rhs.layout().clone(),
)
}
(false, true) => {
let temp = transform_r(rhs)?;
let layout = temp.layout().clone();
(
lhs.to_node(),
lhs.layout().clone(),
NodeKind::Node(temp.graph),
layout,
)
}
(true, true) => {
let temp1 = transform_l(lhs)?;
let layout1 = temp1.layout().clone();
let temp2 = transform_r(rhs)?;
let layout2 = temp2.layout().clone();
(
NodeKind::Node(temp1.graph),
layout1,
NodeKind::Node(temp2.graph),
layout2,
)
}
};
let layout = compute_output_layout(&layout1, &layout2)?;
Ok((
NodeWithLayout {
node: node1,
layout: layout1,
},
NodeWithLayout {
node: node2,
layout: layout2,
},
layout,
))
}
fn view_impl<T, B, D>(source: &D, shape: &[usize]) -> Result<TensorPromise<T, B>, OpError>
where
T: Numeric,
B: Backend,
D: Operand<T, B>,
{
let input = Box::new([source.to_node()]);
let layout = source.layout().view(shape)?;
Ok(TensorPromise::with_layout(
OpKind::View(layout.clone()),
input,
layout,
))
}
fn broadcast_impl<T, B, D>(source: &D, shape: &[usize]) -> Result<TensorPromise<T, B>, OpError>
where
T: Numeric,
B: Backend,
D: Operand<T, B>,
{
let input = Box::new([source.to_node()]);
let layout = source.layout().broadcast(shape)?;
Ok(TensorPromise::with_layout(
OpKind::Broadcast(layout.clone()),
input,
layout,
))
}
fn reshape_impl<T, B, D>(source: &D, shape: &[usize]) -> Result<TensorPromise<T, B>, OpError>
where
T: Numeric,
B: Backend,
D: Operand<T, B>,
{
let cont: TensorPromise<T, B> = as_contiguous_impl(source);
let layout = cont.graph.layout.view(shape)?;
let input = Box::new([NodeKind::Node(cont.graph)]);
Ok(TensorPromise::with_layout(
OpKind::View(layout.clone()),
input,
layout,
))
}
fn slice_impl<T, B, D>(source: &D, range: &[SliceRange]) -> Result<TensorPromise<T, B>, OpError>
where
T: Numeric,
B: Backend,
D: Operand<T, B>,
{
let input = Box::new([source.to_node()]);
let layout = source.layout().slice(range)?;
Ok(TensorPromise::with_layout(
OpKind::Slice(layout.clone()),
input,
layout,
))
}
fn transpose_impl<T, B, D>(source: &D) -> TensorPromise<T, B>
where
T: Numeric,
B: Backend,
D: Operand<T, B>,
{
let input = Box::new([source.to_node()]);
unsafe { TensorPromise::new(OpKind::Transpose, input).unwrap_unchecked() }
}
fn transpose_axes_impl<T, B, D>(source: &D, axes: &[usize]) -> Result<TensorPromise<T, B>, OpError>
where
T: Numeric,
B: Backend,
D: Operand<T, B>,
{
let input = Box::new([source.to_node()]);
let layout = source.layout().transpose_axes(axes)?;
Ok(TensorPromise::with_layout(
OpKind::TransposeAxes(layout.clone()),
input,
layout,
))
}
fn as_contiguous_impl<T, B, D>(source: &D) -> TensorPromise<T, B>
where
T: Numeric,
B: Backend,
D: Operand<T, B>,
{
let node = source.to_node();
unsafe { TensorPromise::new(OpKind::AsContiguous, Box::new([node])).unwrap_unchecked() }
}
fn add_scalar_impl<T, B, D>(lhs: &D, rhs: T) -> TensorPromise<T, B>
where
T: Numeric,
B: Backend,
D: Operand<T, B>,
{
unsafe {
TensorPromise::new(
OpKind::ScalarOp(OpKindScalar::AxBy(T::MUL_NEUTRAL, rhs)),
Box::new([lhs.to_node()]),
)
.unwrap_unchecked()
}
}
fn sub_scalar_impl<T, B, D>(lhs: &D, rhs: T) -> TensorPromise<T, B>
where
T: Numeric,
B: Backend,
D: Operand<T, B>,
T: Numeric + Neg<Output = T>,
{
unsafe {
TensorPromise::new(
OpKind::ScalarOp(OpKindScalar::AxBy(T::MUL_NEUTRAL, -rhs)),
Box::new([lhs.to_node()]),
)
.unwrap_unchecked()
}
}
fn mul_scalar_impl<T, B, D>(lhs: &D, rhs: T) -> TensorPromise<T, B>
where
T: Numeric,
B: Backend,
D: Operand<T, B>,
{
unsafe {
TensorPromise::new(
OpKind::ScalarOp(OpKindScalar::AxBy(rhs, T::SUM_NEUTRAL)),
Box::new([lhs.to_node()]),
)
.unwrap_unchecked()
}
}
fn div_scalar_impl<T, B, D>(lhs: &D, rhs: T) -> TensorPromise<T, B>
where
T: Numeric,
B: Backend,
D: Operand<T, B>,
{
if rhs == T::SUM_NEUTRAL {
panic!("cannot divide by zero. stop.")
}
unsafe {
TensorPromise::new(
OpKind::ScalarOp(OpKindScalar::AxBy(T::MUL_NEUTRAL / rhs, T::SUM_NEUTRAL)),
Box::new([lhs.to_node()]),
)
.unwrap_unchecked()
}
}
fn exp_impl<T, B, D>(source: &D) -> TensorPromise<T, B>
where
T: Numeric,
B: Backend,
D: Operand<T, B>,
{
let input = Box::new([source.to_node()]);
unsafe { TensorPromise::new(OpKind::ScalarOp(OpKindScalar::Exp), input).unwrap_unchecked() }
}
fn ln_impl<T, B, D>(source: &D) -> TensorPromise<T, B>
where
T: Numeric,
B: Backend,
D: Operand<T, B>,
{
let input = Box::new([source.to_node()]);
unsafe { TensorPromise::new(OpKind::ScalarOp(OpKindScalar::Ln), input).unwrap_unchecked() }
}
fn log2_impl<T, B, D>(source: &D) -> TensorPromise<T, B>
where
T: Numeric,
B: Backend,
D: Operand<T, B>,
{
let input = Box::new([source.to_node()]);
unsafe { TensorPromise::new(OpKind::ScalarOp(OpKindScalar::Log2), input).unwrap_unchecked() }
}
fn relu_impl<T, B, D>(source: &D) -> TensorPromise<T, B>
where
T: Numeric,
B: Backend,
D: Operand<T, B>,
{
let input = Box::new([source.to_node()]);
unsafe { TensorPromise::new(OpKind::ScalarOp(OpKindScalar::ReLU), input).unwrap_unchecked() }
}
fn tanh_impl<T, B, D>(source: &D) -> TensorPromise<T, B>
where
T: Numeric,
B: Backend,
D: Operand<T, B>,
{
let input = Box::new([source.to_node()]);
unsafe { TensorPromise::new(OpKind::ScalarOp(OpKindScalar::Tanh), input).unwrap_unchecked() }
}
fn add_tensor_impl<T, B, D1, D2>(lhs: &D1, rhs: &D2) -> TensorPromise<T, B>
where
T: Numeric,
B: Backend,
D1: Operand<T, B>,
D2: Operand<T, B>,
{
let target = find_broadcast_target(lhs.layout(), rhs.layout());
let result = apply_transform_to_pair(
lhs,
rhs,
|l, r| (l.layout().shape() != target, r.layout().shape() != target),
|x| broadcast_impl(x, &target),
|x| broadcast_impl(x, &target),
|l1, l2| compute_layout(&OpKind::<T>::Add, &[l1, l2]),
);
if let Err(err) = result {
panic!("{}", err);
}
let (lhs_b, rhs_b, layout) = unsafe { result.unwrap_unchecked() };
TensorPromise::with_layout(
OpKind::Add,
[lhs_b.to_node(), rhs_b.to_node()].into(),
layout,
)
}
fn sub_tensor_impl<T, B, D1, D2>(lhs: &D1, rhs: &D2) -> TensorPromise<T, B>
where
T: Numeric,
B: Backend,
D1: Operand<T, B>,
D2: Operand<T, B>,
{
let target = find_broadcast_target(lhs.layout(), rhs.layout());
let result = apply_transform_to_pair(
lhs,
rhs,
|l, r| (l.layout().shape() != target, r.layout().shape() != target),
|x| broadcast_impl(x, &target),
|x| broadcast_impl(x, &target),
|l1, l2| compute_layout(&OpKind::<T>::Sub, &[l1, l2]),
);
if let Err(err) = result {
panic!("{}", err);
}
let (lhs_b, rhs_b, layout) = unsafe { result.unwrap_unchecked() };
TensorPromise::with_layout(
OpKind::Sub,
[lhs_b.to_node(), rhs_b.to_node()].into(),
layout,
)
}
fn mul_tensor_impl<T, B, D1, D2>(lhs: &D1, rhs: &D2) -> TensorPromise<T, B>
where
T: Numeric,
B: Backend,
D1: Operand<T, B>,
D2: Operand<T, B>,
{
let target = find_broadcast_target(lhs.layout(), rhs.layout());
let result = apply_transform_to_pair(
lhs,
rhs,
|l, r| (l.layout().shape() != target, r.layout().shape() != target),
|x| broadcast_impl(x, &target),
|x| broadcast_impl(x, &target),
|l1, l2| compute_layout(&OpKind::<T>::Mul, &[l1, l2]),
);
if let Err(err) = result {
panic!("{}", err);
}
let (lhs_b, rhs_b, layout) = unsafe { result.unwrap_unchecked() };
TensorPromise::with_layout(
OpKind::Mul,
[lhs_b.to_node(), rhs_b.to_node()].into(),
layout,
)
}
fn div_tensor_impl<T, B, D1, D2>(lhs: &D1, rhs: &D2) -> TensorPromise<T, B>
where
T: Numeric,
B: Backend,
D1: Operand<T, B>,
D2: Operand<T, B>,
{
let target = find_broadcast_target(lhs.layout(), rhs.layout());
let result = apply_transform_to_pair(
lhs,
rhs,
|l, r| (l.layout().shape() != target, r.layout().shape() != target),
|x| broadcast_impl(x, &target),
|x| broadcast_impl(x, &target),
|l1, l2| compute_layout(&OpKind::<T>::Div, &[l1, l2]),
);
if let Err(err) = result {
panic!("{}", err);
}
let (lhs_b, rhs_b, layout) = unsafe { result.unwrap_unchecked() };
TensorPromise::with_layout(
OpKind::Div,
[lhs_b.to_node(), rhs_b.to_node()].into(),
layout,
)
}
fn matmul_core<T, B, D1, D2>(lhs: &D1, rhs: &D2) -> Result<TensorPromise<T, B>, OpError>
where
T: Numeric,
B: Backend,
D1: Operand<T, B>,
D2: Operand<T, B>,
{
let (lhs_c, rhs_c, _) = apply_transform_to_pair(
lhs,
rhs,
|l, r| (!is_blas_ready(l), !is_blas_ready(r)),
|x| Ok(as_contiguous_impl(x)),
|x| Ok(as_contiguous_impl(x)),
|l1, _| Ok(l1.clone()),
)?;
let target = find_broadcast_target_until_batch(lhs_c.layout(), rhs_c.layout());
let (lhs_b, rhs_b, layout) = apply_transform_to_pair(
&lhs_c,
&rhs_c,
|l, r| {
(
target
.as_ref()
.is_some_and(|target| l.layout().shape() != target.0),
target
.as_ref()
.is_some_and(|target| r.layout().shape() != target.1),
)
},
|x| broadcast_impl(x, unsafe { &target.as_ref().unwrap_unchecked().0 }),
|x| broadcast_impl(x, unsafe { &target.as_ref().unwrap_unchecked().1 }),
|l1, l2| compute_layout(&OpKind::<T>::MatMul(T::MUL_NEUTRAL), &[l1, l2]),
)?;
Ok(TensorPromise::with_layout(
OpKind::MatMul(T::MUL_NEUTRAL),
[lhs_b.to_node(), rhs_b.to_node()].into(),
layout,
))
}
fn drop_dim_from_end<T, B, D>(source: &D, from_end: usize) -> Result<TensorPromise<T, B>, OpError>
where
T: Numeric,
B: Backend,
D: Operand<T, B>,
{
let mut new_shape: Vec<usize> = source.layout().shape().to_vec();
new_shape.remove(new_shape.len() - 1 - from_end);
view_impl(source, &new_shape)
}
fn matmul_tensor_impl<T, B, D1, D2>(lhs: &D1, rhs: &D2) -> Result<TensorPromise<T, B>, OpError>
where
T: Numeric,
B: Backend,
D1: Operand<T, B>,
D2: Operand<T, B>,
{
match (lhs.layout().shape().len(), rhs.layout().shape().len()) {
(1, 1) => {
let lhs_p = reshape_impl(lhs, &[1, lhs.layout().shape()[0]])?;
let rhs_p = reshape_impl(rhs, &[rhs.layout().shape()[0], 1])?;
let result = matmul_core(&lhs_p, &rhs_p)?;
drop_dim_from_end(&result, 0)
}
(1, _) => {
let lhs_p = reshape_impl(lhs, &[1, lhs.layout().shape()[0]])?;
let result = matmul_core(&lhs_p, rhs)?;
drop_dim_from_end(&result, 1)
}
(_, 1) => {
let rhs_p = reshape_impl(rhs, &[rhs.layout().shape()[0], 1])?;
let result = matmul_core(lhs, &rhs_p)?;
drop_dim_from_end(&result, 0)
}
_ => matmul_core(lhs, rhs),
}
}
fn sum_impl<T, B, D>(source: &D) -> TensorPromise<T, B>
where
T: Numeric,
B: Backend,
D: Operand<T, B>,
{
let input = Box::new([source.to_node()]);
unsafe { TensorPromise::new(OpKind::Sum, input).unwrap_unchecked() }
}
fn sum_axis_impl<T, B, D>(
source: &D,
axis: isize,
keep_dims: bool,
) -> Result<TensorPromise<T, B>, OpError>
where
T: Numeric,
B: Backend,
D: Operand<T, B>,
{
let input = Box::new([source.to_node()]);
let op = OpKind::<T>::SumAxis(axis, keep_dims);
let layout = compute_layout(&op, &[source.layout()])?;
Ok(TensorPromise::with_layout(op, input, layout))
}
fn max_impl<T, B, D>(source: &D) -> TensorPromise<T, B>
where
T: Numeric,
B: Backend,
D: Operand<T, B>,
{
let input = Box::new([source.to_node()]);
unsafe { TensorPromise::new(OpKind::Max, input).unwrap_unchecked() }
}
fn max_axis_impl<T, B, D>(
source: &D,
axis: isize,
keep_dims: bool,
) -> Result<TensorPromise<T, B>, OpError>
where
T: Numeric,
B: Backend,
D: Operand<T, B>,
{
let input = Box::new([source.to_node()]);
let op = OpKind::<T>::MaxAxis(axis, keep_dims);
let layout = compute_layout(&op, &[source.layout()])?;
Ok(TensorPromise::with_layout(op, input, layout))
}
fn mean_impl<T, B, D>(source: &D) -> TensorPromise<T, B>
where
T: Numeric,
B: Backend,
D: Operand<T, B>,
{
let input = Box::new([source.to_node()]);
unsafe { TensorPromise::new(OpKind::Mean, input).unwrap_unchecked() }
}
fn mean_axis_impl<T, B, D>(
source: &D,
axis: isize,
keep_dims: bool,
) -> Result<TensorPromise<T, B>, OpError>
where
T: Numeric,
B: Backend,
D: Operand<T, B>,
{
let input = Box::new([source.to_node()]);
let op = OpKind::<T>::MeanAxis(axis, keep_dims);
let layout = compute_layout(&op, &[source.layout()])?;
Ok(TensorPromise::with_layout(op, input, layout))
}
macro_rules! impl_view {
($ty:ident) => {
impl<T, B> $ty<T, B>
where
T: Numeric,
B: Backend,
{
#[inline]
pub fn view(
&self,
shape: &[usize],
) -> Result<<$ty<T, B> as UnaryResult<T, B>>::Output, OpError> {
view_impl(self, shape).map(<$ty<T, B> as UnaryResult<T, B>>::wrap)
}
#[inline]
pub fn reshape(
&self,
shape: &[usize],
) -> Result<<$ty<T, B> as UnaryResult<T, B>>::Output, OpError> {
reshape_impl(self, shape).map(<$ty<T, B> as UnaryResult<T, B>>::wrap)
}
}
};
}
macro_rules! impl_slice {
($ty:ident) => {
impl<T, B> $ty<T, B>
where
T: Numeric,
B: Backend,
{
#[inline]
pub fn slice(
&self,
shape: &[SliceRange],
) -> Result<<$ty<T, B> as UnaryResult<T, B>>::Output, OpError> {
slice_impl(self, shape).map(<$ty<T, B> as UnaryResult<T, B>>::wrap)
}
}
};
}
macro_rules! impl_transpose {
($ty: ident) => {
impl<T, B> $ty<T, B>
where
T: Numeric,
B: Backend,
{
#[inline]
pub fn transpose(&self) -> <$ty<T, B> as UnaryResult<T, B>>::Output {
<$ty<T, B> as UnaryResult<T, B>>::wrap(transpose_impl(self))
}
}
};
}
macro_rules! impl_transpose_axes {
($ty:ident) => {
impl<T, B> $ty<T, B>
where
T: Numeric,
B: Backend,
{
#[inline]
pub fn transpose_axes(
&self,
axes: &[usize],
) -> Result<<$ty<T, B> as UnaryResult<T, B>>::Output, OpError> {
transpose_axes_impl(self, axes).map(<$ty<T, B> as UnaryResult<T, B>>::wrap)
}
}
};
}
macro_rules! impl_as_contiguous {
($ty: ident) => {
impl<T, B> $ty<T, B>
where
T: Numeric,
B: Backend,
{
#[inline]
pub fn as_contiguous(&self) -> <$ty<T, B> as UnaryResult<T, B>>::Output {
<$ty<T, B> as UnaryResult<T, B>>::wrap(as_contiguous_impl(self))
}
}
};
}
macro_rules! impl_broadcast {
($ty:ident) => {
impl<T, B> $ty<T, B>
where
T: Numeric,
B: Backend,
{
#[inline]
pub fn broadcast(
&self,
shape: &[usize],
) -> Result<<$ty<T, B> as UnaryResult<T, B>>::Output, OpError> {
broadcast_impl(self, shape).map(<$ty<T, B> as UnaryResult<T, B>>::wrap)
}
}
};
}
macro_rules! impl_reshape_like {
($ty:ident) => {
impl_view!($ty);
impl_slice!($ty);
impl_transpose!($ty);
impl_transpose_axes!($ty);
impl_as_contiguous!($ty);
impl_broadcast!($ty);
};
}
macro_rules! impl_add_scalar {
($ty:ident) => {
impl<T, B> Add<T> for &$ty<T, B>
where
T: NumericOp,
B: Backend,
{
type Output = <$ty<T, B> as UnaryResult<T, B>>::Output;
#[inline]
fn add(self, rhs: T) -> Self::Output {
<$ty<T, B> as UnaryResult<T, B>>::wrap(add_scalar_impl(self, rhs))
}
}
impl<T, B> Add<T> for $ty<T, B>
where
T: NumericOp,
B: Backend,
{
type Output = <$ty<T, B> as UnaryResult<T, B>>::Output;
#[inline]
fn add(self, rhs: T) -> Self::Output {
(&self).add(rhs)
}
}
};
}
macro_rules! impl_sub_scalar {
($ty:ident) => {
impl<T, B> Sub<T> for &$ty<T, B>
where
T: NumericOp + Neg<Output = T>,
B: Backend,
{
type Output = <$ty<T, B> as UnaryResult<T, B>>::Output;
#[inline]
fn sub(self, rhs: T) -> Self::Output {
<$ty<T, B> as UnaryResult<T, B>>::wrap(sub_scalar_impl(self, rhs))
}
}
impl<T, B> Sub<T> for $ty<T, B>
where
T: NumericOp + Neg<Output = T>,
B: Backend,
{
type Output = <$ty<T, B> as UnaryResult<T, B>>::Output;
#[inline]
fn sub(self, rhs: T) -> Self::Output {
(&self).sub(rhs)
}
}
};
}
macro_rules! impl_mul_scalar {
($ty:ident) => {
impl<T, B> Mul<T> for &$ty<T, B>
where
T: NumericOp,
B: Backend,
{
type Output = <$ty<T, B> as UnaryResult<T, B>>::Output;
#[inline]
fn mul(self, rhs: T) -> Self::Output {
<$ty<T, B> as UnaryResult<T, B>>::wrap(mul_scalar_impl(self, rhs))
}
}
impl<T, B> Mul<T> for $ty<T, B>
where
T: NumericOp,
B: Backend,
{
type Output = <$ty<T, B> as UnaryResult<T, B>>::Output;
#[inline]
fn mul(self, rhs: T) -> Self::Output {
(&self).mul(rhs)
}
}
};
}
macro_rules! impl_div_scalar {
($ty:ident) => {
impl<T, B> Div<T> for &$ty<T, B>
where
T: NumericOp,
B: Backend,
{
type Output = <$ty<T, B> as UnaryResult<T, B>>::Output;
#[inline]
fn div(self, rhs: T) -> Self::Output {
<$ty<T, B> as UnaryResult<T, B>>::wrap(div_scalar_impl(self, rhs))
}
}
impl<T, B> Div<T> for $ty<T, B>
where
T: NumericOp,
B: Backend,
{
type Output = <$ty<T, B> as UnaryResult<T, B>>::Output;
#[inline]
fn div(self, rhs: T) -> Self::Output {
(&self).div(rhs)
}
}
};
}
macro_rules! impl_exp {
($ty:ident) => {
impl<T, B> $ty<T, B>
where
T: FloatLike,
B: Backend,
{
#[inline]
pub fn exp(&self) -> <$ty<T, B> as UnaryResult<T, B>>::Output {
<$ty<T, B> as UnaryResult<T, B>>::wrap(exp_impl(self))
}
}
};
}
macro_rules! impl_ln {
($ty:ident) => {
impl<T, B> $ty<T, B>
where
T: FloatLike,
B: Backend,
{
#[inline]
pub fn ln(&self) -> <$ty<T, B> as UnaryResult<T, B>>::Output {
<$ty<T, B> as UnaryResult<T, B>>::wrap(ln_impl(self))
}
}
};
}
macro_rules! impl_log2 {
($ty:ident) => {
impl<T, B> $ty<T, B>
where
T: FloatLike,
B: Backend,
{
#[inline]
pub fn log2(&self) -> <$ty<T, B> as UnaryResult<T, B>>::Output {
<$ty<T, B> as UnaryResult<T, B>>::wrap(log2_impl(self))
}
}
};
}
macro_rules! impl_relu {
($ty:ident) => {
impl<T, B> $ty<T, B>
where
T: FloatLike,
B: Backend,
{
#[inline]
pub fn relu(&self) -> <$ty<T, B> as UnaryResult<T, B>>::Output {
<$ty<T, B> as UnaryResult<T, B>>::wrap(relu_impl(self))
}
}
};
}
macro_rules! impl_tanh {
($ty:ident) => {
impl<T, B> $ty<T, B>
where
T: FloatLike,
B: Backend,
{
#[inline]
pub fn tanh(&self) -> <$ty<T, B> as UnaryResult<T, B>>::Output {
<$ty<T, B> as UnaryResult<T, B>>::wrap(tanh_impl(self))
}
}
};
}
macro_rules! impl_unary_scalar_ops {
($ty:ident) => {
impl_exp!($ty);
impl_ln!($ty);
impl_log2!($ty);
impl_relu!($ty);
impl_tanh!($ty);
};
}
macro_rules! impl_op_scalar {
($ty:ident) => {
impl_add_scalar!($ty);
impl_sub_scalar!($ty);
impl_div_scalar!($ty);
impl_mul_scalar!($ty);
};
}
macro_rules! impl_add_assign_scalar {
($ty:ident) => {
impl<T, B> AddAssign<T> for $ty<T, B>
where
T: NumericOp,
B: Backend,
{
#[inline]
fn add_assign(&mut self, rhs: T) {
*self = add_scalar_impl(&*self, rhs);
}
}
};
}
macro_rules! impl_sub_assign_scalar {
($ty:ident) => {
impl<T, B> SubAssign<T> for $ty<T, B>
where
T: NumericOp + Neg<Output = T>,
B: Backend,
{
#[inline]
fn sub_assign(&mut self, rhs: T) {
*self = sub_scalar_impl(&*self, rhs);
}
}
};
}
macro_rules! impl_mul_assign_scalar {
($ty:ident) => {
impl<T, B> MulAssign<T> for $ty<T, B>
where
T: NumericOp,
B: Backend,
{
#[inline]
fn mul_assign(&mut self, rhs: T) {
*self = mul_scalar_impl(&*self, rhs);
}
}
};
}
macro_rules! impl_div_assign_scalar {
($ty:ident) => {
impl<T, B> DivAssign<T> for $ty<T, B>
where
T: NumericOp,
B: Backend,
{
#[inline]
fn div_assign(&mut self, rhs: T) {
*self = div_scalar_impl(&*self, rhs);
}
}
};
}
macro_rules! impl_op_assign_scalar {
($ty:ident) => {
impl_add_assign_scalar!($ty);
impl_sub_assign_scalar!($ty);
impl_mul_assign_scalar!($ty);
impl_div_assign_scalar!($ty);
};
}
macro_rules! impl_tensor_binop {
($trait:ident, $method:ident, $impl_fn:ident, $lhs:ident, $rhs:ident) => {
impl<T, B> $trait<&$rhs<T, B>> for &$lhs<T, B>
where
T: NumericOp,
B: Backend,
$lhs<T, B>: BinaryResult<$rhs<T, B>, T, B>,
{
type Output = <$lhs<T, B> as BinaryResult<$rhs<T, B>, T, B>>::Output;
#[inline]
fn $method(self, rhs: &$rhs<T, B>) -> Self::Output {
<$lhs<T, B> as BinaryResult<$rhs<T, B>, T, B>>::wrap($impl_fn(self, rhs))
}
}
impl<T, B> $trait<$rhs<T, B>> for &$lhs<T, B>
where
T: NumericOp,
B: Backend,
$lhs<T, B>: BinaryResult<$rhs<T, B>, T, B>,
{
type Output = <$lhs<T, B> as BinaryResult<$rhs<T, B>, T, B>>::Output;
#[inline]
fn $method(self, rhs: $rhs<T, B>) -> Self::Output {
<$lhs<T, B> as BinaryResult<$rhs<T, B>, T, B>>::wrap($impl_fn(self, &rhs))
}
}
impl<T, B> $trait<&$rhs<T, B>> for $lhs<T, B>
where
T: NumericOp,
B: Backend,
$lhs<T, B>: BinaryResult<$rhs<T, B>, T, B>,
{
type Output = <$lhs<T, B> as BinaryResult<$rhs<T, B>, T, B>>::Output;
#[inline]
fn $method(self, rhs: &$rhs<T, B>) -> Self::Output {
<$lhs<T, B> as BinaryResult<$rhs<T, B>, T, B>>::wrap($impl_fn(&self, rhs))
}
}
impl<T, B> $trait<$rhs<T, B>> for $lhs<T, B>
where
T: NumericOp,
B: Backend,
$lhs<T, B>: BinaryResult<$rhs<T, B>, T, B>,
{
type Output = <$lhs<T, B> as BinaryResult<$rhs<T, B>, T, B>>::Output;
#[inline]
fn $method(self, rhs: $rhs<T, B>) -> Self::Output {
<$lhs<T, B> as BinaryResult<$rhs<T, B>, T, B>>::wrap($impl_fn(&self, &rhs))
}
}
};
}
macro_rules! impl_tensor_ops {
($lhs:ident, $rhs:ident) => {
impl_tensor_binop!(Add, add, add_tensor_impl, $lhs, $rhs);
impl_tensor_binop!(Sub, sub, sub_tensor_impl, $lhs, $rhs);
impl_tensor_binop!(Mul, mul, mul_tensor_impl, $lhs, $rhs);
impl_tensor_binop!(Div, div, div_tensor_impl, $lhs, $rhs);
};
}
macro_rules! impl_tensor_ops_cross {
([$($ty:ident),+ $(,)?]) => {
impl_tensor_ops_cross!(@rows [$($ty),+] [$($ty),+]);
};
(@rows [$($lhs:ident),+] $rhs:tt) => {
$( impl_tensor_ops_cross!(@row $lhs $rhs); )+
};
(@row $lhs:ident [$($rhs:ident),+]) => {
$( impl_tensor_ops!($lhs, $rhs); )+
};
}
macro_rules! impl_matmul {
($ty:ident) => {
impl<T, B> $ty<T, B>
where
T: CanMatMul,
B: Backend,
{
#[inline]
pub fn matmul<D>(
&self,
rhs: &D,
) -> Result<<$ty<T, B> as BinaryResult<D, T, B>>::Output, OpError>
where
D: Operand<T, B>,
$ty<T, B>: BinaryResult<D, T, B>,
{
matmul_tensor_impl(self, rhs).map(<$ty<T, B> as BinaryResult<D, T, B>>::wrap)
}
}
};
}
macro_rules! impl_sum {
($ty:ident) => {
impl<T, B> $ty<T, B>
where
T: NumericOp,
B: Backend,
{
#[inline]
pub fn sum(&self) -> <$ty<T, B> as UnaryResult<T, B>>::Output {
<$ty<T, B> as UnaryResult<T, B>>::wrap(sum_impl(self))
}
}
};
}
macro_rules! impl_sum_axis {
($ty:ident) => {
impl<T, B> $ty<T, B>
where
T: NumericOp,
B: Backend,
{
#[inline]
pub fn sum_axis(
&self,
axis: isize,
keep_dims: bool,
) -> Result<<$ty<T, B> as UnaryResult<T, B>>::Output, OpError> {
sum_axis_impl(self, axis, keep_dims).map(<$ty<T, B> as UnaryResult<T, B>>::wrap)
}
}
};
}
macro_rules! impl_max {
($ty:ident) => {
impl<T, B> $ty<T, B>
where
T: NumericOp,
B: Backend,
{
#[inline]
pub fn max(&self) -> <$ty<T, B> as UnaryResult<T, B>>::Output {
<$ty<T, B> as UnaryResult<T, B>>::wrap(max_impl(self))
}
}
};
}
macro_rules! impl_max_axis {
($ty:ident) => {
impl<T, B> $ty<T, B>
where
T: NumericOp,
B: Backend,
{
#[inline]
pub fn max_axis(
&self,
axis: isize,
keep_dims: bool,
) -> Result<<$ty<T, B> as UnaryResult<T, B>>::Output, OpError> {
max_axis_impl(self, axis, keep_dims).map(<$ty<T, B> as UnaryResult<T, B>>::wrap)
}
}
};
}
macro_rules! impl_mean {
($ty:ident) => {
impl<T, B> $ty<T, B>
where
T: FloatLike,
B: Backend,
{
#[inline]
pub fn mean(&self) -> <$ty<T, B> as UnaryResult<T, B>>::Output {
<$ty<T, B> as UnaryResult<T, B>>::wrap(mean_impl(self))
}
}
};
}
macro_rules! impl_mean_axis {
($ty:ident) => {
impl<T, B> $ty<T, B>
where
T: FloatLike,
B: Backend,
{
#[inline]
pub fn mean_axis(
&self,
axis: isize,
keep_dims: bool,
) -> Result<<$ty<T, B> as UnaryResult<T, B>>::Output, OpError> {
mean_axis_impl(self, axis, keep_dims).map(<$ty<T, B> as UnaryResult<T, B>>::wrap)
}
}
};
}
macro_rules! impl_tensor_assign_binop {
($trait:ident, $method:ident, $impl_fn:ident, $rhs:ident) => {
impl<T, B> $trait<$rhs<T, B>> for TensorPromise<T, B>
where
T: NumericOp,
B: Backend,
{
#[inline]
fn $method(&mut self, rhs: $rhs<T, B>) {
*self = $impl_fn(&*self, &rhs);
}
}
impl<T, B> $trait<&$rhs<T, B>> for TensorPromise<T, B>
where
T: NumericOp,
B: Backend,
{
#[inline]
fn $method(&mut self, rhs: &$rhs<T, B>) {
*self = $impl_fn(&*self, rhs);
}
}
};
}
macro_rules! impl_tensor_assign_ops {
($rhs:ident) => {
impl_tensor_assign_binop!(AddAssign, add_assign, add_tensor_impl, $rhs);
impl_tensor_assign_binop!(SubAssign, sub_assign, sub_tensor_impl, $rhs);
impl_tensor_assign_binop!(MulAssign, mul_assign, mul_tensor_impl, $rhs);
impl_tensor_assign_binop!(DivAssign, div_assign, div_tensor_impl, $rhs);
};
}
macro_rules! impl_all_ops {
($ty:ident) => {
impl_reshape_like!($ty);
impl_unary_scalar_ops!($ty);
impl_op_scalar!($ty);
impl_matmul!($ty);
impl_sum!($ty);
impl_sum_axis!($ty);
impl_max!($ty);
impl_max_axis!($ty);
impl_mean!($ty);
impl_mean_axis!($ty);
};
}
impl_all_ops!(Tensor);
impl_all_ops!(TensorPromise);
impl_all_ops!(CachedTensorPromise);
impl_all_ops!(BakedPromise);
impl_all_ops!(SkeletonSlot);
impl_all_ops!(SkeletonPromise);
impl_tensor_ops_cross!([
Tensor,
TensorPromise,
CachedTensorPromise,
BakedPromise,
SkeletonSlot,
SkeletonPromise,
]);
impl_op_assign_scalar!(TensorPromise);
impl_tensor_assign_ops!(Tensor);
impl_tensor_assign_ops!(TensorPromise);
impl_tensor_assign_ops!(CachedTensorPromise);
impl_tensor_assign_ops!(BakedPromise);