#[cfg(test)]
mod tests {
use crate::swin::config::SwinConfig;
use crate::swin::model::{
window_partition, window_reverse, cyclic_shift,
PatchMerging, SwinModel, SwinStage,
};
use crate::swin::tasks::SwinForImageClassification;
use scirs2_core::ndarray::Array4;
use trustformers_core::device::Device;
use trustformers_core::traits::Config;
fn mini_config() -> SwinConfig {
SwinConfig {
image_size: 32, patch_size: 4,
num_channels: 3,
embed_dim: 16,
depths: vec![1, 1],
num_heads: vec![2, 4],
window_size: 4, mlp_ratio: 2.0,
qkv_bias: true,
drop_rate: 0.0,
attn_drop_rate: 0.0,
drop_path_rate: 0.0,
num_labels: 5,
layer_norm_eps: 1e-5,
}
}
#[test]
fn test_swin_config_tiny() {
let config = SwinConfig::swin_tiny_patch4_window7_224();
assert_eq!(config.image_size, 224);
assert_eq!(config.patch_size, 4);
assert_eq!(config.embed_dim, 96);
assert_eq!(config.depths, vec![2, 2, 6, 2]);
assert_eq!(config.num_heads, vec![3, 6, 12, 24]);
assert_eq!(config.window_size, 7);
assert_eq!(config.num_stages(), 4);
assert_eq!(config.initial_resolution(), 56); assert_eq!(config.stage_dim(0), 96);
assert_eq!(config.stage_dim(1), 192);
assert_eq!(config.stage_dim(2), 384);
assert_eq!(config.stage_dim(3), 768);
assert_eq!(config.final_dim(), 768);
config.validate().expect("tiny config should be valid");
}
#[test]
fn test_swin_config_small() {
let config = SwinConfig::swin_small_patch4_window7_224();
assert_eq!(config.embed_dim, 96);
assert_eq!(config.depths, vec![2, 2, 18, 2]);
config.validate().expect("small config should be valid");
}
#[test]
fn test_swin_config_base() {
let config = SwinConfig::swin_base_patch4_window7_224();
assert_eq!(config.embed_dim, 128);
assert_eq!(config.final_dim(), 1024); config.validate().expect("base config should be valid");
}
#[test]
fn test_swin_config_base_384() {
let config = SwinConfig::swin_base_patch4_window12_384();
assert_eq!(config.image_size, 384);
assert_eq!(config.window_size, 12);
assert_eq!(config.initial_resolution(), 96);
config.validate().expect("base-384 config should be valid");
}
#[test]
fn test_swin_config_invalid_depths_heads_mismatch() {
let config = SwinConfig {
depths: vec![2, 2, 6],
num_heads: vec![3, 6], ..SwinConfig::swin_tiny_patch4_window7_224()
};
assert!(config.validate().is_err());
}
#[test]
fn test_window_partition() {
let x = Array4::<f32>::ones((1, 14, 14, 16));
let windows = window_partition(&x, 7).expect("window_partition should succeed");
assert_eq!(windows.shape(), &[4, 7, 7, 16]);
}
#[test]
fn test_window_partition_multiple_batches() {
let x = Array4::<f32>::ones((2, 14, 14, 8));
let windows = window_partition(&x, 7).expect("window_partition should succeed");
assert_eq!(windows.shape(), &[8, 7, 7, 8]);
}
#[test]
fn test_window_reverse_roundtrip() {
let x = Array4::from_shape_fn((1, 14, 14, 4), |(_, i, j, k)| {
(i * 14 * 4 + j * 4 + k) as f32
});
let windows = window_partition(&x, 7).expect("partition should succeed");
let recovered = window_reverse(&windows, 7, 14, 14).expect("reverse should succeed");
assert_eq!(recovered.shape(), x.shape());
for bi in 0..1 {
for i in 0..14 {
for j in 0..14 {
for k in 0..4 {
assert!((recovered[[bi, i, j, k]] - x[[bi, i, j, k]]).abs() < 1e-5);
}
}
}
}
}
#[test]
fn test_cyclic_shift_identity() {
let x = Array4::<f32>::ones((1, 7, 7, 4));
let shifted = cyclic_shift(&x, 0);
assert_eq!(shifted.shape(), x.shape());
for bi in 0..1 {
for i in 0..7 {
for j in 0..7 {
for k in 0..4 {
assert!((shifted[[bi, i, j, k]] - x[[bi, i, j, k]]).abs() < 1e-5);
}
}
}
}
}
#[test]
fn test_patch_merging_shapes() {
let pm = PatchMerging::new(16, 1e-5, Device::CPU).expect("PatchMerging::new should succeed");
let x = Array4::<f32>::zeros((2, 14, 14, 16));
let out = pm.forward(&x).expect("PatchMerging forward should succeed");
assert_eq!(out.shape(), &[2, 7, 7, 32]);
}
#[test]
fn test_patch_merging_wrong_channels() {
let pm = PatchMerging::new(16, 1e-5, Device::CPU).expect("PatchMerging::new should succeed");
let x = Array4::<f32>::zeros((1, 14, 14, 8)); assert!(pm.forward(&x).is_err());
}
#[test]
fn test_swin_stage_forward_no_downsample() {
let stage = SwinStage::new(
16, 2, 2, 7, 2.0, true, 0.0, 0.0, 1e-5, false, Device::CPU,
)
.expect("SwinStage::new should succeed");
let x = Array4::<f32>::zeros((1, 7, 7, 16));
let out = stage.forward(&x).expect("stage forward should succeed");
assert_eq!(out.shape(), &[1, 7, 7, 16]);
}
#[test]
fn test_swin_stage_forward_with_downsample() {
let stage = SwinStage::new(
16, 2, 2, 7, 2.0, true, 0.0, 0.0, 1e-5, true, Device::CPU,
)
.expect("SwinStage::new should succeed");
let x = Array4::<f32>::zeros((1, 14, 14, 16));
let out = stage.forward(&x).expect("stage with downsample should succeed");
assert_eq!(out.shape(), &[1, 7, 7, 32]);
}
#[test]
fn test_swin_model_shapes() {
let config = mini_config();
let model = SwinModel::new(config.clone()).expect("SwinModel::new should succeed");
let images = Array4::<f32>::zeros((1, 32, 32, 3));
let features = model.forward(&images).expect("SwinModel forward should succeed");
let expected_dim = config.final_dim();
assert_eq!(features.shape(), &[1, expected_dim]);
}
#[test]
fn test_swin_model_batch() {
let config = mini_config();
let model = SwinModel::new(config.clone()).expect("SwinModel::new should succeed");
let images = Array4::<f32>::zeros((3, 32, 32, 3));
let features = model.forward(&images).expect("SwinModel forward should succeed");
assert_eq!(features.shape(), &[3, config.final_dim()]);
}
#[test]
fn test_swin_classification_head() {
let config = mini_config();
let model = SwinForImageClassification::new(config.clone())
.expect("SwinForImageClassification::new should succeed");
let images = Array4::<f32>::zeros((2, 32, 32, 3));
let logits = model.forward(&images).expect("classification forward should succeed");
assert_eq!(logits.shape(), &[2, config.num_labels]);
}
#[test]
fn test_swin_weight_map_structure() {
let config = mini_config();
let model = SwinForImageClassification::new(config.clone())
.expect("SwinForImageClassification::new should succeed");
let wmap = model.weight_map();
assert!(wmap.contains_key("classifier.weight"));
assert!(wmap.contains_key("classifier.bias"));
assert!(wmap.contains_key("swin.layernorm.weight"));
for s_idx in 0..config.num_stages() {
for b_idx in 0..config.depths[s_idx] {
let key = format!(
"swin.encoder.layers.{}.blocks.{}.layernorm_before.weight",
s_idx, b_idx
);
assert!(wmap.contains_key(&key), "Missing key: {}", key);
}
}
}
}