use super::*;
#[test]
fn padded_positions_never_contribute() {
let identity = Tensor::new(
&[[1f32, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]],
&Device::Cpu,
)
.unwrap();
let zero_bias = Tensor::new(&[0f32, 0.0, 0.0], &Device::Cpu).unwrap();
let norm_weight = Tensor::new(&[1f32, 1.0, 1.0], &Device::Cpu).unwrap();
let decoder_weight = Tensor::new(&[[1f32, 0.0, 0.0]], &Device::Cpu).unwrap();
let decoder_bias = Tensor::new(&[0f32], &Device::Cpu).unwrap();
let build = || {
head(
Linear::new(identity.clone(), Some(zero_bias.clone())),
norm(norm_weight.clone(), zero_bias.clone(), 3, 1e-12),
decoder_weight.clone(),
decoder_bias.clone(),
CHUNK,
)
};
let alone_hidden = Tensor::new(&[[[0.3f32, 0.3, 0.3], [0.31, 0.29, 0.3]]], &Device::Cpu).unwrap();
let alone_mask = Tensor::new(&[[1u32, 1]], &Device::Cpu).unwrap();
let padded_hidden = Tensor::new(
&[[[0.3f32, 0.3, 0.3], [0.31, 0.29, 0.3], [50.0, -50.0, -50.0]]],
&Device::Cpu,
)
.unwrap();
let padded_mask = Tensor::new(&[[1u32, 1, 0]], &Device::Cpu).unwrap();
let alone = build().pool_logits(&alone_hidden, &alone_mask).unwrap();
let padded = build().pool_logits(&padded_hidden, &padded_mask).unwrap();
assert_rows_close(&alone, &padded);
}
#[test]
fn the_decoder_is_the_embedding_transposed() {
let transform_weight = Tensor::new(&[[0f32, 0.0], [0.0, 0.0]], &Device::Cpu).unwrap();
let transform_bias = Tensor::new(&[0f32, 0.0], &Device::Cpu).unwrap();
let transform_norm = norm(
Tensor::new(&[1f32, 1.0], &Device::Cpu).unwrap(),
Tensor::new(&[1f32, 2.0], &Device::Cpu).unwrap(),
2,
1e-12,
);
let decoder_weight = Tensor::new(&[[1f32, 10.0], [100.0, 1000.0]], &Device::Cpu).unwrap();
let decoder_bias = Tensor::new(&[0f32, 0.0], &Device::Cpu).unwrap();
let model = head(
Linear::new(transform_weight, Some(transform_bias)),
transform_norm,
decoder_weight,
decoder_bias,
CHUNK,
);
let hidden = Tensor::new(&[[[0f32, 0.0]]], &Device::Cpu).unwrap();
let mask = Tensor::new(&[[1u32]], &Device::Cpu).unwrap();
let rows = model.pool_logits(&hidden, &mask).unwrap();
assert_eq!(rows[0].indices, vec![0, 1]);
let expected = [22f32.ln(), 2101f32.ln()];
for (got, want) in rows[0].values.iter().zip(expected) {
assert!((got - want).abs() < 1e-4, "{got} vs {want}");
}
}
#[test]
fn output_is_sorted_and_positive() {
let transform = Linear::new(
Tensor::new(&[[0f32]], &Device::Cpu).unwrap(),
Some(Tensor::new(&[0f32], &Device::Cpu).unwrap()),
);
let transform_norm = norm(
Tensor::new(&[1f32], &Device::Cpu).unwrap(),
Tensor::new(&[2f32], &Device::Cpu).unwrap(),
1,
1e-12,
);
let decoder_weight = Tensor::new(&[[-1f32], [1.0], [0.5]], &Device::Cpu).unwrap();
let decoder_bias = Tensor::new(&[0f32, 0.0, 0.0], &Device::Cpu).unwrap();
let model = head(
transform,
transform_norm,
decoder_weight,
decoder_bias,
CHUNK,
);
let hidden = Tensor::new(&[[[0f32]]], &Device::Cpu).unwrap();
let mask = Tensor::new(&[[1u32]], &Device::Cpu).unwrap();
let rows = model.pool_logits(&hidden, &mask).unwrap();
assert_eq!(rows[0].indices, vec![1, 2]);
assert!(rows[0].indices.windows(2).all(|w| w[0] < w[1]));
assert!(rows[0].values.iter().all(|&v| v > 0.0));
let expected = [3f32.ln(), 2f32.ln()];
for (got, want) in rows[0].values.iter().zip(expected) {
assert!((got - want).abs() < 1e-5, "{got} vs {want}");
}
}