#![cfg(feature = "eval")]
use rlx_tensor::Tensor;
fn approx(a: &[f32], b: &[f32]) {
assert_eq!(a.len(), b.len(), "length mismatch: {a:?} vs {b:?}");
for (x, y) in a.iter().zip(b) {
assert!((x - y).abs() < 1e-5, "{a:?} != {b:?}");
}
}
#[test]
fn add_two_constants() {
let a = Tensor::from_vec(vec![1.0, 2.0, 3.0], [3]);
let b = Tensor::ones([3]);
let c = &a + &b;
approx(&c.to_vec(), &[2.0, 3.0, 4.0]);
}
#[test]
fn fused_chain() {
let x = Tensor::from_vec(vec![-1.0, 0.5, 2.0, -3.0], [4]);
let y = ((&x * 2.0f32) + 1.0f32).relu();
approx(&y.to_vec(), &[0.0, 2.0, 5.0, 0.0]);
}
#[test]
fn matmul_constants() {
let a = Tensor::from_vec(vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0], [2, 3]);
let b = Tensor::from_vec(vec![1.0, 0.0, 0.0, 1.0, 1.0, 1.0], [3, 2]);
let c = a.matmul(&b);
approx(&c.to_vec(), &[4.0, 5.0, 10.0, 11.0]);
}
#[test]
fn reduce_sum() {
let a = Tensor::from_vec(vec![1.0, 2.0, 3.0, 4.0], [2, 2]);
let s = a.sum([1], false);
approx(&s.to_vec(), &[3.0, 7.0]);
}
#[test]
fn clamp_and_minmax() {
let a = Tensor::from_vec(vec![-2.0, 0.5, 9.0], [3]);
approx(&a.clamp(0.0, 6.0).to_vec(), &[0.0, 0.5, 6.0]);
let b = Tensor::from_vec(vec![1.0, 1.0, 1.0], [3]);
approx(&a.maximum(&b).to_vec(), &[1.0, 1.0, 9.0]);
}
#[test]
fn arange_constructor() {
let a = Tensor::arange(5);
approx(&a.to_vec(), &[0.0, 1.0, 2.0, 3.0, 4.0]);
}
#[cfg(feature = "eval-metal")]
#[test]
fn metal_is_auto_selected_and_computes() {
use rlx_tensor::{Device, fastest_device, is_available};
assert!(is_available(Device::Metal), "Metal backend should be live");
assert_eq!(fastest_device(), Device::Metal, "GPU should outrank CPU");
let a = Tensor::from_vec(vec![1.0, 2.0, 3.0], [3]);
let b = Tensor::from_vec(vec![4.0, 5.0, 6.0], [3]);
approx(&(&a * &b).to_vec(), &[4.0, 10.0, 18.0]);
}
#[test]
fn auto_selected_device_is_available() {
use rlx_tensor::{Device, available_devices, fastest_device, is_available};
let d = fastest_device();
assert!(is_available(d), "auto-selected device must be available");
assert!(available_devices().contains(&Device::Cpu));
let x = Tensor::from_vec(vec![1.0, 2.0], [2]);
approx(&x.relu().to_vec(), &[1.0, 2.0]);
}
#[test]
fn reused_operand_across_exprs() {
let a = Tensor::from_vec(vec![1.0, 2.0], [2]);
let b = Tensor::from_vec(vec![10.0, 20.0], [2]);
let lhs = &a + &b; let rhs = &a * &b; let out = &lhs + &rhs; approx(&out.to_vec(), &[21.0, 62.0]);
}