#[test]
fn deleted_dead_code_files_are_gone() {
let src = std::path::Path::new(concat!(env!("CARGO_MANIFEST_DIR"), "/src"));
for orphan in [
"ops.rs.backup",
"ops_legacy.rs",
"lib_new.rs",
"lazy_ops.rs",
"ops", ] {
let path = src.join(orphan);
assert!(
!path.exists(),
"{orphan} should have been deleted as dead/unreachable code, \
but still exists at {path:?}"
);
}
}
use torsh_tensor::creation::{manual_seed, tensor_1d, tensor_2d, zeros};
use torsh_tensor::Tensor;
#[test]
fn normal_inplace_fills_nonzero_values() {
let mut t = zeros::<f32>(&[10, 10]).expect("zeros should succeed");
t.normal_(0.0, 1.0).expect("normal_ should succeed");
let data = t.to_vec().expect("to_vec should succeed");
let non_zero_count = data.iter().filter(|&&x| x != 0.0).count();
assert!(
non_zero_count > 0,
"normal_ should fill tensor with non-zero values"
);
}
#[test]
fn normal_inplace_mean_is_close_to_requested_mean() {
manual_seed(12345);
let mut t = zeros::<f32>(&[200, 200]).expect("zeros should succeed");
t.normal_(5.0, 2.0).expect("normal_ should succeed");
let data = t.to_vec().expect("to_vec should succeed");
let mean = data.iter().sum::<f32>() / data.len() as f32;
assert!(
(mean - 5.0).abs() < 0.5,
"mean {mean} should be close to the requested mean 5.0"
);
}
#[test]
fn normal_inplace_rejects_negative_std() {
let mut t = zeros::<f32>(&[4]).expect("zeros should succeed");
assert!(t.normal_(0.0, -1.0).is_err());
}
#[test]
fn normal_inplace_rejects_non_finite_parameters() {
let mut t = zeros::<f32>(&[4]).expect("zeros should succeed");
assert!(t.normal_(f64::NAN, 1.0).is_err());
assert!(t.normal_(0.0, f64::INFINITY).is_err());
assert!(t.normal_(f64::NEG_INFINITY, 1.0).is_err());
}
#[test]
fn normal_inplace_accepts_zero_std_as_a_point_mass() {
let mut t = zeros::<f32>(&[16]).expect("zeros should succeed");
t.normal_(3.0, 0.0)
.expect("std=0.0 should be accepted, matching PyTorch's normal_ semantics");
let data = t.to_vec().expect("to_vec should succeed");
assert!(data.iter().all(|&x| (x - 3.0).abs() < 1e-6));
}
#[test]
fn normal_inplace_rejects_tensors_that_require_grad() {
let mut t = zeros::<f32>(&[4]).expect("zeros should succeed");
t = t.requires_grad_(true);
assert!(
t.normal_(0.0, 1.0).is_err(),
"normal_ must refuse to mutate a tensor that requires grad, like every \
other in-place op on this type"
);
}
#[test]
fn multinomial_with_replacement_returns_valid_indices() {
let weights = tensor_1d(&[0.1f32, 0.2, 0.3, 0.4]).expect("tensor_1d should succeed");
let samples = Tensor::multinomial(&weights, 100, true).expect("multinomial should succeed");
assert_eq!(samples.shape().dims(), &[100]);
assert_eq!(samples.dtype(), torsh_core::DType::I64);
let sample_data = samples.to_vec().expect("to_vec should succeed");
for &sample in &sample_data {
assert!(
(0i64..4).contains(&sample),
"sample index {sample} out of bounds"
);
}
}
#[test]
fn multinomial_without_replacement_rejects_oversampling() {
let weights = tensor_1d(&[0.1f32, 0.2, 0.3, 0.4]).expect("tensor_1d should succeed");
assert!(Tensor::multinomial(&weights, 5, false).is_err());
}
#[test]
fn multinomial_without_replacement_returns_unique_indices() {
let weights = tensor_1d(&[0.1f32, 0.2, 0.3, 0.4]).expect("tensor_1d should succeed");
let samples = Tensor::multinomial(&weights, 3, false).expect("multinomial should succeed");
assert_eq!(samples.shape().dims(), &[3]);
let mut sample_data = samples.to_vec().expect("to_vec should succeed");
sample_data.sort_unstable();
let before = sample_data.len();
sample_data.dedup();
assert_eq!(
sample_data.len(),
before,
"samples without replacement should be unique"
);
}
#[test]
fn multinomial_rejects_all_zero_weights() {
let zero_weights = tensor_1d(&[0.0f32, 0.0, 0.0, 0.0]).expect("tensor_1d should succeed");
assert!(Tensor::multinomial(&zero_weights, 1, true).is_err());
}
#[test]
fn multinomial_rejects_negative_weights() {
let weights = tensor_1d(&[0.5f32, -0.1, 0.6]).expect("tensor_1d should succeed");
assert!(Tensor::multinomial(&weights, 1, true).is_err());
}
#[test]
fn multinomial_rejects_non_finite_weights() {
let weights = tensor_1d(&[0.5f32, f32::NAN, 0.6]).expect("tensor_1d should succeed");
assert!(Tensor::multinomial(&weights, 1, true).is_err());
}
#[test]
fn multinomial_rejects_non_1d_weights() {
let weights = tensor_2d(&[&[0.5f32, 0.5], &[0.5, 0.5]]).expect("tensor_2d should succeed");
assert!(Tensor::multinomial(&weights, 1, true).is_err());
}
#[test]
fn multinomial_never_samples_a_zero_weight_category() {
let weights = tensor_1d(&[1.0f32, 0.0, 1.0, 0.0]).expect("tensor_1d should succeed");
let samples = Tensor::multinomial(&weights, 200, true).expect("multinomial should succeed");
let data = samples.to_vec().expect("to_vec should succeed");
assert!(
data.iter().all(|&idx| idx == 0 || idx == 2),
"multinomial must never select a zero-weight category"
);
}