Refactor/extract tch backend (#103)

This commit is contained in:
Nathaniel Simard 2022-11-15 21:06:40 -05:00 committed by GitHub
parent 23677b8e89
commit ab51c22a55
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
123 changed files with 1805 additions and 1637 deletions

38
.github/workflows/test-burn-tch.yml vendored Normal file
View File

@ -0,0 +1,38 @@
name: test
on: [push]
jobs:
publish:
name: test burn tch
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v2
- name: install rust nightly
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
components: rustfmt, clippy
override: true
- name: check format
run: |
cd burn-tch
cargo fmt --check --all
- name: check doc
run: |
cd burn-tch
cargo test --no-default-features --features doc --doc
- name: check tests
run: |
cd burn-tch
cargo test --tests
- name: check clippy
run: |
cargo clippy -p burn-tch -- -D warnings

View File

@ -26,22 +26,13 @@ jobs:
- name: check doc
run: |
cd burn-tensor
cargo test --no-default-features --features doc --doc
cargo test --doc
- name: check tests backend ndarray
run: |
cd burn-tensor
cargo test --no-default-features --features ndarray --tests
- name: check tests backend tch
run: |
cd burn-tensor
cargo test --no-default-features --features tch --tests
- name: check clippy backend tch
run: |
cargo clippy -p burn-tensor --no-default-features --features tch -- -D warnings
cargo test --no-default-features --features ndarray export_tests --tests
- name: check clippy backend ndarray
run: |
cargo clippy -p burn-tensor --no-default-features --features tch -- -D warnings
cargo clippy -p burn-tensor --no-default-features --features ndarray -- -D warnings

View File

@ -27,7 +27,7 @@ jobs:
- name: check doc
run: |
cd burn
cargo test --no-default-features --features doc --doc
cargo test --doc
- name: check tests
run: |

View File

@ -4,5 +4,6 @@ members = [
"burn-derive",
"burn-tensor",
"burn-dataset",
"burn-tch",
"examples/*",
]

27
burn-tch/Cargo.toml Normal file
View File

@ -0,0 +1,27 @@
[package]
name = "burn-tch"
version = "0.2.3"
authors = ["nathanielsimard <nathaniel.simard.42@gmail.com>"]
description = "Tch backend for burn"
repository = "https://github.com/burn-rs/burn/tree/main/burn-tch"
readme="README.md"
keywords = ["deep-learning", "machine-learning", "data"]
categories = ["science"]
license = "MIT/Apache-2.0"
edition = "2021"
[features]
doc = ["tch/doc-only"]
[dependencies]
burn-tensor = { path = "../burn-tensor", version = "0.2.3", default-features = false }
rand = "0.8"
num-traits = "0.2"
tch = { version = "0.8" }
serde = { version = "1.0", features = ["derive"] }
lazy_static = "1.4"
half = { version = "1.6", features = ["num-traits"] } # needs to be 1.6 to work with tch
[dev-dependencies]
burn-tensor = { path = "../burn-tensor", version = "0.2.3", default-features = false, features = ["export_tests"] }

1
burn-tch/LICENSE-APACHE Symbolic link
View File

@ -0,0 +1 @@
../LICENSE-APACHE

1
burn-tch/LICENSE-MIT Symbolic link
View File

@ -0,0 +1 @@
../LICENSE-MIT

3
burn-tch/README.md Normal file
View File

@ -0,0 +1,3 @@
# Burn-tch
Tch backend for burn.

View File

@ -1,7 +1,7 @@
use super::element::TchElement;
use super::TchTensor;
use crate::tensor::backend::Backend;
use crate::tensor::{Data, Distribution, Shape};
use burn_tensor::backend::Backend;
use burn_tensor::{Data, Distribution, Shape};
#[derive(Clone, Copy, Debug)]
/// The device struct when using the `tch` backend.
@ -11,7 +11,7 @@ use crate::tensor::{Data, Distribution, Shape};
/// # Example
///
/// ```rust
/// use burn_tensor::backend::TchDevice;
/// use burn_tch::TchDevice;
///
/// let device_gpu_1 = TchDevice::Cuda(0); // First GPU
/// let device_gpu_2 = TchDevice::Cuda(1); // Second GPU

14
burn-tch/src/element.rs Normal file
View File

@ -0,0 +1,14 @@
use burn_tensor::Element;
use half::f16;
pub trait TchElement: Element + tch::kind::Element {}
impl TchElement for f64 {}
impl TchElement for f32 {}
impl TchElement for f16 {}
impl TchElement for i64 {}
impl TchElement for i32 {}
impl TchElement for i16 {}
impl TchElement for u8 {}

View File

@ -8,3 +8,10 @@ mod tensor_ops;
pub use backend::*;
pub use tensor::*;
pub use tensor_ops::*;
#[cfg(test)]
mod tests {
type TestBackend = crate::TchBackend<f32>;
burn_tensor::test_all!();
}

View File

@ -1,5 +1,5 @@
use super::{element::TchElement, TchBackend, TchTensor};
use crate::{ops::ModuleOps, Shape};
use burn_tensor::{ops::ModuleOps, Shape};
impl<E: TchElement> ModuleOps<TchBackend<E>> for TchBackend<E> {
fn embedding(weights: &TchTensor<E, 2>, indexes: &TchTensor<i64, 2>) -> TchTensor<E, 3> {

View File

@ -1,4 +1,5 @@
use crate::tensor::{backend::tch::TchTensor, ops::*};
use crate::TchTensor;
use burn_tensor::ops::*;
impl<P, const D: usize> Zeros for TchTensor<P, D>
where

View File

@ -1,9 +1,5 @@
use super::element::TchElement;
use crate::{
backend::{TchBackend, TchDevice},
ops::TensorOps,
tensor::{Data, Shape},
};
use crate::{element::TchElement, TchBackend, TchDevice};
use burn_tensor::{ops::TensorOps, Data, Shape};
lazy_static::lazy_static! {
static ref NO_GRAD: tch::NoGradGuard = {
@ -53,16 +49,6 @@ impl<const D: usize> From<Shape<D>> for TchShape<D> {
}
}
impl<const D: usize> From<Vec<i64>> for Shape<D> {
fn from(shape: Vec<i64>) -> Self {
let mut dims = [1; D];
for (i, dim) in shape.into_iter().enumerate() {
dims[i] = dim as usize;
}
Self::new(dims)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
pub struct TchKind<P: tch::kind::Element> {
_p: P,
@ -99,12 +85,12 @@ impl<P: tch::kind::Element + Default, const D: usize> TchTensor<P, D> {
#[cfg(test)]
mod utils {
use super::*;
use crate::{backend::TchBackend, ops::TensorOps};
use crate::{backend::TchBackend, element::TchElement};
impl<P: tch::kind::Element, const D: usize> TchTensor<P, D> {
impl<P: TchElement, const D: usize> TchTensor<P, D> {
pub(crate) fn into_data(self) -> Data<P, D>
where
P: TchElement,
P: tch::kind::Element,
{
<TchBackend<P> as TensorOps<TchBackend<P>>>::into_data(self)
}
@ -131,7 +117,7 @@ impl<P: tch::kind::Element + Default + Copy + std::fmt::Debug, const D: usize> T
#[cfg(test)]
mod tests {
use super::*;
use crate::tensor::Distribution;
use burn_tensor::Distribution;
use rand::prelude::StdRng;
use rand::SeedableRng;

View File

@ -1,5 +1,5 @@
use super::{element::TchElement, TchBackend, TchDevice, TchKind, TchShape, TchTensor};
use crate::{backend::Backend, ops::TensorOps, Data, ElementConversion, Shape};
use burn_tensor::{backend::Backend, ops::TensorOps, Data, ElementConversion, Shape};
use std::ops::{Add, Div, Mul, Range, Sub};
impl<E: TchElement> TensorOps<TchBackend<E>> for TchBackend<E> {

View File

@ -20,10 +20,9 @@ all-features = false
no-default-features = true
[features]
default = ["tch", "ndarray"]
tch = ["dep:tch"]
default = ["ndarray"]
ndarray = ["dep:ndarray", "dep:libm"]
doc = ["dep:tch", "tch/doc-only", "dep:ndarray"]
export_tests = []
[dependencies]
num-traits = "0.2"
@ -31,12 +30,6 @@ derive-new = "0.5"
rand = "0.8"
half = { version = "1.6", features = ["num-traits"] } # needs to be 1.6 to work with tch
# Backends
tch = { version = "0.8", optional = true }
lazy_static = "1.4"
# NdArray
ndarray = { version = "0.15", optional = true }
libm = { version = "0.2", optional = true }

View File

@ -43,7 +43,7 @@ fn main() {
#[cfg(feature = "ndarray")]
{
run::<backend::NdArrayBackend<f32>>(x.clone(), y.clone());
run_ad::<backend::NdArrayADBackend<f32>>(x.clone(), y.clone());
run_ad::<backend::NdArrayADBackend<f32>>(x, y);
}
#[cfg(feature = "tch")]

View File

@ -6,5 +6,8 @@ pub use graph::grad::Gradients;
mod tensor;
#[cfg(feature = "export_tests")]
mod tests;
pub use half::f16;
pub use tensor::*;

View File

@ -90,6 +90,3 @@ impl<B: Backend> ADBackend for ADBackendDecorator<B> {
#[cfg(feature = "ndarray")]
pub type ADBackendNdArray<E> =
ADBackendDecorator<crate::tensor::backend::ndarray::NdArrayBackend<E>>;
#[cfg(feature = "tch")]
pub type ADBackendTch<E> = ADBackendDecorator<crate::tensor::backend::tch::TchBackend<E>>;

View File

@ -41,14 +41,4 @@ pub mod helper {
pub type TestADTensor<E, const D: usize> = Tensor<ADBackendNdArray<E>, D>;
}
pub use helper_impl::*;
#[cfg(feature = "tch")]
#[cfg(not(feature = "ndarray"))]
mod helper_impl {
use crate::tensor::backend::autodiff::ADBackendTch;
use crate::tensor::Tensor;
pub type TestADTensor<E, const D: usize> = Tensor<ADBackendTch<E>, D>;
}
pub use helper_impl::*;
}

View File

@ -7,16 +7,6 @@ pub use base::*;
pub(crate) mod autodiff;
#[cfg(feature = "tch")]
pub(crate) mod tch;
#[cfg(feature = "tch")]
pub type TchADBackend<E> = self::autodiff::ADBackendTch<E>;
#[cfg(feature = "tch")]
pub type TchBackend<E> = self::tch::TchBackend<E>;
#[cfg(feature = "tch")]
pub type TchDevice = self::tch::TchDevice;
#[cfg(feature = "ndarray")]
pub(crate) mod ndarray;
#[cfg(feature = "ndarray")]
@ -25,3 +15,5 @@ pub type NdArrayADBackend<E> = self::autodiff::ADBackendNdArray<E>;
pub type NdArrayBackend<E> = self::ndarray::NdArrayBackend<E>;
#[cfg(feature = "ndarray")]
pub type NdArrayDevice = self::ndarray::NdArrayDevice;
pub use autodiff::ADBackendDecorator;

View File

@ -1,32 +0,0 @@
use crate::ops::{Ones, Zeros};
use crate::{
make_element, Distribution, Element, ElementConversion, ElementPrecision, ElementRandom,
ElementValue, Precision,
};
use half::f16;
use num_traits::ToPrimitive;
use rand::rngs::StdRng;
pub(crate) trait TchElement: Element + tch::kind::Element {}
impl TchElement for f64 {}
impl TchElement for f32 {}
impl TchElement for f16 {}
impl TchElement for i64 {}
impl TchElement for i32 {}
impl TchElement for i16 {}
impl TchElement for u8 {}
make_element!(
ty f16 Precision::Half,
zero <f16 as num_traits::Zero>::zero(),
one <f16 as num_traits::One>::one(),
convert |elem: &dyn ToPrimitive| f16::from_f32(elem.to_f32().unwrap()),
random |distribution: Distribution<f16>, rng: &mut StdRng| {
let distribution: Distribution<f32> = distribution.convert();
let sample = distribution.sampler(rng).sample();
f16::from_elem(sample)
}
);

View File

@ -1,4 +1,5 @@
use crate::{tensor::ops::*, Distribution};
use half::f16;
use num_traits::ToPrimitive;
use rand::prelude::StdRng;
@ -182,3 +183,14 @@ make_element!(
convert |elem: &dyn ToPrimitive| elem.to_u8().unwrap(),
random |distribution: Distribution<u8>, rng: &mut StdRng| distribution.sampler(rng).sample()
);
make_element!(
ty f16 Precision::Half,
zero <f16 as num_traits::Zero>::zero(),
one <f16 as num_traits::One>::one(),
convert |elem: &dyn ToPrimitive| f16::from_f32(elem.to_f32().unwrap()),
random |distribution: Distribution<f16>, rng: &mut StdRng| {
let distribution: Distribution<f32> = distribution.convert();
let sample = distribution.sampler(rng).sample();
f16::from_elem(sample)
}
);

View File

@ -1,4 +1,4 @@
pub(crate) mod ops;
pub mod ops;
pub(crate) mod stats;
mod base;

View File

@ -22,6 +22,16 @@ impl<const D: usize> From<[usize; D]> for Shape<D> {
}
}
impl<const D: usize> From<Vec<i64>> for Shape<D> {
fn from(shape: Vec<i64>) -> Self {
let mut dims = [1; D];
for (i, dim) in shape.into_iter().enumerate() {
dims[i] = dim as usize;
}
Self::new(dims)
}
}
impl<const D1: usize> Shape<D1> {
pub fn index<const D2: usize>(&self, indexes: [Range<usize>; D2]) -> Self {
if D2 > D1 {

View File

@ -0,0 +1,19 @@
#[macro_export]
macro_rules! test_gelu {
() => {
#[test]
fn test_gelu() {
let data = Data::from([[
0.5447, 0.9809, 0.4114, 0.1398, 0.8045, 0.4103, 0.2388, 0.5262, 0.6677, 0.6737,
]]);
let tensor = Tensor::<TestBackend, 2>::from_data(data);
let data_actual = activation::gelu(&tensor).to_data();
let data_expected = Data::from([[
0.3851, 0.8207, 0.2714, 0.0777, 0.6351, 0.2704, 0.1419, 0.3687, 0.4993, 0.5051,
]]);
data_expected.assert_approx_eq(&data_actual, 3);
}
};
}

View File

@ -0,0 +1,15 @@
#[macro_export]
macro_rules! test_relu {
() => {
#[test]
fn test_relu_d2() {
let data = Data::from([[0.0, -1.0, 2.0], [3.0, -4.0, 5.0]]);
let tensor = Tensor::<TestBackend, 2>::from_data(data);
let data_actual = activation::relu(&tensor).to_data();
let data_expected = Data::from([[0.0, 0.0, 2.0], [3.0, 0.0, 5.0]]);
assert_eq!(data_expected, data_actual);
}
};
}

View File

@ -0,0 +1,15 @@
#[macro_export]
macro_rules! test_softmax {
() => {
#[test]
fn test_softmax_d2() {
let data = Data::from([[1.0, 7.0], [13.0, -3.0]]);
let tensor = Tensor::<TestBackend, 2>::from_data(data);
let data_actual = activation::softmax(&tensor, 1).to_data();
let data_expected = Data::from([[2.47e-03, 9.975e-01], [1.0, 1.1254e-07]]);
data_actual.assert_approx_eq(&data_expected, 4);
}
};
}

View File

@ -0,0 +1,64 @@
#[macro_export]
macro_rules! test_ad_add {
() => {
#[test]
fn should_diff_add() {
let data_1 = Data::from([2.0, 5.0]);
let data_2 = Data::from([4.0, 1.0]);
let tensor_1 = Tensor::<TestADBackend, 1>::from_data(data_1);
let tensor_2 = Tensor::<TestADBackend, 1>::from_data(data_2);
let tensor_3 = tensor_1.clone() + tensor_2.clone();
let grads = tensor_3.backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
assert_eq!(grad_1.to_data(), Data::from([1.0, 1.0]));
assert_eq!(grad_2.to_data(), Data::from([1.0, 1.0]));
assert_eq!(tensor_3.into_data(), Data::from([6.0, 6.0]));
}
#[test]
fn should_diff_add_scalar() {
let data = Data::from([2.0, 10.0]);
let tensor = Tensor::<TestADBackend, 1>::from_data(data);
let tensor_out = tensor.add_scalar(5.0);
let grads = tensor_out.backward();
let grad = tensor.grad(&grads).unwrap();
assert_eq!(grad.to_data(), Data::from([1.0, 1.0]));
assert_eq!(tensor_out.into_data(), Data::from([7.0, 15.0]));
}
#[test]
fn test_add_complex_1() {
let data_1: Data<f32, 2> = Data::from([[1.0, 7.0], [13.0, -3.0]]);
let data_2: Data<f32, 2> = Data::from([[4.0, 7.0], [2.0, 3.0]]);
let data_3: Data<f32, 2> = Data::from([[2.0, 2.0], [2.0, 2.0]]);
let tensor_1 = Tensor::<TestADBackend, 2>::from_data(data_1);
let tensor_2 = Tensor::<TestADBackend, 2>::from_data(data_2);
let tensor_3 = Tensor::<TestADBackend, 2>::from_data(data_3);
let tensor_4 = tensor_1.add(&tensor_2);
let tensor_5 = tensor_4
.add(&tensor_3)
.add_scalar(5.0)
.add(&tensor_1)
.add(&tensor_2);
let tensor_6 = tensor_1.add(&tensor_5);
let grads = tensor_6.backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
assert_eq!(grad_1.to_data(), Data::from([[3.0, 3.0], [3.0, 3.0]]));
assert_eq!(grad_2.to_data(), Data::from([[2.0, 2.0], [2.0, 2.0]]));
}
};
}

View File

@ -0,0 +1,120 @@
#[macro_export]
macro_rules! test_ad_aggregation {
() => {
#[test]
fn should_diff_mean() {
let data_1 = Data::<f32, 2>::from([[1.0, 7.0], [-2.0, -3.0]]);
let data_2 = Data::<f32, 2>::from([[4.0, -7.0], [2.0, 3.0]]);
let tensor_1 = TestADTensor::from_data(data_1);
let tensor_2 = TestADTensor::from_data(data_2);
let tensor_3 = tensor_1.matmul(&tensor_2);
let tensor_4 = tensor_1.mul(&tensor_3.mean().unsqueeze());
let grads = tensor_4.backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
grad_1
.to_data()
.assert_approx_eq(&Data::from([[3.5, 9.5], [3.5, 9.5]]), 5);
grad_2
.to_data()
.assert_approx_eq(&Data::from([[-0.75, -0.75], [3.0, 3.0]]), 5);
}
#[test]
fn should_diff_sum_1() {
let data_1 = Data::<f32, 2>::from([[1.0, 7.0], [-2.0, -3.0]]);
let data_2 = Data::<f32, 2>::from([[4.0, -7.0], [2.0, 3.0]]);
let tensor_1 = TestADTensor::from_data(data_1);
let tensor_2 = TestADTensor::from_data(data_2);
let tensor_3 = tensor_1.matmul(&tensor_2);
let tensor_4 = tensor_1.mul(&tensor_3.sum().unsqueeze());
let grads = tensor_4.backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
grad_1
.to_data()
.assert_approx_eq(&Data::from([[14.0, 38.0], [14.0, 38.0]]), 5);
grad_2
.to_data()
.assert_approx_eq(&Data::from([[-3.0, -3.0], [12.0, 12.0]]), 5);
}
#[test]
fn should_diff_sum_2() {
let data_1 = Data::from([[0.0, 1.0], [3.0, 4.0]]);
let data_2 = Data::from([[6.0, 7.0], [9.0, 10.0]]);
let tensor_1 = TestADTensor::from_data(data_1);
let tensor_2 = TestADTensor::from_data(data_2);
let tensor_3 = tensor_1.matmul(&tensor_2);
let tensor_4 = tensor_3.sum_dim(1);
let tensor_5 = tensor_4.mul(&tensor_3);
let grads = tensor_5.sum().backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
grad_1
.to_data()
.assert_approx_eq(&Data::from([[494.0, 722.0], [2990.0, 4370.0]]), 3);
grad_2
.to_data()
.assert_approx_eq(&Data::from([[690.0, 690.0], [958.0, 958.0]]), 3);
}
#[test]
fn should_diff_mean_dim() {
let data_1 = Data::<f32, 2>::from([[1.0, 7.0], [-2.0, -3.0]]);
let data_2 = Data::<f32, 2>::from([[4.0, -7.0], [2.0, 3.0]]);
let tensor_1 = TestADTensor::from_data(data_1);
let tensor_2 = TestADTensor::from_data(data_2);
let tensor_3 = tensor_1.matmul(&tensor_2);
let tensor_4 = tensor_1.mul(&tensor_3.mean_dim(1).unsqueeze());
let grads = tensor_4.backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
grad_1
.to_data()
.assert_approx_eq(&Data::from([[4.0, 36.0], [3.0, -17.0]]), 5);
grad_2
.to_data()
.assert_approx_eq(&Data::from([[9.0, 9.0], [35.5, 35.5]]), 5);
}
#[test]
fn should_diff_sum_dim() {
let data_1 = Data::<f32, 2>::from([[1.0, 7.0], [-2.0, -3.0]]);
let data_2 = Data::<f32, 2>::from([[4.0, -7.0], [2.0, 3.0]]);
let tensor_1 = TestADTensor::from_data(data_1);
let tensor_2 = TestADTensor::from_data(data_2);
let tensor_3 = tensor_1.matmul(&tensor_2);
let tensor_4 = tensor_1.mul(&tensor_3.sum_dim(1).unsqueeze());
let grads = tensor_4.backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
grad_1
.to_data()
.assert_approx_eq(&Data::from([[8.0, 72.0], [6.0, -34.0]]), 5);
grad_2
.to_data()
.assert_approx_eq(&Data::from([[18.0, 18.0], [71.0, 71.0]]), 5);
}
};
}

View File

@ -0,0 +1,68 @@
#[macro_export]
macro_rules! test_ad_cat {
() => {
#[test]
fn should_diff_cat() {
let data_1 = Data::<_, 2>::from([[2.0, -1.0], [5.0, 2.0]]);
let data_2 = Data::<_, 2>::from([[5.0, 4.0], [-1.0, 4.0]]);
let tensor_1 = TestADTensor::from_data(data_1);
let tensor_2 = TestADTensor::from_data(data_2);
let tensor_3 = tensor_1.matmul(&tensor_2);
let grads = tensor_3.backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
let mut tensor_1_list = Vec::new();
let mut tensor_2_list = Vec::new();
for i in 0..2 {
tensor_1_list.push(TestADTensor::from_data(
tensor_1.index([i..i + 1]).to_data(),
));
tensor_2_list.push(TestADTensor::from_data(
tensor_2.index([i..i + 1]).to_data(),
));
}
let tensor_1_cat = TestADTensor::cat(tensor_1_list.clone(), 0);
let tensor_2_cat = TestADTensor::cat(tensor_2_list.clone(), 0);
let tensor_3_cat = tensor_1_cat.matmul(&tensor_2_cat);
let grads_cat = tensor_3_cat.backward();
let grad_1_cat = tensor_1_cat.grad(&grads_cat).unwrap();
let grad_2_cat = tensor_2_cat.grad(&grads_cat).unwrap();
let grad_1_list_1 = tensor_1_list.get(0).unwrap().grad(&grads_cat).unwrap();
let grad_1_list_2 = tensor_1_list.get(1).unwrap().grad(&grads_cat).unwrap();
let grad_2_list_1 = tensor_2_list.get(0).unwrap().grad(&grads_cat).unwrap();
let grad_2_list_2 = tensor_2_list.get(1).unwrap().grad(&grads_cat).unwrap();
grad_1.to_data().assert_approx_eq(&grad_1_cat.to_data(), 3);
grad_2.to_data().assert_approx_eq(&grad_2_cat.to_data(), 3);
grad_1
.index([0..1])
.to_data()
.assert_approx_eq(&grad_1_list_1.to_data(), 3);
grad_1
.index([1..2])
.to_data()
.assert_approx_eq(&grad_1_list_2.to_data(), 3);
grad_2
.index([0..1])
.to_data()
.assert_approx_eq(&grad_2_list_1.to_data(), 3);
grad_2
.index([1..2])
.to_data()
.assert_approx_eq(&grad_2_list_2.to_data(), 3);
}
};
}

View File

@ -0,0 +1,29 @@
#[macro_export]
macro_rules! test_ad_cross_entropy_loss {
() => {
#[test]
fn test_cross_entropy_loss_grad() {
let data_1 = Data::from([[0.0, 1.0], [3.0, 4.0]]);
let data_2 = Data::from([[6.0, 7.0], [9.0, 10.0]]);
let data_targets = Data::from([[0.8, 0.2], [0.9, 0.1]]);
let tensor_1 = Tensor::<TestADBackend, 2>::from_data(data_1);
let tensor_2 = Tensor::<TestADBackend, 2>::from_data(data_2);
let tensor_targets = Tensor::<TestADBackend, 2>::from_data(data_targets);
let tensor_3 = tensor_1.matmul(&tensor_2);
let tensor_4 = loss::cross_entropy_with_logits(&tensor_3, &tensor_targets);
let grads = tensor_4.backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
grad_1
.to_data()
.assert_approx_eq(&Data::from([[0.2655, 0.2655], [0.4496, 0.4496]]), 3);
grad_2
.to_data()
.assert_approx_eq(&Data::from([[-1.3486, 1.3486], [-2.0637, 2.0637]]), 3);
}
};
}

View File

@ -0,0 +1,88 @@
#[macro_export]
macro_rules! test_ad_div {
() => {
#[test]
fn should_diff_div() {
let data_1 = Data::from([1.0, 7.0]);
let data_2 = Data::from([4.0, 7.0]);
let tensor_1 = TestADTensor::from_data(data_1);
let tensor_2 = TestADTensor::from_data(data_2);
let tensor_3 = tensor_1.div(&tensor_2);
let grads = tensor_3.backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
grad_1
.to_data()
.assert_approx_eq(&Data::from([0.25, 0.1429]), 3);
grad_2
.to_data()
.assert_approx_eq(&Data::from([-0.0625, -0.1429]), 3);
}
#[test]
fn should_diff_div_scalar() {
let data = Data::from([1.0, 7.0]);
let tensor = TestADTensor::from_data(data);
let tensor_out = tensor.div_scalar(4.0);
let grads = tensor_out.backward();
let grad = tensor.grad(&grads).unwrap();
assert_eq!(grad.to_data(), Data::from([0.25, 0.25]));
}
#[test]
fn test_div_complex_1() {
let data_1: Data<f32, 2> = Data::from([[1.0, 7.0], [13.0, -3.0]]);
let data_2: Data<f32, 2> = Data::from([[4.0, 7.0], [2.0, 3.0]]);
let data_3: Data<f32, 2> = Data::from([[2.0, 2.0], [2.0, 2.0]]);
let tensor_1 = TestADTensor::from_data(data_1);
let tensor_2 = TestADTensor::from_data(data_2);
let tensor_3 = TestADTensor::from_data(data_3);
let tensor_4 = tensor_1.div(&tensor_2);
let tensor_5 = tensor_4.div(&tensor_3);
let grads = tensor_5.backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
grad_1
.to_data()
.assert_approx_eq(&Data::from([[0.1250, 0.0714], [0.25, 0.1667]]), 3);
grad_2
.to_data()
.assert_approx_eq(&Data::from([[-0.0312, -0.0714], [-1.6250, 0.1667]]), 3);
}
#[test]
fn test_div_complex_2() {
let data_1 = Data::from([[0.0, 1.0], [3.0, 4.0]]);
let data_2 = Data::from([[6.0, 7.0], [9.0, 10.0]]);
let tensor_1 = TestADTensor::from_data(data_1);
let tensor_2 = TestADTensor::from_data(data_2);
let tensor_3 = tensor_1.matmul(&tensor_2);
let tensor_4 = tensor_3.div(&tensor_2);
let grads = tensor_4.sum().backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
grad_1
.to_data()
.assert_approx_eq(&Data::from([[2.00, 2.9286], [1.3667, 2.0]]), 3);
grad_2
.to_data()
.assert_approx_eq(&Data::from([[0.0833, 0.0959], [-0.0556, -0.0671]]), 3);
}
};
}

View File

@ -0,0 +1,27 @@
#[macro_export]
macro_rules! test_ad_erf {
() => {
#[test]
fn should_diff_erf() {
let data_1 = Data::<f32, 2>::from([[0.0, 1.0], [3.0, 4.0]]);
let data_2 = Data::<f32, 2>::from([[6.0, 7.0], [9.0, 10.0]]);
let tensor_1 = TestADTensor::from_data(data_1);
let tensor_2 = TestADTensor::from_data(data_2);
let tensor_3 = tensor_1.matmul(&tensor_2.erf());
let tensor_4 = tensor_3.matmul(&tensor_2);
let grads = tensor_4.backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
grad_1
.to_data()
.assert_approx_eq(&Data::from([[32.0, 32.0], [32.0, 32.0]]), 3);
grad_2
.to_data()
.assert_approx_eq(&Data::from([[8.0, 8.0], [8.0, 8.0]]), 3);
}
};
}

View File

@ -0,0 +1,27 @@
#[macro_export]
macro_rules! test_ad_exp {
() => {
#[test]
fn should_diff_exp() {
let data_1 = Data::<f32, 2>::from([[1.0, 7.0], [-2.0, -3.0]]);
let data_2 = Data::<f32, 2>::from([[4.0, -7.0], [2.0, 3.0]]);
let tensor_1 = TestADTensor::from_data(data_1);
let tensor_2 = TestADTensor::from_data(data_2);
let tensor_3 = tensor_1.matmul(&tensor_2.exp());
let grads = tensor_3.backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
grad_1
.to_data()
.assert_approx_eq(&Data::from([[54.5991, 27.4746], [54.5991, 27.4746]]), 3);
grad_2.to_data().assert_approx_eq(
&Data::from([[-5.4598e+01, -9.1188e-04], [2.9556e+01, 8.0342e+01]]),
3,
);
}
};
}

View File

@ -0,0 +1,76 @@
#[macro_export]
macro_rules! test_ad_index {
() => {
#[test]
fn should_diff_matmul_with_index() {
let data_1: Data<f32, 2> = Data::from([[1.0, 7.0], [2.0, 3.0]]);
let data_2: Data<f32, 2> = Data::from([[4.0, 7.0, 100.0], [2.0, 3.0, 15.0]]);
let tensor_1 = TestADTensor::from_data(data_1);
let tensor_2 = TestADTensor::from_data(data_2);
let tensor_3 = tensor_2.index([0..2, 0..2]);
let tensor_4 = &tensor_1.matmul(&tensor_3);
let grads = tensor_4.backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
assert_eq!(grad_1.to_data(), Data::from([[11.0, 5.0], [11.0, 5.0]]));
assert_eq!(
grad_2.to_data(),
Data::from([[3.0, 3.0, 0.0], [10.0, 10.0, 0.0]])
);
}
#[test]
fn should_diff_matmul_with_index_assign() {
let data_1: Data<f32, 2> = Data::from([[1.0, 7.0], [2.0, 3.0]]);
let data_2: Data<f32, 2> = Data::from([[4.0, 7.0], [2.0, 3.0]]);
let data_assigned: Data<f32, 2> = Data::from([[9.0]]);
let tensor_1 = TestADTensor::from_data(data_1);
let tensor_2 = TestADTensor::from_data(data_2);
let tensor_assigned = TestADTensor::from_data(data_assigned);
let tensor_3 = tensor_1.matmul(&tensor_2);
let tensor_4 = tensor_3.index_assign([0..1, 0..1], &tensor_assigned);
let tensor_5 = &tensor_4.matmul(&tensor_1);
let grads = tensor_5.backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
assert_eq!(grad_1.to_data(), Data::from([[58.0, 38.0], [118.0, 82.0]]));
assert_eq!(grad_2.to_data(), Data::from([[16.0, 15.0], [24.0, 50.0]]));
}
#[test]
fn should_diff_matmul_with_index_assign_complex() {
let data_1: Data<f32, 2> = Data::from([[1.0, 7.0], [2.0, 3.0]]);
let data_2: Data<f32, 2> = Data::from([[4.0, 7.0], [2.0, 3.0]]);
let data_3: Data<f32, 2> = Data::from([[9.0]]);
let tensor_1 = TestADTensor::from_data(data_1);
let tensor_2 = TestADTensor::from_data(data_2);
let tensor_3 = TestADTensor::from_data(data_3);
let tensor_4 = tensor_1.matmul(&tensor_2);
let tensor_5 = tensor_2.index([0..1, 0..1]);
let tensor_6 = tensor_5.mul(&tensor_3);
let tensor_7 = tensor_4.index_assign([0..1, 0..1], &tensor_6);
let tensor_8 = &tensor_7.matmul(&tensor_1);
let grads = tensor_8.backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
let grad_3 = tensor_3.grad(&grads).unwrap();
assert_eq!(grad_3.to_data(), Data::from([[32.0]]));
assert_eq!(grad_1.to_data(), Data::from([[85.0, 65.0], [118.0, 82.0]]));
assert_eq!(grad_2.to_data(), Data::from([[88.0, 15.0], [24.0, 50.0]]));
}
};
}

View File

@ -0,0 +1,27 @@
#[macro_export]
macro_rules! test_ad_log {
() => {
#[test]
fn should_diff_log() {
let data_1 = Data::<f32, 2>::from([[0.0, 1.0], [3.0, 4.0]]);
let data_2 = Data::<f32, 2>::from([[6.0, 7.0], [9.0, 10.0]]);
let tensor_1 = TestADTensor::from_data(data_1);
let tensor_2 = TestADTensor::from_data(data_2);
let tensor_3 = tensor_1.matmul(&tensor_2.log());
let tensor_4 = tensor_3.matmul(&tensor_2);
let grads = tensor_4.backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
grad_1
.to_data()
.assert_approx_eq(&Data::from([[60.2652, 72.3130], [60.2652, 72.3130]]), 3);
grad_2
.to_data()
.assert_approx_eq(&Data::from([[22.8614, 24.5043], [24.5729, 26.8507]]), 3);
}
};
}

View File

@ -0,0 +1,25 @@
#[macro_export]
macro_rules! test_ad_mask {
() => {
#[test]
fn should_diff_mask() {
let data_1 = Data::<f32, 2>::from([[1.0, 7.0], [2.0, 3.0]]);
let data_2 = Data::<f32, 2>::from([[4.0, 7.0], [2.0, 3.0]]);
let mask = Data::<bool, 2>::from([[true, false], [false, true]]);
let tensor_1 = TestADTensor::from_data(data_1);
let tensor_2 = TestADTensor::from_data(data_2);
let mask = BoolTensor::from_data(mask);
let tensor_3 = tensor_1.matmul(&tensor_2);
let tensor_4 = tensor_3.mask_fill(&mask, 2.0);
let grads = tensor_4.backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
assert_eq!(grad_1.to_data(), Data::from([[7.0, 3.0], [4.0, 2.0]]));
assert_eq!(grad_2.to_data(), Data::from([[2.0, 1.0], [3.0, 7.0]]));
}
};
}

View File

@ -0,0 +1,77 @@
#[macro_export]
macro_rules! test_ad_matmul {
() => {
#[test]
fn should_diff_matmul() {
let data_1: Data<f32, 2> = Data::from([[1.0, 7.0], [2.0, 3.0]]);
let data_2: Data<f32, 2> = Data::from([[4.0, 7.0], [2.0, 3.0]]);
let tensor_1 = TestADTensor::from_data(data_1);
let tensor_2 = TestADTensor::from_data(data_2);
let tensor_3 = &tensor_1.matmul(&tensor_2);
let grads = tensor_3.backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
assert_eq!(grad_1.to_data(), Data::from([[11.0, 5.0], [11.0, 5.0]]));
assert_eq!(grad_2.to_data(), Data::from([[3.0, 3.0], [10.0, 10.0]]));
assert_eq!(
tensor_3.clone().into_data(),
Data::from([[18.0, 28.0], [14.0, 23.0]])
);
}
#[test]
fn test_matmul_complex_1() {
let data_1: Data<f32, 2> = Data::from([[1.0, 7.0], [13.0, -3.0]]);
let data_2: Data<f32, 2> = Data::from([[4.0, 7.0], [2.0, 3.0]]);
let data_3: Data<f32, 2> = Data::from([[2.0, 2.0], [2.0, 2.0]]);
let tensor_1 = TestADTensor::from_data(data_1);
let tensor_2 = TestADTensor::from_data(data_2);
let tensor_3 = TestADTensor::from_data(data_3);
let tensor_4 = tensor_1.matmul(&tensor_2);
let tensor_5 = tensor_4.matmul(&tensor_3);
let grads = tensor_5.backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
assert_eq!(grad_1.to_data(), Data::from([[44.0, 20.0], [44.0, 20.0]]));
assert_eq!(grad_2.to_data(), Data::from([[56.0, 56.0], [16.0, 16.0]]));
}
#[test]
fn test_matmul_complex_2() {
let data_1: Data<f32, 2> = Data::from([[1.0, 7.0], [13.0, -3.0]]);
let data_2: Data<f32, 2> = Data::from([[4.0, 7.0], [2.0, 3.0]]);
let data_3: Data<f32, 2> = Data::from([[2.0, 2.0], [2.0, 2.0]]);
let tensor_1 = TestADTensor::from_data(data_1);
let tensor_2 = TestADTensor::from_data(data_2);
let tensor_3 = TestADTensor::from_data(data_3);
let tensor_4 = tensor_1.matmul(&tensor_2);
let tensor_5 = tensor_4.matmul(&tensor_3);
let tensor_6 = tensor_1.matmul(&tensor_5);
let grads = tensor_6.backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
assert_eq!(
grad_1.to_data(),
Data::from([[800.0, 792.0], [360.0, 592.0]])
);
assert_eq!(
grad_2.to_data(),
Data::from([[264., 264.0], [344.0, 344.0]])
);
}
};
}

View File

@ -0,0 +1,63 @@
#[macro_export]
macro_rules! test_ad_mul {
() => {
#[test]
fn should_diff_mul() {
let data_1 = Data::from([1.0, 7.0]);
let data_2 = Data::from([4.0, 7.0]);
let tensor_1 = TestADTensor::from_data(data_1.clone());
let tensor_2 = TestADTensor::from_data(data_2.clone());
let tensor_3 = tensor_1.mul(&tensor_2);
let grads = tensor_3.backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
assert_eq!(grad_1.to_data(), data_2);
assert_eq!(grad_2.to_data(), data_1);
assert_eq!(tensor_3.into_data(), Data::from([4.0, 49.0]));
}
#[test]
fn should_diff_mul_scalar() {
let data = Data::from([2.0, 5.0]);
let tensor = TestADTensor::from_data(data);
let tensor_out = tensor.mul_scalar(4.0);
let grads = tensor_out.backward();
let grad = tensor.grad(&grads).unwrap();
assert_eq!(tensor_out.into_data(), Data::from([8.0, 20.0]));
assert_eq!(grad.to_data(), Data::from([4.0, 4.0]));
}
#[test]
fn test_mul_complex_1() {
let data_1: Data<f32, 2> = Data::from([[1.0, 7.0], [13.0, -3.0]]);
let data_2: Data<f32, 2> = Data::from([[4.0, 7.0], [2.0, 3.0]]);
let data_3: Data<f32, 2> = Data::from([[2.0, 2.0], [2.0, 2.0]]);
let tensor_1 = TestADTensor::from_data(data_1);
let tensor_2 = TestADTensor::from_data(data_2);
let tensor_3 = TestADTensor::from_data(data_3);
let tensor_4 = tensor_1.mul(&tensor_2);
let tensor_5 = tensor_4.mul(&tensor_3);
let tensor_6 = tensor_1.mul(&tensor_5);
let grads = tensor_6.backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
assert_eq!(
grad_1.to_data(),
Data::from([[16.0, 196.0], [104.0, -36.0]])
);
assert_eq!(grad_2.to_data(), Data::from([[2.0, 98.0], [338.0, 18.0]]));
}
};
}

View File

@ -0,0 +1,23 @@
#[macro_export]
macro_rules! test_ad_neg {
() => {
#[test]
fn should_diff_neg() {
let data_1 = Data::<f32, 2>::from([[1.0, 7.0], [2.0, 3.0]]);
let data_2 = Data::<f32, 2>::from([[4.0, 7.0], [2.0, 3.0]]);
let tensor_1 = TestADTensor::from_data(data_1);
let tensor_2 = TestADTensor::from_data(data_2);
let tensor_3 = tensor_1.matmul(&tensor_2.neg());
let tensor_4 = tensor_3.neg();
let grads = tensor_4.backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
assert_eq!(grad_1.to_data(), Data::from([[11.0, 5.0], [11.0, 5.0]]));
assert_eq!(grad_2.to_data(), Data::from([[3.0, 3.0], [10.0, 10.0]]));
}
};
}

View File

@ -0,0 +1,27 @@
#[macro_export]
macro_rules! test_ad_powf {
() => {
#[test]
fn should_diff_powf() {
let data_1 = Data::<f32, 2>::from([[0.0, 1.0], [3.0, 4.0]]);
let data_2 = Data::<f32, 2>::from([[6.0, 7.0], [9.0, 10.0]]);
let tensor_1 = TestADTensor::from_data(data_1);
let tensor_2 = TestADTensor::from_data(data_2);
let tensor_3 = tensor_1.matmul(&tensor_2.powf(0.4));
let tensor_4 = tensor_3.matmul(&tensor_2);
let grads = tensor_4.backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
grad_1
.to_data()
.assert_approx_eq(&Data::from([[68.0, 79.0328], [68.0, 79.0328]]), 3);
grad_2
.to_data()
.assert_approx_eq(&Data::from([[23.5081, 25.2779], [26.0502, 28.6383]]), 3);
}
};
}

View File

@ -0,0 +1,24 @@
#[macro_export]
macro_rules! test_ad_relu {
() => {
#[test]
fn should_diff_relu() {
let data_1 = Data::<f32, 2>::from([[1.0, 7.0], [-2.0, -3.0]]);
let data_2 = Data::<f32, 2>::from([[4.0, -7.0], [2.0, 3.0]]);
let tensor_1 = TestADTensor::from_data(data_1);
let tensor_2 = TestADTensor::from_data(data_2);
let tensor_3 = tensor_1.matmul(&tensor_2);
let tensor_4 = activation::relu(&tensor_3);
let tensor_5 = tensor_4.matmul(&tensor_2);
let grads = tensor_5.backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
assert_eq!(grad_1.to_data(), Data::from([[-47.0, 9.0], [-35.0, 15.0]]));
assert_eq!(grad_2.to_data(), Data::from([[15.0, 13.0], [-2.0, 39.0]]));
}
};
}

View File

@ -0,0 +1,23 @@
#[macro_export]
macro_rules! test_ad_reshape {
() => {
#[test]
fn should_diff_reshape() {
let data_1: Data<f32, 2> = Data::from([[1.0, 7.0], [2.0, 3.0]]);
let data_2: Data<f32, 1> = Data::from([4.0, 7.0, 2.0, 3.0]);
let tensor_1 = TestADTensor::from_data(data_1);
let tensor_2 = TestADTensor::from_data(data_2);
let tensor_3 = tensor_2.reshape([2, 2]);
let tensor_4 = &tensor_1.matmul(&tensor_3);
let grads = tensor_4.backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
assert_eq!(grad_1.to_data(), Data::from([[11.0, 5.0], [11.0, 5.0]]));
assert_eq!(grad_2.to_data(), Data::from([3.0, 3.0, 10.0, 10.0]));
}
};
}

View File

@ -0,0 +1,48 @@
#[macro_export]
macro_rules! test_ad_softmax {
() => {
#[test]
fn test_softmax_grad() {
let data_1 = Data::from([[0.0, 1.0], [3.0, 4.0]]);
let data_2 = Data::from([[6.0, 7.0], [9.0, 10.0]]);
let tensor_1 = Tensor::<TestADBackend, 2>::from_data(data_1);
let tensor_2 = Tensor::<TestADBackend, 2>::from_data(data_2);
let tensor_3 = tensor_1.matmul(&tensor_2);
let tensor_4 = activation::softmax(&tensor_3, 1).matmul(&tensor_2);
let grads = tensor_4.backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
grad_1
.to_data()
.assert_approx_eq(&Data::from([[1.1797, 1.1797], [0.0055, 0.0055]]), 3);
grad_2
.to_data()
.assert_approx_eq(&Data::from([[0.2534, 0.2862], [0.5286, 2.9317]]), 3);
}
#[test]
fn test_log_softmax_grad() {
let data_1 = Data::from([[0.0, 1.0], [3.0, 4.0]]);
let data_2 = Data::from([[6.0, 7.0], [9.0, 10.0]]);
let tensor_1 = Tensor::<TestADBackend, 2>::from_data(data_1);
let tensor_2 = Tensor::<TestADBackend, 2>::from_data(data_2);
let tensor_3 = tensor_1.matmul(&tensor_2);
let tensor_4 = activation::log_softmax(&tensor_3, 1).matmul(&tensor_2);
let grads = tensor_4.backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
grad_1
.to_data()
.assert_approx_eq(&Data::from([[-4.3939, -4.3939], [-12.9709, -12.9709]]), 3);
grad_2
.to_data()
.assert_approx_eq(&Data::from([[30.5984, -47.2267], [55.9631, -56.5914]]), 3);
}
};
}

View File

@ -0,0 +1,59 @@
#[macro_export]
macro_rules! test_ad_sub {
() => {
#[test]
fn should_diff_sub() {
let data_1 = Data::from([2.0, 5.0]);
let data_2 = Data::from([4.0, 1.0]);
let tensor_1 = TestADTensor::from_data(data_1);
let tensor_2 = TestADTensor::from_data(data_2);
let tensor_3 = tensor_1.sub(&tensor_2);
let grads = tensor_3.backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
assert_eq!(grad_1.to_data(), Data::from([1.0, 1.0]));
assert_eq!(grad_2.to_data(), Data::from([-1.0, -1.0]));
assert_eq!(tensor_3.into_data(), Data::from([-2.0, 4.0]));
}
#[test]
fn should_diff_sub_scalar() {
let data = Data::from([2.0, 10.0]);
let tensor = TestADTensor::from_data(data);
let tensor_out = tensor.sub_scalar(5.0);
let grads = tensor_out.backward();
let grad = tensor.grad(&grads).unwrap();
assert_eq!(grad.to_data(), Data::from([1.0, 1.0]));
assert_eq!(tensor_out.into_data(), Data::from([-3.0, 5.0]));
}
#[test]
fn test_sub_complex_1() {
let data_1: Data<f32, 2> = Data::from([[1.0, 7.0], [13.0, -3.0]]);
let data_2: Data<f32, 2> = Data::from([[4.0, 7.0], [2.0, 3.0]]);
let data_3: Data<f32, 2> = Data::from([[2.0, 2.0], [2.0, 2.0]]);
let tensor_1 = TestADTensor::from_data(data_1);
let tensor_2 = TestADTensor::from_data(data_2);
let tensor_3 = TestADTensor::from_data(data_3);
let tensor_4 = tensor_1.sub(&tensor_2);
let tensor_5 = tensor_4.sub(&tensor_3).sub_scalar(5.0);
let tensor_6 = tensor_1.sub(&tensor_5);
let grads = tensor_6.backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
assert_eq!(grad_1.to_data(), Data::from([[0.0, 0.0], [0.0, 0.0]]));
assert_eq!(grad_2.to_data(), Data::from([[1.0, 1.0], [1.0, 1.0]]));
}
};
}

View File

@ -0,0 +1,50 @@
#[macro_export]
macro_rules! test_ad_transpose {
() => {
#[test]
fn should_diff_transpose() {
let data_1 = Data::<f32, 2>::from([[1.0, 7.0], [2.0, 3.0]]);
let data_2 = Data::<f32, 2>::from([[4.0, 7.0], [2.0, 3.0]]);
let tensor_1 = TestADTensor::from_data(data_1);
let tensor_2 = TestADTensor::from_data(data_2);
let tensor_3 = tensor_1.matmul(&tensor_2.transpose());
let tensor_4 = tensor_3.transpose();
let grads = tensor_4.backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
assert_eq!(grad_1.to_data(), Data::from([[6.0, 10.0], [6.0, 10.0]]));
assert_eq!(grad_2.to_data(), Data::from([[3.0, 10.0], [3.0, 10.0]]));
}
#[test]
fn should_diff_swap_dims() {
let data_1 =
Data::<f32, 3>::from([[[0.0, 1.0], [3.0, 4.0]], [[6.0, 7.0], [9.0, 10.0]]]);
let data_2 =
Data::<f32, 3>::from([[[1.0, 4.0], [2.0, 5.0]], [[7.0, 10.0], [8.0, 11.0]]]);
let tensor_1 = TestADTensor::from_data(data_1);
let tensor_2 = TestADTensor::from_data(data_2);
let tensor_3 = tensor_1.matmul(&tensor_2.swap_dims(0, 2));
let tensor_4 = tensor_3.matmul(&tensor_2.swap_dims(1, 2));
let grads = tensor_4.backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
assert_eq!(
grad_1.to_data(),
Data::from([[[66., 78.], [66., 78.]], [[270., 306.], [270., 306.]]])
);
assert_eq!(
grad_2.to_data(),
Data::from([[[22., 286.], [28., 316.]], [[172., 652.], [190., 694.]]])
);
}
};
}

View File

@ -0,0 +1,70 @@
mod activation;
mod grad;
mod module;
mod ops;
mod stats;
#[macro_export]
macro_rules! test_all {
() => {
use burn_tensor::activation::*;
use burn_tensor::backend::Backend;
use burn_tensor::module::*;
use burn_tensor::*;
type TestADTensor<const D: usize> = burn_tensor::Tensor<TestADBackend, D>;
type TestADBackend = burn_tensor::backend::ADBackendDecorator<TestBackend>;
// test activation
burn_tensor::test_gelu!();
burn_tensor::test_relu!();
burn_tensor::test_softmax!();
// test ad
burn_tensor::test_ad_add!();
burn_tensor::test_ad_aggregation!();
burn_tensor::test_ad_cat!();
burn_tensor::test_ad_cross_entropy_loss!();
burn_tensor::test_ad_div!();
burn_tensor::test_ad_erf!();
burn_tensor::test_ad_exp!();
burn_tensor::test_ad_index!();
burn_tensor::test_ad_log!();
burn_tensor::test_ad_mask!();
burn_tensor::test_ad_matmul!();
burn_tensor::test_ad_mul!();
burn_tensor::test_ad_neg!();
burn_tensor::test_ad_powf!();
burn_tensor::test_ad_relu!();
burn_tensor::test_ad_reshape!();
burn_tensor::test_ad_softmax!();
burn_tensor::test_ad_sub!();
burn_tensor::test_ad_transpose!();
// test module
burn_tensor::test_module_backward!();
burn_tensor::test_module_forward!();
// test ops
burn_tensor::test_add!();
burn_tensor::test_aggregation!();
burn_tensor::test_arg!();
burn_tensor::test_div!();
burn_tensor::test_erf!();
burn_tensor::test_exp!();
burn_tensor::test_index!();
burn_tensor::test_map_comparison!();
burn_tensor::test_mask!();
burn_tensor::test_matmul!();
burn_tensor::test_mul!();
burn_tensor::test_neg!();
burn_tensor::test_powf!();
burn_tensor::test_repeat!();
burn_tensor::test_reshape!();
burn_tensor::test_sub!();
burn_tensor::test_transpose!();
// test stats
burn_tensor::test_stats!();
};
}

View File

@ -0,0 +1,27 @@
#[macro_export]
macro_rules! test_module_backward {
() => {
#[test]
fn test_embedding_backward() {
let weights = Data::from([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]);
let indexes = Data::from([[0, 1], [1, 1]]);
let x = Data::from([
[[1.0, 2.0], [4.0, 5.0], [3.0, 4.0]],
[[4.0, 5.0], [8.0, 5.0], [1.0, 9.0]],
]);
let weights = Tensor::<TestADBackend, 2>::from_data(weights);
let indexes =
Tensor::<<TestADBackend as Backend>::IntegerBackend, 2>::from_data(indexes);
let x = Tensor::<TestADBackend, 3>::from_data(x);
let output = embedding(&weights, &indexes);
let output = output.matmul(&x);
let grads = output.backward();
let grad = weights.grad(&grads).unwrap();
let expected =
Data::<<TestADBackend as Backend>::Elem, 2>::from([[3., 9., 7.], [21., 35., 27.]]);
assert_eq!(grad.to_data(), expected);
}
};
}

View File

@ -0,0 +1,19 @@
#[macro_export]
macro_rules! test_module_forward {
() => {
#[test]
fn test_embedding_forward() {
let weights = Data::from([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]);
let indexes = Data::from([[0, 1], [1, 1]]);
let weights = Tensor::<TestBackend, 2>::from_data(weights);
let indexes = Tensor::<<TestBackend as Backend>::IntegerBackend, 2>::from_data(indexes);
let output = embedding(&weights, &indexes);
let expected = Data::from([
[[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]],
[[3.0, 4.0, 5.0], [3.0, 4.0, 5.0]],
]);
assert_eq!(output.to_data(), expected);
}
};
}

View File

@ -0,0 +1,17 @@
#[macro_export]
macro_rules! test_add {
() => {
#[test]
fn test_add_d2() {
let data_1 = Data::from([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]);
let data_2 = Data::from([[6.0, 7.0, 8.0], [9.0, 10.0, 11.0]]);
let tensor_1 = Tensor::<TestBackend, 2>::from_data(data_1);
let tensor_2 = Tensor::<TestBackend, 2>::from_data(data_2);
let data_actual = (tensor_1 + tensor_2).into_data();
let data_expected = Data::from([[6.0, 8.0, 10.0], [12.0, 14.0, 16.0]]);
assert_eq!(data_expected, data_actual);
}
};
}

View File

@ -0,0 +1,44 @@
#[macro_export]
macro_rules! test_aggregation {
() => {
#[test]
fn test_should_mean() {
let data = Data::from([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]);
let tensor = Tensor::<TestBackend, 2>::from_data(data);
let data_actual = tensor.mean().to_data();
assert_eq!(data_actual, Data::from([15.0 / 6.0]));
}
#[test]
fn test_should_sum() {
let data = Data::from([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]);
let tensor = Tensor::<TestBackend, 2>::from_data(data);
let data_actual = tensor.sum().to_data();
assert_eq!(data_actual, Data::from([15.0]));
}
#[test]
fn test_should_mean_dim() {
let data = Data::from([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]);
let tensor = Tensor::<TestBackend, 2>::from_data(data);
let data_actual = tensor.mean_dim(1).to_data();
assert_eq!(data_actual, Data::from([[3.0 / 3.0], [12.0 / 3.0]]));
}
#[test]
fn test_should_sum_dim() {
let data = Data::from([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]);
let tensor = Tensor::<TestBackend, 2>::from_data(data);
let data_actual = tensor.sum_dim(1).to_data();
assert_eq!(data_actual, Data::from([[3.0], [12.0]]));
}
};
}

View File

@ -0,0 +1,15 @@
#[macro_export]
macro_rules! test_arg {
() => {
#[test]
fn test_argmax_2d() {
let data = Data::from([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]);
let tensor = Tensor::<TestBackend, 2>::from_data(data);
let data_actual = tensor.argmax(1);
let data_expected = Data::from([[2], [2]]);
assert_eq!(data_expected, data_actual.to_data());
}
};
}

View File

@ -0,0 +1,31 @@
#[macro_export]
macro_rules! test_div {
() => {
#[test]
fn should_support_div_ops() {
let data_1 = Data::from([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]);
let data_2 = Data::from([[1.0, 1.0, 2.0], [3.0, 4.0, 5.0]]);
let tensor_1 = Tensor::<TestBackend, 2>::from_data(data_1);
let tensor_2 = Tensor::<TestBackend, 2>::from_data(data_2);
let output = tensor_1 / tensor_2;
let data_actual = output.into_data();
let data_expected = Data::from([[0.0, 1.0, 1.0], [1.0, 1.0, 1.0]]);
assert_eq!(data_expected, data_actual);
}
#[test]
fn should_support_div_scalar_ops() {
let data = Data::from([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]);
let scalar = 2.0;
let tensor = Tensor::<TestBackend, 2>::from_data(data);
let output = tensor / scalar;
let data_actual = output.into_data();
let data_expected = Data::from([[0.0, 0.5, 1.0], [1.5, 2.0, 2.5]]);
assert_eq!(data_expected, data_actual);
}
};
}

View File

@ -0,0 +1,15 @@
#[macro_export]
macro_rules! test_erf {
() => {
#[test]
fn should_support_erf_ops() {
let data = Data::from([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]);
let tensor = Tensor::<TestBackend, 2>::from_data(data);
let data_actual = tensor.erf().into_data();
let data_expected = Data::from([[0.0000, 0.8427, 0.9953], [1.0000, 1.0000, 1.0000]]);
data_expected.assert_approx_eq(&data_actual, 3);
}
};
}

View File

@ -0,0 +1,15 @@
#[macro_export]
macro_rules! test_exp {
() => {
#[test]
fn should_support_exp_ops() {
let data = Data::from([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]);
let tensor = Tensor::<TestBackend, 2>::from_data(data);
let data_actual = tensor.exp().into_data();
let data_expected = Data::from([[1.0, 2.71830, 7.3891], [20.0855, 54.5981, 148.4132]]);
data_expected.assert_approx_eq(&data_actual, 3);
}
};
}

View File

@ -0,0 +1,78 @@
#[macro_export]
macro_rules! test_index {
() => {
#[test]
fn should_support_full_indexing_1d() {
let data = Data::from([0.0, 1.0, 2.0]);
let tensor = Tensor::<TestBackend, 1>::from_data(data.clone());
let data_actual = tensor.index([0..3]).into_data();
assert_eq!(data, data_actual);
}
#[test]
fn should_support_partial_indexing_1d() {
let data = Data::from([0.0, 1.0, 2.0]);
let tensor = Tensor::<TestBackend, 1>::from_data(data);
let data_actual = tensor.index([1..3]).into_data();
let data_expected = Data::from([1.0, 2.0]);
assert_eq!(data_expected, data_actual);
}
#[test]
fn should_support_full_indexing_2d() {
let data = Data::from([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]);
let tensor = Tensor::<TestBackend, 2>::from_data(data.clone());
let data_actual_1 = tensor.index([0..2]).into_data();
let data_actual_2 = tensor.index([0..2, 0..3]).into_data();
assert_eq!(data, data_actual_1);
assert_eq!(data, data_actual_2);
}
#[test]
fn should_support_partial_indexing_2d() {
let data = Data::from([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]);
let tensor = Tensor::<TestBackend, 2>::from_data(data);
let data_actual = tensor.index([0..2, 0..2]).into_data();
let data_expected = Data::from([[0.0, 1.0], [3.0, 4.0]]);
assert_eq!(data_expected, data_actual);
}
#[test]
fn should_support_indexe_assign_1d() {
let data = Data::from([0.0, 1.0, 2.0]);
let data_assigned = Data::from([10.0, 5.0]);
let tensor = Tensor::<TestBackend, 1>::from_data(data);
let tensor_assigned = Tensor::<TestBackend, 1>::from_data(data_assigned);
let data_actual = tensor.index_assign([0..2], &tensor_assigned).into_data();
let data_expected = Data::from([10.0, 5.0, 2.0]);
assert_eq!(data_expected, data_actual);
}
#[test]
fn should_support_indexe_assign_2d() {
let data = Data::from([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]);
let data_assigned = Data::from([[10.0, 5.0]]);
let tensor = Tensor::<TestBackend, 2>::from_data(data);
let tensor_assigned = Tensor::<TestBackend, 2>::from_data(data_assigned);
let data_actual = tensor
.index_assign([1..2, 0..2], &tensor_assigned)
.into_data();
let data_expected = Data::from([[0.0, 1.0, 2.0], [10.0, 5.0, 5.0]]);
assert_eq!(data_expected, data_actual);
}
};
}

View File

@ -0,0 +1,100 @@
#[macro_export]
macro_rules! test_map_comparison {
() => {
#[test]
fn test_greater_scalar() {
let data_1 = Data::from([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]);
let tensor_1 = Tensor::<TestBackend, 2>::from_data(data_1);
let data_actual = tensor_1.greater_scalar(4.0);
let data_expected = Data::from([[false, false, false], [false, false, true]]);
assert_eq!(data_expected, data_actual.to_data());
}
#[test]
fn test_greater_equal_scalar() {
let data_1 = Data::from([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]);
let tensor_1 = Tensor::<TestBackend, 2>::from_data(data_1);
let data_actual = tensor_1.greater_equal_scalar(4.0);
let data_expected = Data::from([[false, false, false], [false, true, true]]);
assert_eq!(data_expected, data_actual.to_data());
}
#[test]
fn test_greater() {
let data_1 = Data::from([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]);
let data_2 = Data::from([[1.0, 1.0, 1.0], [4.0, 3.0, 50.0]]);
let tensor_1 = Tensor::<TestBackend, 2>::from_data(data_1);
let tensor_2 = Tensor::<TestBackend, 2>::from_data(data_2);
let data_actual = tensor_1.greater(&tensor_2);
let data_expected = Data::from([[false, false, true], [false, true, false]]);
assert_eq!(data_expected, data_actual.to_data());
}
#[test]
fn test_greater_equal() {
let data_1 = Data::from([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]);
let data_2 = Data::from([[1.0, 1.0, 1.0], [4.0, 3.0, 50.0]]);
let tensor_1 = Tensor::<TestBackend, 2>::from_data(data_1);
let tensor_2 = Tensor::<TestBackend, 2>::from_data(data_2);
let data_actual = tensor_1.greater_equal(&tensor_2);
let data_expected = Data::from([[false, true, true], [false, true, false]]);
assert_eq!(data_expected, data_actual.to_data());
}
#[test]
fn test_lower_scalar() {
let data_1 = Data::from([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]);
let tensor_1 = Tensor::<TestBackend, 2>::from_data(data_1);
let data_actual = tensor_1.lower_scalar(4.0);
let data_expected = Data::from([[true, true, true], [true, false, false]]);
assert_eq!(data_expected, data_actual.to_data());
}
#[test]
fn test_lower_equal_scalar() {
let data_1 = Data::from([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]);
let tensor_1 = Tensor::<TestBackend, 2>::from_data(data_1);
let data_actual = tensor_1.lower_equal_scalar(4.0);
let data_expected = Data::from([[true, true, true], [true, true, false]]);
assert_eq!(data_expected, data_actual.to_data());
}
#[test]
fn test_lower() {
let data_1 = Data::from([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]);
let data_2 = Data::from([[1.0, 1.0, 1.0], [4.0, 3.0, 50.0]]);
let tensor_1 = Tensor::<TestBackend, 2>::from_data(data_1);
let tensor_2 = Tensor::<TestBackend, 2>::from_data(data_2);
let data_actual = tensor_1.lower(&tensor_2);
let data_expected = Data::from([[true, false, false], [true, false, true]]);
assert_eq!(data_expected, data_actual.to_data());
}
#[test]
fn test_lower_equal() {
let data_1 = Data::from([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]);
let data_2 = Data::from([[1.0, 1.0, 1.0], [4.0, 3.0, 50.0]]);
let tensor_1 = Tensor::<TestBackend, 2>::from_data(data_1);
let tensor_2 = Tensor::<TestBackend, 2>::from_data(data_2);
let data_actual = tensor_1.lower_equal(&tensor_2);
let data_expected = Data::from([[true, true, false], [true, false, true]]);
assert_eq!(data_expected, data_actual.to_data());
}
};
}

View File

@ -0,0 +1,16 @@
#[macro_export]
macro_rules! test_mask {
() => {
#[test]
fn should_support_mask_ops() {
let tensor = Tensor::<TestBackend, 2>::from_data(Data::from([[1.0, 7.0], [2.0, 3.0]]));
let mask =
BoolTensor::<TestBackend, 2>::from_data(Data::from([[true, false], [false, true]]));
let data_actual = tensor.mask_fill(&mask, 2.0).to_data();
let data_expected = Data::from([[2.0, 7.0], [2.0, 2.0]]);
assert_eq!(data_expected, data_actual);
}
};
}

View File

@ -0,0 +1,34 @@
#[macro_export]
macro_rules! test_matmul {
() => {
#[test]
fn test_matmul_d2() {
let data_1 = Data::from([[1.0, 7.0], [2.0, 3.0], [1.0, 5.0]]);
let data_2 = Data::from([[4.0, 7.0, 5.0], [2.0, 3.0, 5.0]]);
let tensor_1 = Tensor::<TestBackend, 2>::from_data(data_1);
let tensor_2 = Tensor::<TestBackend, 2>::from_data(data_2);
let tensor_3 = tensor_1.matmul(&tensor_2);
assert_eq!(
tensor_3.into_data(),
Data::from([[18.0, 28.0, 40.0], [14.0, 23.0, 25.0], [14.0, 22.0, 30.0]])
);
}
#[test]
fn test_matmul_d3() {
let data_1 = Data::from([[[1.0, 7.0], [2.0, 3.0]]]);
let data_2 = Data::from([[[4.0, 7.0], [2.0, 3.0]]]);
let tensor_1 = Tensor::<TestBackend, 3>::from_data(data_1);
let tensor_2 = Tensor::<TestBackend, 3>::from_data(data_2);
let tensor_3 = tensor_1.matmul(&tensor_2);
assert_eq!(
tensor_3.into_data(),
Data::from([[[18.0, 28.0], [14.0, 23.0]]])
);
}
};
}

View File

@ -0,0 +1,31 @@
#[macro_export]
macro_rules! test_mul {
() => {
#[test]
fn should_support_mul_ops() {
let data_1 = Data::from([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]);
let data_2 = Data::from([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]);
let tensor_1 = Tensor::<TestBackend, 2>::from_data(data_1);
let tensor_2 = Tensor::<TestBackend, 2>::from_data(data_2);
let output = tensor_1 * tensor_2;
let data_actual = output.into_data();
let data_expected = Data::from([[0.0, 1.0, 4.0], [9.0, 16.0, 25.0]]);
assert_eq!(data_expected, data_actual);
}
#[test]
fn should_support_mul_scalar_ops() {
let data = Data::from([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]);
let scalar = 2.0;
let tensor = Tensor::<TestBackend, 2>::from_data(data);
let output = tensor * scalar;
let data_actual = output.into_data();
let data_expected = Data::from([[0.0, 2.0, 4.0], [6.0, 8.0, 10.0]]);
assert_eq!(data_expected, data_actual);
}
};
}

View File

@ -0,0 +1,15 @@
#[macro_export]
macro_rules! test_neg {
() => {
#[test]
fn should_support_neg_ops() {
let data = Data::from([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]);
let tensor = Tensor::<TestBackend, 2>::from_data(data);
let data_actual = tensor.neg().into_data();
let data_expected = Data::from([[-0.0, -1.0, -2.0], [-3.0, -4.0, -5.0]]);
assert_eq!(data_expected, data_actual);
}
};
}

View File

@ -0,0 +1,15 @@
#[macro_export]
macro_rules! test_powf {
() => {
#[test]
fn should_support_powf_ops() {
let data = Data::from([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]);
let tensor = Tensor::<TestBackend, 2>::from_data(data);
let data_actual = tensor.powf(0.71).into_data();
let data_expected = Data::from([[0.0, 1.0, 1.6358], [2.182, 2.6759, 3.1352]]);
data_expected.assert_approx_eq(&data_actual, 3);
}
};
}

View File

@ -0,0 +1,20 @@
#[macro_export]
macro_rules! test_repeat {
() => {
#[test]
fn should_support_repeat_ops() {
let data = Data::from([[0.0, 1.0, 2.0]]);
let tensor = Tensor::<TestBackend, 2>::from_data(data);
let data_actual = tensor.repeat(0, 4).into_data();
let data_expected = Data::from([
[0.0, 1.0, 2.0],
[0.0, 1.0, 2.0],
[0.0, 1.0, 2.0],
[0.0, 1.0, 2.0],
]);
assert_eq!(data_expected, data_actual);
}
};
}

View File

@ -0,0 +1,26 @@
#[macro_export]
macro_rules! test_reshape {
() => {
#[test]
fn should_support_reshape_1d() {
let data = Data::from([0.0, 1.0, 2.0]);
let tensor = Tensor::<TestBackend, 1>::from_data(data);
let data_actual = tensor.reshape(Shape::new([1, 3])).into_data();
let data_expected = Data::from([[0.0, 1.0, 2.0]]);
assert_eq!(data_expected, data_actual);
}
#[test]
fn should_support_reshape_2d() {
let data = Data::from([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]);
let tensor = Tensor::<TestBackend, 2>::from_data(data);
let data_actual = tensor.reshape(Shape::new([6])).into_data();
let data_expected = Data::from([0.0, 1.0, 2.0, 3.0, 4.0, 5.0]);
assert_eq!(data_expected, data_actual);
}
};
}

View File

@ -0,0 +1,17 @@
#[macro_export]
macro_rules! test_sub {
() => {
#[test]
fn should_support_sub_ops() {
let data_1 = Data::from([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]);
let data_2 = Data::from([[6.0, 7.0, 8.0], [9.0, 10.0, 11.0]]);
let data_expected = Data::from([[-6.0, -6.0, -6.0], [-6.0, -6.0, -6.0]]);
let tensor_1 = Tensor::<TestBackend, 2>::from_data(data_1);
let tensor_2 = Tensor::<TestBackend, 2>::from_data(data_2);
let data_actual = (tensor_1 - tensor_2).into_data();
assert_eq!(data_expected, data_actual);
}
};
}

View File

@ -0,0 +1,39 @@
#[macro_export]
macro_rules! test_transpose {
() => {
#[test]
fn should_support_transpose_ops() {
let data = Data::from([
[[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]],
[[6.0, 7.0, 8.0], [9.0, 10.0, 11.0]],
]);
let tensor = Tensor::<TestBackend, 3>::from_data(data);
let data_actual = tensor.transpose().into_data();
let data_expected = Data::from([
[[0.0, 3.0], [1.0, 4.0], [2.0, 5.0]],
[[6.0, 9.0], [7.0, 10.0], [8.0, 11.0]],
]);
data_expected.assert_approx_eq(&data_actual, 3);
}
#[test]
fn should_support_swap_dims() {
let data = Data::from([
[[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]],
[[6.0, 7.0, 8.0], [9.0, 10.0, 11.0]],
]);
let tensor = Tensor::<TestBackend, 3>::from_data(data);
let data_actual = tensor.swap_dims(0, 2).into_data();
let data_expected = Data::from([
[[0.0, 6.0], [3.0, 9.0]],
[[1.0, 7.0], [4.0, 10.0]],
[[2.0, 8.0], [5.0, 11.0]],
]);
data_expected.assert_approx_eq(&data_actual, 3);
}
};
}

View File

@ -0,0 +1,15 @@
#[macro_export]
macro_rules! test_stats {
() => {
#[test]
fn test_var() {
let data = Data::from([[0.5, 1.8, 0.2, -2.0], [3.0, -4.0, 5.0, 0.0]]);
let tensor = Tensor::<TestBackend, 2>::from_data(data);
let data_actual = tensor.var(1).into_data();
let data_expected = Data::from([[2.4892], [15.3333]]);
data_expected.assert_approx_eq(&data_actual, 3);
}
};
}

View File

@ -1 +1,4 @@
mod tensor;
pub type TestBackend = burn_tensor::backend::NdArrayBackend<f32>;
#[cfg(feature = "export_tests")]
burn_tensor::test_all!();

View File

@ -1,18 +0,0 @@
use super::super::TestBackend;
use burn_tensor::activation;
use burn_tensor::{Data, Tensor};
#[test]
fn test_gelu() {
let data = Data::from([[
0.5447, 0.9809, 0.4114, 0.1398, 0.8045, 0.4103, 0.2388, 0.5262, 0.6677, 0.6737,
]]);
let tensor = Tensor::<TestBackend, 2>::from_data(data);
let data_actual = activation::gelu(&tensor).to_data();
let data_expected = Data::from([[
0.3851, 0.8207, 0.2714, 0.0777, 0.6351, 0.2704, 0.1419, 0.3687, 0.4993, 0.5051,
]]);
data_expected.assert_approx_eq(&data_actual, 3);
}

View File

@ -1,14 +0,0 @@
use super::super::TestBackend;
use burn_tensor::activation;
use burn_tensor::{Data, Tensor};
#[test]
fn test_relu_d2() {
let data = Data::from([[0.0, -1.0, 2.0], [3.0, -4.0, 5.0]]);
let tensor = Tensor::<TestBackend, 2>::from_data(data);
let data_actual = activation::relu(&tensor).to_data();
let data_expected = Data::from([[0.0, 0.0, 2.0], [3.0, 0.0, 5.0]]);
assert_eq!(data_expected, data_actual);
}

View File

@ -1,14 +0,0 @@
use super::super::TestBackend;
use burn_tensor::activation;
use burn_tensor::{Data, Tensor};
#[test]
fn test_softmax_d2() {
let data = Data::from([[1.0, 7.0], [13.0, -3.0]]);
let tensor = Tensor::<TestBackend, 2>::from_data(data);
let data_actual = activation::softmax(&tensor, 1).to_data();
let data_expected = Data::from([[2.47e-03, 9.975e-01], [1.0, 1.1254e-07]]);
data_actual.assert_approx_eq(&data_expected, 4);
}

View File

@ -1,62 +0,0 @@
use crate::tensor::TestADBackend;
use burn_tensor::{Data, Tensor};
#[test]
fn should_diff_add() {
let data_1 = Data::from([2.0, 5.0]);
let data_2 = Data::from([4.0, 1.0]);
let tensor_1 = Tensor::<TestADBackend, 1>::from_data(data_1);
let tensor_2 = Tensor::<TestADBackend, 1>::from_data(data_2);
let tensor_3 = tensor_1.clone() + tensor_2.clone();
let grads = tensor_3.backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
assert_eq!(grad_1.to_data(), Data::from([1.0, 1.0]));
assert_eq!(grad_2.to_data(), Data::from([1.0, 1.0]));
assert_eq!(tensor_3.into_data(), Data::from([6.0, 6.0]));
}
#[test]
fn should_diff_add_scalar() {
let data = Data::from([2.0, 10.0]);
let tensor = Tensor::<TestADBackend, 1>::from_data(data);
let tensor_out = tensor.add_scalar(5.0);
let grads = tensor_out.backward();
let grad = tensor.grad(&grads).unwrap();
assert_eq!(grad.to_data(), Data::from([1.0, 1.0]));
assert_eq!(tensor_out.into_data(), Data::from([7.0, 15.0]));
}
#[test]
fn test_add_complex_1() {
let data_1: Data<f32, 2> = Data::from([[1.0, 7.0], [13.0, -3.0]]);
let data_2: Data<f32, 2> = Data::from([[4.0, 7.0], [2.0, 3.0]]);
let data_3: Data<f32, 2> = Data::from([[2.0, 2.0], [2.0, 2.0]]);
let tensor_1 = Tensor::<TestADBackend, 2>::from_data(data_1);
let tensor_2 = Tensor::<TestADBackend, 2>::from_data(data_2);
let tensor_3 = Tensor::<TestADBackend, 2>::from_data(data_3);
let tensor_4 = tensor_1.add(&tensor_2);
let tensor_5 = tensor_4
.add(&tensor_3)
.add_scalar(5.0)
.add(&tensor_1)
.add(&tensor_2);
let tensor_6 = tensor_1.add(&tensor_5);
let grads = tensor_6.backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
assert_eq!(grad_1.to_data(), Data::from([[3.0, 3.0], [3.0, 3.0]]));
assert_eq!(grad_2.to_data(), Data::from([[2.0, 2.0], [2.0, 2.0]]));
}

View File

@ -1,118 +0,0 @@
use crate::tensor::TestADTensor;
use burn_tensor::Data;
#[test]
fn should_diff_mean() {
let data_1 = Data::<f32, 2>::from([[1.0, 7.0], [-2.0, -3.0]]);
let data_2 = Data::<f32, 2>::from([[4.0, -7.0], [2.0, 3.0]]);
let tensor_1 = TestADTensor::from_data(data_1);
let tensor_2 = TestADTensor::from_data(data_2);
let tensor_3 = tensor_1.matmul(&tensor_2);
let tensor_4 = tensor_1.mul(&tensor_3.mean().unsqueeze());
let grads = tensor_4.backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
grad_1
.to_data()
.assert_approx_eq(&Data::from([[3.5, 9.5], [3.5, 9.5]]), 5);
grad_2
.to_data()
.assert_approx_eq(&Data::from([[-0.75, -0.75], [3.0, 3.0]]), 5);
}
#[test]
fn should_diff_sum_1() {
let data_1 = Data::<f32, 2>::from([[1.0, 7.0], [-2.0, -3.0]]);
let data_2 = Data::<f32, 2>::from([[4.0, -7.0], [2.0, 3.0]]);
let tensor_1 = TestADTensor::from_data(data_1);
let tensor_2 = TestADTensor::from_data(data_2);
let tensor_3 = tensor_1.matmul(&tensor_2);
let tensor_4 = tensor_1.mul(&tensor_3.sum().unsqueeze());
let grads = tensor_4.backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
grad_1
.to_data()
.assert_approx_eq(&Data::from([[14.0, 38.0], [14.0, 38.0]]), 5);
grad_2
.to_data()
.assert_approx_eq(&Data::from([[-3.0, -3.0], [12.0, 12.0]]), 5);
}
#[test]
fn should_diff_sum_2() {
let data_1 = Data::from([[0.0, 1.0], [3.0, 4.0]]);
let data_2 = Data::from([[6.0, 7.0], [9.0, 10.0]]);
let tensor_1 = TestADTensor::from_data(data_1);
let tensor_2 = TestADTensor::from_data(data_2);
let tensor_3 = tensor_1.matmul(&tensor_2);
let tensor_4 = tensor_3.sum_dim(1);
let tensor_5 = tensor_4.mul(&tensor_3);
let grads = tensor_5.sum().backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
grad_1
.to_data()
.assert_approx_eq(&Data::from([[494.0, 722.0], [2990.0, 4370.0]]), 3);
grad_2
.to_data()
.assert_approx_eq(&Data::from([[690.0, 690.0], [958.0, 958.0]]), 3);
}
#[test]
fn should_diff_mean_dim() {
let data_1 = Data::<f32, 2>::from([[1.0, 7.0], [-2.0, -3.0]]);
let data_2 = Data::<f32, 2>::from([[4.0, -7.0], [2.0, 3.0]]);
let tensor_1 = TestADTensor::from_data(data_1);
let tensor_2 = TestADTensor::from_data(data_2);
let tensor_3 = tensor_1.matmul(&tensor_2);
let tensor_4 = tensor_1.mul(&tensor_3.mean_dim(1).unsqueeze());
let grads = tensor_4.backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
grad_1
.to_data()
.assert_approx_eq(&Data::from([[4.0, 36.0], [3.0, -17.0]]), 5);
grad_2
.to_data()
.assert_approx_eq(&Data::from([[9.0, 9.0], [35.5, 35.5]]), 5);
}
#[test]
fn should_diff_sum_dim() {
let data_1 = Data::<f32, 2>::from([[1.0, 7.0], [-2.0, -3.0]]);
let data_2 = Data::<f32, 2>::from([[4.0, -7.0], [2.0, 3.0]]);
let tensor_1 = TestADTensor::from_data(data_1);
let tensor_2 = TestADTensor::from_data(data_2);
let tensor_3 = tensor_1.matmul(&tensor_2);
let tensor_4 = tensor_1.mul(&tensor_3.sum_dim(1).unsqueeze());
let grads = tensor_4.backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
grad_1
.to_data()
.assert_approx_eq(&Data::from([[8.0, 72.0], [6.0, -34.0]]), 5);
grad_2
.to_data()
.assert_approx_eq(&Data::from([[18.0, 18.0], [71.0, 71.0]]), 5);
}

View File

@ -1,66 +0,0 @@
use crate::tensor::TestADTensor;
use burn_tensor::Data;
#[test]
fn should_diff_cat() {
let data_1 = Data::<_, 2>::from([[2.0, -1.0], [5.0, 2.0]]);
let data_2 = Data::<_, 2>::from([[5.0, 4.0], [-1.0, 4.0]]);
let tensor_1 = TestADTensor::from_data(data_1);
let tensor_2 = TestADTensor::from_data(data_2);
let tensor_3 = tensor_1.matmul(&tensor_2);
let grads = tensor_3.backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
let mut tensor_1_list = Vec::new();
let mut tensor_2_list = Vec::new();
for i in 0..2 {
tensor_1_list.push(TestADTensor::from_data(
tensor_1.index([i..i + 1]).to_data(),
));
tensor_2_list.push(TestADTensor::from_data(
tensor_2.index([i..i + 1]).to_data(),
));
}
let tensor_1_cat = TestADTensor::cat(tensor_1_list.clone(), 0);
let tensor_2_cat = TestADTensor::cat(tensor_2_list.clone(), 0);
let tensor_3_cat = tensor_1_cat.matmul(&tensor_2_cat);
let grads_cat = tensor_3_cat.backward();
let grad_1_cat = tensor_1_cat.grad(&grads_cat).unwrap();
let grad_2_cat = tensor_2_cat.grad(&grads_cat).unwrap();
let grad_1_list_1 = tensor_1_list.get(0).unwrap().grad(&grads_cat).unwrap();
let grad_1_list_2 = tensor_1_list.get(1).unwrap().grad(&grads_cat).unwrap();
let grad_2_list_1 = tensor_2_list.get(0).unwrap().grad(&grads_cat).unwrap();
let grad_2_list_2 = tensor_2_list.get(1).unwrap().grad(&grads_cat).unwrap();
grad_1.to_data().assert_approx_eq(&grad_1_cat.to_data(), 3);
grad_2.to_data().assert_approx_eq(&grad_2_cat.to_data(), 3);
grad_1
.index([0..1])
.to_data()
.assert_approx_eq(&grad_1_list_1.to_data(), 3);
grad_1
.index([1..2])
.to_data()
.assert_approx_eq(&grad_1_list_2.to_data(), 3);
grad_2
.index([0..1])
.to_data()
.assert_approx_eq(&grad_2_list_1.to_data(), 3);
grad_2
.index([1..2])
.to_data()
.assert_approx_eq(&grad_2_list_2.to_data(), 3);
}

View File

@ -1,27 +0,0 @@
use super::super::TestADBackend;
use burn_tensor::{loss, Data, Tensor};
#[test]
fn test_cross_entropy_loss_grad() {
let data_1 = Data::from([[0.0, 1.0], [3.0, 4.0]]);
let data_2 = Data::from([[6.0, 7.0], [9.0, 10.0]]);
let data_targets = Data::from([[0.8, 0.2], [0.9, 0.1]]);
let tensor_1 = Tensor::<TestADBackend, 2>::from_data(data_1);
let tensor_2 = Tensor::<TestADBackend, 2>::from_data(data_2);
let tensor_targets = Tensor::<TestADBackend, 2>::from_data(data_targets);
let tensor_3 = tensor_1.matmul(&tensor_2);
let tensor_4 = loss::cross_entropy_with_logits(&tensor_3, &tensor_targets);
let grads = tensor_4.backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
grad_1
.to_data()
.assert_approx_eq(&Data::from([[0.2655, 0.2655], [0.4496, 0.4496]]), 3);
grad_2
.to_data()
.assert_approx_eq(&Data::from([[-1.3486, 1.3486], [-2.0637, 2.0637]]), 3);
}

View File

@ -1,86 +0,0 @@
use super::super::TestADTensor;
use burn_tensor::Data;
#[test]
fn should_diff_div() {
let data_1 = Data::from([1.0, 7.0]);
let data_2 = Data::from([4.0, 7.0]);
let tensor_1 = TestADTensor::from_data(data_1);
let tensor_2 = TestADTensor::from_data(data_2);
let tensor_3 = tensor_1.div(&tensor_2);
let grads = tensor_3.backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
grad_1
.to_data()
.assert_approx_eq(&Data::from([0.25, 0.1429]), 3);
grad_2
.to_data()
.assert_approx_eq(&Data::from([-0.0625, -0.1429]), 3);
}
#[test]
fn should_diff_div_scalar() {
let data = Data::from([1.0, 7.0]);
let tensor = TestADTensor::from_data(data);
let tensor_out = tensor.div_scalar(4.0);
let grads = tensor_out.backward();
let grad = tensor.grad(&grads).unwrap();
assert_eq!(grad.to_data(), Data::from([0.25, 0.25]));
}
#[test]
fn test_div_complex_1() {
let data_1: Data<f32, 2> = Data::from([[1.0, 7.0], [13.0, -3.0]]);
let data_2: Data<f32, 2> = Data::from([[4.0, 7.0], [2.0, 3.0]]);
let data_3: Data<f32, 2> = Data::from([[2.0, 2.0], [2.0, 2.0]]);
let tensor_1 = TestADTensor::from_data(data_1);
let tensor_2 = TestADTensor::from_data(data_2);
let tensor_3 = TestADTensor::from_data(data_3);
let tensor_4 = tensor_1.div(&tensor_2);
let tensor_5 = tensor_4.div(&tensor_3);
let grads = tensor_5.backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
grad_1
.to_data()
.assert_approx_eq(&Data::from([[0.1250, 0.0714], [0.25, 0.1667]]), 3);
grad_2
.to_data()
.assert_approx_eq(&Data::from([[-0.0312, -0.0714], [-1.6250, 0.1667]]), 3);
}
#[test]
fn test_div_complex_2() {
let data_1 = Data::from([[0.0, 1.0], [3.0, 4.0]]);
let data_2 = Data::from([[6.0, 7.0], [9.0, 10.0]]);
let tensor_1 = TestADTensor::from_data(data_1);
let tensor_2 = TestADTensor::from_data(data_2);
let tensor_3 = tensor_1.matmul(&tensor_2);
let tensor_4 = tensor_3.div(&tensor_2);
let grads = tensor_4.sum().backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
grad_1
.to_data()
.assert_approx_eq(&Data::from([[2.00, 2.9286], [1.3667, 2.0]]), 3);
grad_2
.to_data()
.assert_approx_eq(&Data::from([[0.0833, 0.0959], [-0.0556, -0.0671]]), 3);
}

View File

@ -1,25 +0,0 @@
use crate::tensor::TestADTensor;
use burn_tensor::Data;
#[test]
fn should_diff_erf() {
let data_1 = Data::<f32, 2>::from([[0.0, 1.0], [3.0, 4.0]]);
let data_2 = Data::<f32, 2>::from([[6.0, 7.0], [9.0, 10.0]]);
let tensor_1 = TestADTensor::from_data(data_1);
let tensor_2 = TestADTensor::from_data(data_2);
let tensor_3 = tensor_1.matmul(&tensor_2.erf());
let tensor_4 = tensor_3.matmul(&tensor_2);
let grads = tensor_4.backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
grad_1
.to_data()
.assert_approx_eq(&Data::from([[32.0, 32.0], [32.0, 32.0]]), 3);
grad_2
.to_data()
.assert_approx_eq(&Data::from([[8.0, 8.0], [8.0, 8.0]]), 3);
}

View File

@ -1,25 +0,0 @@
use crate::tensor::TestADTensor;
use burn_tensor::Data;
#[test]
fn should_diff_exp() {
let data_1 = Data::<f32, 2>::from([[1.0, 7.0], [-2.0, -3.0]]);
let data_2 = Data::<f32, 2>::from([[4.0, -7.0], [2.0, 3.0]]);
let tensor_1 = TestADTensor::from_data(data_1);
let tensor_2 = TestADTensor::from_data(data_2);
let tensor_3 = tensor_1.matmul(&tensor_2.exp());
let grads = tensor_3.backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
grad_1
.to_data()
.assert_approx_eq(&Data::from([[54.5991, 27.4746], [54.5991, 27.4746]]), 3);
grad_2.to_data().assert_approx_eq(
&Data::from([[-5.4598e+01, -9.1188e-04], [2.9556e+01, 8.0342e+01]]),
3,
);
}

View File

@ -1,74 +0,0 @@
use crate::tensor::TestADTensor;
use burn_tensor::Data;
#[test]
fn should_diff_matmul_with_index() {
let data_1: Data<f32, 2> = Data::from([[1.0, 7.0], [2.0, 3.0]]);
let data_2: Data<f32, 2> = Data::from([[4.0, 7.0, 100.0], [2.0, 3.0, 15.0]]);
let tensor_1 = TestADTensor::from_data(data_1);
let tensor_2 = TestADTensor::from_data(data_2);
let tensor_3 = tensor_2.index([0..2, 0..2]);
let tensor_4 = &tensor_1.matmul(&tensor_3);
let grads = tensor_4.backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
assert_eq!(grad_1.to_data(), Data::from([[11.0, 5.0], [11.0, 5.0]]));
assert_eq!(
grad_2.to_data(),
Data::from([[3.0, 3.0, 0.0], [10.0, 10.0, 0.0]])
);
}
#[test]
fn should_diff_matmul_with_index_assign() {
let data_1: Data<f32, 2> = Data::from([[1.0, 7.0], [2.0, 3.0]]);
let data_2: Data<f32, 2> = Data::from([[4.0, 7.0], [2.0, 3.0]]);
let data_assigned: Data<f32, 2> = Data::from([[9.0]]);
let tensor_1 = TestADTensor::from_data(data_1);
let tensor_2 = TestADTensor::from_data(data_2);
let tensor_assigned = TestADTensor::from_data(data_assigned);
let tensor_3 = tensor_1.matmul(&tensor_2);
let tensor_4 = tensor_3.index_assign([0..1, 0..1], &tensor_assigned);
let tensor_5 = &tensor_4.matmul(&tensor_1);
let grads = tensor_5.backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
assert_eq!(grad_1.to_data(), Data::from([[58.0, 38.0], [118.0, 82.0]]));
assert_eq!(grad_2.to_data(), Data::from([[16.0, 15.0], [24.0, 50.0]]));
}
#[test]
fn should_diff_matmul_with_index_assign_complex() {
let data_1: Data<f32, 2> = Data::from([[1.0, 7.0], [2.0, 3.0]]);
let data_2: Data<f32, 2> = Data::from([[4.0, 7.0], [2.0, 3.0]]);
let data_3: Data<f32, 2> = Data::from([[9.0]]);
let tensor_1 = TestADTensor::from_data(data_1);
let tensor_2 = TestADTensor::from_data(data_2);
let tensor_3 = TestADTensor::from_data(data_3);
let tensor_4 = tensor_1.matmul(&tensor_2);
let tensor_5 = tensor_2.index([0..1, 0..1]);
let tensor_6 = tensor_5.mul(&tensor_3);
let tensor_7 = tensor_4.index_assign([0..1, 0..1], &tensor_6);
let tensor_8 = &tensor_7.matmul(&tensor_1);
let grads = tensor_8.backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
let grad_3 = tensor_3.grad(&grads).unwrap();
assert_eq!(grad_3.to_data(), Data::from([[32.0]]));
assert_eq!(grad_1.to_data(), Data::from([[85.0, 65.0], [118.0, 82.0]]));
assert_eq!(grad_2.to_data(), Data::from([[88.0, 15.0], [24.0, 50.0]]));
}

View File

@ -1,25 +0,0 @@
use crate::tensor::TestADTensor;
use burn_tensor::Data;
#[test]
fn should_diff_log() {
let data_1 = Data::<f32, 2>::from([[0.0, 1.0], [3.0, 4.0]]);
let data_2 = Data::<f32, 2>::from([[6.0, 7.0], [9.0, 10.0]]);
let tensor_1 = TestADTensor::from_data(data_1);
let tensor_2 = TestADTensor::from_data(data_2);
let tensor_3 = tensor_1.matmul(&tensor_2.log());
let tensor_4 = tensor_3.matmul(&tensor_2);
let grads = tensor_4.backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
grad_1
.to_data()
.assert_approx_eq(&Data::from([[60.2652, 72.3130], [60.2652, 72.3130]]), 3);
grad_2
.to_data()
.assert_approx_eq(&Data::from([[22.8614, 24.5043], [24.5729, 26.8507]]), 3);
}

View File

@ -1,23 +0,0 @@
use crate::tensor::TestADTensor;
use burn_tensor::{BoolTensor, Data};
#[test]
fn should_diff_mask() {
let data_1 = Data::<f32, 2>::from([[1.0, 7.0], [2.0, 3.0]]);
let data_2 = Data::<f32, 2>::from([[4.0, 7.0], [2.0, 3.0]]);
let mask = Data::<bool, 2>::from([[true, false], [false, true]]);
let tensor_1 = TestADTensor::from_data(data_1);
let tensor_2 = TestADTensor::from_data(data_2);
let mask = BoolTensor::from_data(mask);
let tensor_3 = tensor_1.matmul(&tensor_2);
let tensor_4 = tensor_3.mask_fill(&mask, 2.0);
let grads = tensor_4.backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
assert_eq!(grad_1.to_data(), Data::from([[7.0, 3.0], [4.0, 2.0]]));
assert_eq!(grad_2.to_data(), Data::from([[2.0, 1.0], [3.0, 7.0]]));
}

View File

@ -1,75 +0,0 @@
use crate::tensor::TestADTensor;
use burn_tensor::Data;
#[test]
fn should_diff_matmul() {
let data_1: Data<f32, 2> = Data::from([[1.0, 7.0], [2.0, 3.0]]);
let data_2: Data<f32, 2> = Data::from([[4.0, 7.0], [2.0, 3.0]]);
let tensor_1 = TestADTensor::from_data(data_1);
let tensor_2 = TestADTensor::from_data(data_2);
let tensor_3 = &tensor_1.matmul(&tensor_2);
let grads = tensor_3.backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
assert_eq!(grad_1.to_data(), Data::from([[11.0, 5.0], [11.0, 5.0]]));
assert_eq!(grad_2.to_data(), Data::from([[3.0, 3.0], [10.0, 10.0]]));
assert_eq!(
tensor_3.clone().into_data(),
Data::from([[18.0, 28.0], [14.0, 23.0]])
);
}
#[test]
fn test_matmul_complex_1() {
let data_1: Data<f32, 2> = Data::from([[1.0, 7.0], [13.0, -3.0]]);
let data_2: Data<f32, 2> = Data::from([[4.0, 7.0], [2.0, 3.0]]);
let data_3: Data<f32, 2> = Data::from([[2.0, 2.0], [2.0, 2.0]]);
let tensor_1 = TestADTensor::from_data(data_1);
let tensor_2 = TestADTensor::from_data(data_2);
let tensor_3 = TestADTensor::from_data(data_3);
let tensor_4 = tensor_1.matmul(&tensor_2);
let tensor_5 = tensor_4.matmul(&tensor_3);
let grads = tensor_5.backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
assert_eq!(grad_1.to_data(), Data::from([[44.0, 20.0], [44.0, 20.0]]));
assert_eq!(grad_2.to_data(), Data::from([[56.0, 56.0], [16.0, 16.0]]));
}
#[test]
fn test_matmul_complex_2() {
let data_1: Data<f32, 2> = Data::from([[1.0, 7.0], [13.0, -3.0]]);
let data_2: Data<f32, 2> = Data::from([[4.0, 7.0], [2.0, 3.0]]);
let data_3: Data<f32, 2> = Data::from([[2.0, 2.0], [2.0, 2.0]]);
let tensor_1 = TestADTensor::from_data(data_1);
let tensor_2 = TestADTensor::from_data(data_2);
let tensor_3 = TestADTensor::from_data(data_3);
let tensor_4 = tensor_1.matmul(&tensor_2);
let tensor_5 = tensor_4.matmul(&tensor_3);
let tensor_6 = tensor_1.matmul(&tensor_5);
let grads = tensor_6.backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
assert_eq!(
grad_1.to_data(),
Data::from([[800.0, 792.0], [360.0, 592.0]])
);
assert_eq!(
grad_2.to_data(),
Data::from([[264., 264.0], [344.0, 344.0]])
);
}

View File

@ -1,61 +0,0 @@
use crate::tensor::TestADTensor;
use burn_tensor::Data;
#[test]
fn should_diff_mul() {
let data_1 = Data::from([1.0, 7.0]);
let data_2 = Data::from([4.0, 7.0]);
let tensor_1 = TestADTensor::from_data(data_1.clone());
let tensor_2 = TestADTensor::from_data(data_2.clone());
let tensor_3 = tensor_1.mul(&tensor_2);
let grads = tensor_3.backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
assert_eq!(grad_1.to_data(), data_2);
assert_eq!(grad_2.to_data(), data_1);
assert_eq!(tensor_3.into_data(), Data::from([4.0, 49.0]));
}
#[test]
fn should_diff_mul_scalar() {
let data = Data::from([2.0, 5.0]);
let tensor = TestADTensor::from_data(data);
let tensor_out = tensor.mul_scalar(4.0);
let grads = tensor_out.backward();
let grad = tensor.grad(&grads).unwrap();
assert_eq!(tensor_out.into_data(), Data::from([8.0, 20.0]));
assert_eq!(grad.to_data(), Data::from([4.0, 4.0]));
}
#[test]
fn test_mul_complex_1() {
let data_1: Data<f32, 2> = Data::from([[1.0, 7.0], [13.0, -3.0]]);
let data_2: Data<f32, 2> = Data::from([[4.0, 7.0], [2.0, 3.0]]);
let data_3: Data<f32, 2> = Data::from([[2.0, 2.0], [2.0, 2.0]]);
let tensor_1 = TestADTensor::from_data(data_1);
let tensor_2 = TestADTensor::from_data(data_2);
let tensor_3 = TestADTensor::from_data(data_3);
let tensor_4 = tensor_1.mul(&tensor_2);
let tensor_5 = tensor_4.mul(&tensor_3);
let tensor_6 = tensor_1.mul(&tensor_5);
let grads = tensor_6.backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
assert_eq!(
grad_1.to_data(),
Data::from([[16.0, 196.0], [104.0, -36.0]])
);
assert_eq!(grad_2.to_data(), Data::from([[2.0, 98.0], [338.0, 18.0]]));
}

View File

@ -1,21 +0,0 @@
use crate::tensor::TestADTensor;
use burn_tensor::Data;
#[test]
fn should_diff_neg() {
let data_1 = Data::<f32, 2>::from([[1.0, 7.0], [2.0, 3.0]]);
let data_2 = Data::<f32, 2>::from([[4.0, 7.0], [2.0, 3.0]]);
let tensor_1 = TestADTensor::from_data(data_1);
let tensor_2 = TestADTensor::from_data(data_2);
let tensor_3 = tensor_1.matmul(&tensor_2.neg());
let tensor_4 = tensor_3.neg();
let grads = tensor_4.backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
assert_eq!(grad_1.to_data(), Data::from([[11.0, 5.0], [11.0, 5.0]]));
assert_eq!(grad_2.to_data(), Data::from([[3.0, 3.0], [10.0, 10.0]]));
}

View File

@ -1,25 +0,0 @@
use crate::tensor::TestADTensor;
use burn_tensor::Data;
#[test]
fn should_diff_powf() {
let data_1 = Data::<f32, 2>::from([[0.0, 1.0], [3.0, 4.0]]);
let data_2 = Data::<f32, 2>::from([[6.0, 7.0], [9.0, 10.0]]);
let tensor_1 = TestADTensor::from_data(data_1);
let tensor_2 = TestADTensor::from_data(data_2);
let tensor_3 = tensor_1.matmul(&tensor_2.powf(0.4));
let tensor_4 = tensor_3.matmul(&tensor_2);
let grads = tensor_4.backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
grad_1
.to_data()
.assert_approx_eq(&Data::from([[68.0, 79.0328], [68.0, 79.0328]]), 3);
grad_2
.to_data()
.assert_approx_eq(&Data::from([[23.5081, 25.2779], [26.0502, 28.6383]]), 3);
}

View File

@ -1,22 +0,0 @@
use crate::tensor::TestADTensor;
use burn_tensor::{activation::relu, Data};
#[test]
fn should_diff_relu() {
let data_1 = Data::<f32, 2>::from([[1.0, 7.0], [-2.0, -3.0]]);
let data_2 = Data::<f32, 2>::from([[4.0, -7.0], [2.0, 3.0]]);
let tensor_1 = TestADTensor::from_data(data_1);
let tensor_2 = TestADTensor::from_data(data_2);
let tensor_3 = tensor_1.matmul(&tensor_2);
let tensor_4 = relu(&tensor_3);
let tensor_5 = tensor_4.matmul(&tensor_2);
let grads = tensor_5.backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
assert_eq!(grad_1.to_data(), Data::from([[-47.0, 9.0], [-35.0, 15.0]]));
assert_eq!(grad_2.to_data(), Data::from([[15.0, 13.0], [-2.0, 39.0]]));
}

View File

@ -1,21 +0,0 @@
use crate::tensor::TestADTensor;
use burn_tensor::Data;
#[test]
fn should_diff_mul() {
let data_1: Data<f32, 2> = Data::from([[1.0, 7.0], [2.0, 3.0]]);
let data_2: Data<f32, 1> = Data::from([4.0, 7.0, 2.0, 3.0]);
let tensor_1 = TestADTensor::from_data(data_1);
let tensor_2 = TestADTensor::from_data(data_2);
let tensor_3 = tensor_2.reshape([2, 2]);
let tensor_4 = &tensor_1.matmul(&tensor_3);
let grads = tensor_4.backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
assert_eq!(grad_1.to_data(), Data::from([[11.0, 5.0], [11.0, 5.0]]));
assert_eq!(grad_2.to_data(), Data::from([3.0, 3.0, 10.0, 10.0]));
}

View File

@ -1,46 +0,0 @@
use super::super::TestADBackend;
use burn_tensor::{activation, Data, Tensor};
#[test]
fn test_softmax_grad() {
let data_1 = Data::from([[0.0, 1.0], [3.0, 4.0]]);
let data_2 = Data::from([[6.0, 7.0], [9.0, 10.0]]);
let tensor_1 = Tensor::<TestADBackend, 2>::from_data(data_1);
let tensor_2 = Tensor::<TestADBackend, 2>::from_data(data_2);
let tensor_3 = tensor_1.matmul(&tensor_2);
let tensor_4 = activation::softmax(&tensor_3, 1).matmul(&tensor_2);
let grads = tensor_4.backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
grad_1
.to_data()
.assert_approx_eq(&Data::from([[1.1797, 1.1797], [0.0055, 0.0055]]), 3);
grad_2
.to_data()
.assert_approx_eq(&Data::from([[0.2534, 0.2862], [0.5286, 2.9317]]), 3);
}
#[test]
fn test_log_softmax_grad() {
let data_1 = Data::from([[0.0, 1.0], [3.0, 4.0]]);
let data_2 = Data::from([[6.0, 7.0], [9.0, 10.0]]);
let tensor_1 = Tensor::<TestADBackend, 2>::from_data(data_1);
let tensor_2 = Tensor::<TestADBackend, 2>::from_data(data_2);
let tensor_3 = tensor_1.matmul(&tensor_2);
let tensor_4 = activation::log_softmax(&tensor_3, 1).matmul(&tensor_2);
let grads = tensor_4.backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
grad_1
.to_data()
.assert_approx_eq(&Data::from([[-4.3939, -4.3939], [-12.9709, -12.9709]]), 3);
grad_2
.to_data()
.assert_approx_eq(&Data::from([[30.5984, -47.2267], [55.9631, -56.5914]]), 3);
}

View File

@ -1,57 +0,0 @@
use crate::tensor::TestADTensor;
use burn_tensor::Data;
#[test]
fn should_diff_sub() {
let data_1 = Data::from([2.0, 5.0]);
let data_2 = Data::from([4.0, 1.0]);
let tensor_1 = TestADTensor::from_data(data_1);
let tensor_2 = TestADTensor::from_data(data_2);
let tensor_3 = tensor_1.sub(&tensor_2);
let grads = tensor_3.backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
assert_eq!(grad_1.to_data(), Data::from([1.0, 1.0]));
assert_eq!(grad_2.to_data(), Data::from([-1.0, -1.0]));
assert_eq!(tensor_3.into_data(), Data::from([-2.0, 4.0]));
}
#[test]
fn should_diff_sub_scalar() {
let data = Data::from([2.0, 10.0]);
let tensor = TestADTensor::from_data(data);
let tensor_out = tensor.sub_scalar(5.0);
let grads = tensor_out.backward();
let grad = tensor.grad(&grads).unwrap();
assert_eq!(grad.to_data(), Data::from([1.0, 1.0]));
assert_eq!(tensor_out.into_data(), Data::from([-3.0, 5.0]));
}
#[test]
fn test_sub_complex_1() {
let data_1: Data<f32, 2> = Data::from([[1.0, 7.0], [13.0, -3.0]]);
let data_2: Data<f32, 2> = Data::from([[4.0, 7.0], [2.0, 3.0]]);
let data_3: Data<f32, 2> = Data::from([[2.0, 2.0], [2.0, 2.0]]);
let tensor_1 = TestADTensor::from_data(data_1);
let tensor_2 = TestADTensor::from_data(data_2);
let tensor_3 = TestADTensor::from_data(data_3);
let tensor_4 = tensor_1.sub(&tensor_2);
let tensor_5 = tensor_4.sub(&tensor_3).sub_scalar(5.0);
let tensor_6 = tensor_1.sub(&tensor_5);
let grads = tensor_6.backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
assert_eq!(grad_1.to_data(), Data::from([[0.0, 0.0], [0.0, 0.0]]));
assert_eq!(grad_2.to_data(), Data::from([[1.0, 1.0], [1.0, 1.0]]));
}

View File

@ -1,46 +0,0 @@
use crate::tensor::TestADTensor;
use burn_tensor::Data;
#[test]
fn should_diff_transpose() {
let data_1 = Data::<f32, 2>::from([[1.0, 7.0], [2.0, 3.0]]);
let data_2 = Data::<f32, 2>::from([[4.0, 7.0], [2.0, 3.0]]);
let tensor_1 = TestADTensor::from_data(data_1);
let tensor_2 = TestADTensor::from_data(data_2);
let tensor_3 = tensor_1.matmul(&tensor_2.transpose());
let tensor_4 = tensor_3.transpose();
let grads = tensor_4.backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
assert_eq!(grad_1.to_data(), Data::from([[6.0, 10.0], [6.0, 10.0]]));
assert_eq!(grad_2.to_data(), Data::from([[3.0, 10.0], [3.0, 10.0]]));
}
#[test]
fn should_diff_swap_dims() {
let data_1 = Data::<f32, 3>::from([[[0.0, 1.0], [3.0, 4.0]], [[6.0, 7.0], [9.0, 10.0]]]);
let data_2 = Data::<f32, 3>::from([[[1.0, 4.0], [2.0, 5.0]], [[7.0, 10.0], [8.0, 11.0]]]);
let tensor_1 = TestADTensor::from_data(data_1);
let tensor_2 = TestADTensor::from_data(data_2);
let tensor_3 = tensor_1.matmul(&tensor_2.swap_dims(0, 2));
let tensor_4 = tensor_3.matmul(&tensor_2.swap_dims(1, 2));
let grads = tensor_4.backward();
let grad_1 = tensor_1.grad(&grads).unwrap();
let grad_2 = tensor_2.grad(&grads).unwrap();
assert_eq!(
grad_1.to_data(),
Data::from([[[66., 78.], [66., 78.]], [[270., 306.], [270., 306.]]])
);
assert_eq!(
grad_2.to_data(),
Data::from([[[22., 286.], [28., 316.]], [[172., 652.], [190., 694.]]])
);
}

View File

@ -1,19 +0,0 @@
#[cfg(feature = "ndarray")]
pub type TestBackend = burn_tensor::backend::NdArrayBackend<f32>;
#[cfg(all(feature = "tch", not(any(feature = "ndarray"))))]
pub type TestBackend = burn_tensor::backend::TchBackend<f32>;
#[cfg(feature = "ndarray")]
pub type TestADBackend = burn_tensor::backend::NdArrayADBackend<f32>;
#[cfg(all(feature = "tch", not(any(feature = "ndarray"))))]
pub type TestADBackend = burn_tensor::backend::TchADBackend<f32>;
pub type TestADTensor<const D: usize> = burn_tensor::Tensor<TestADBackend, D>;
mod activation;
mod grad;
mod module;
mod ops;
mod stats;

View File

@ -1,24 +0,0 @@
use super::super::TestADBackend;
use burn_tensor::{backend::Backend, module, Data, Tensor};
#[test]
fn test_embedding_backward() {
let weights = Data::from([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]]);
let indexes = Data::from([[0, 1], [1, 1]]);
let x = Data::from([
[[1.0, 2.0], [4.0, 5.0], [3.0, 4.0]],
[[4.0, 5.0], [8.0, 5.0], [1.0, 9.0]],
]);
let weights = Tensor::<TestADBackend, 2>::from_data(weights);
let indexes = Tensor::<<TestADBackend as Backend>::IntegerBackend, 2>::from_data(indexes);
let x = Tensor::<TestADBackend, 3>::from_data(x);
let output = module::embedding(&weights, &indexes);
let output = output.matmul(&x);
let grads = output.backward();
let grad = weights.grad(&grads).unwrap();
let expected =
Data::<<TestADBackend as Backend>::Elem, 2>::from([[3., 9., 7.], [21., 35., 27.]]);
assert_eq!(grad.to_data(), expected);
}

Some files were not shown because too many files have changed in this diff Show More