use crate::prelude::*;
pub fn std_axis<T, const I: isize>(t: T, epsilon: T::Dtype) -> T::Reduced
where
T: Reduce1<I>,
T::Array: HasAxis<I>,
{
sqrt(add_scalar(var_axis::<T, I>(t), epsilon))
}
pub fn var_axis<T, const I: isize>(t: T) -> T::Reduced
where
T: Reduce1<I>,
T::Array: HasAxis<I>,
{
let num_elements: f32 = <T::Array as HasAxis<I>>::SIZE as f32;
let (t, tape) = t.split_tape();
let mean = mean_axis::<T, I>(t.duplicate().put_tape(tape)).broadcast1();
div_scalar(sum_axis::<T, I>(square(sub(mean, &t))), num_elements)
}
macro_rules! impl_std_and_var {
($typename:ident, [$($Vs:tt),*]) => {
impl<$(const $Vs: usize, )* H: Tape> $typename<$($Vs, )* H> {
pub fn std_axis<const I: isize>(self, epsilon: f32) -> <Self as Reduce1<I>>::Reduced
where
Self: Reduce1<I>,
<Self as HasArrayType>::Array: HasAxis<I>,
{
std_axis::<Self, I>(self, epsilon)
}
pub fn var_axis<const I: isize>(self) -> <Self as Reduce1<I>>::Reduced
where
Self: Reduce1<I>,
<Self as HasArrayType>::Array: HasAxis<I>,
{
var_axis::<Self, I>(self)
}
}
};
}
impl_std_and_var!(Tensor1D, [M]);
impl_std_and_var!(Tensor2D, [M, N]);
impl_std_and_var!(Tensor3D, [M, N, O]);
impl_std_and_var!(Tensor4D, [M, N, O, P]);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_valids_var_axis() {
let _: Tensor0D = Tensor1D::<5>::zeros().var_axis::<-1>();
let _: Tensor1D<3> = Tensor2D::<5, 3>::zeros().var_axis::<0>();
let _: Tensor1D<5> = Tensor2D::<5, 3>::zeros().var_axis::<-1>();
let _: Tensor2D<5, 3> = Tensor3D::<7, 5, 3>::zeros().var_axis::<0>();
let _: Tensor2D<7, 3> = Tensor3D::<7, 5, 3>::zeros().var_axis::<1>();
let _: Tensor2D<7, 5> = Tensor3D::<7, 5, 3>::zeros().var_axis::<-1>();
let _: Tensor3D<7, 5, 3> = Tensor4D::<9, 7, 5, 3>::zeros().var_axis::<0>();
let _: Tensor3D<9, 5, 3> = Tensor4D::<9, 7, 5, 3>::zeros().var_axis::<1>();
let _: Tensor3D<9, 7, 3> = Tensor4D::<9, 7, 5, 3>::zeros().var_axis::<2>();
let _: Tensor3D<9, 7, 5> = Tensor4D::<9, 7, 5, 3>::zeros().var_axis::<-1>();
}
#[test]
fn test_var_axis_0_2d() {
let t: Tensor2D<2, 4> = Tensor2D::new([[1.0, 2.0, 3.0, 4.0], [0.0, 2.0, 5.0, 10.0]]);
let r = t.trace().var_axis::<0>();
assert_eq!(r.data(), &[0.25, 0.0, 1.0, 9.0]);
let gradients = r.mean().backward();
assert_eq!(
gradients.ref_gradient(&t),
&[[0.125, 0.0, -0.25, -0.75], [-0.125, 0.0, 0.25, 0.75]]
);
}
#[test]
fn test_var_axis_1_2d() {
let t: Tensor2D<2, 4> = Tensor2D::new([[1.0, 2.0, 3.0, 4.0], [0.0, 2.0, 5.0, 10.0]]);
let r: Tensor1D<2, OwnedTape> = t.trace().var_axis::<-1>();
assert_eq!(r.data(), &[1.25, 14.1875]);
let gradients = r.mean().backward();
assert_eq!(
gradients.ref_gradient(&t),
&[
[-0.375, -0.125, 0.125, 0.375],
[-1.0625, -0.5625, 0.1875, 1.4375]
]
);
}
#[test]
fn test_std_axis_0_2d() {
let t: Tensor2D<2, 4> = Tensor2D::new([[1.0, 2.0, 3.0, 4.0], [0.0, 2.0, 5.0, 10.0]]);
let r = t.trace().std_axis::<0>(1e-8);
assert_eq!(r.data(), &[0.5, 0.0001, 1.0, 3.0]);
let gradients = r.mean().backward();
assert_eq!(
gradients.ref_gradient(&t),
&[[0.125, 0.0, -0.125, -0.125], [-0.125, 0.0, 0.125, 0.125]]
);
}
#[test]
fn test_std_axis_1_2d() {
let t: Tensor2D<2, 4> = Tensor2D::new([[1.0, 2.0, 3.0, 4.0], [0.0, 2.0, 5.0, 10.0]]);
let r: Tensor1D<2, OwnedTape> = t.trace().std_axis::<-1>(0.0);
assert_eq!(r.data(), &[1.118034, 3.7666297]);
let gradients = r.mean().backward();
assert_eq!(
gradients.ref_gradient(&t),
&[
[-0.16770509, -0.0559017, 0.0559017, 0.16770509],
[-0.14104122, -0.07466887, 0.024889633, 0.19082046]
]
);
}
}