use candle_core::Tensor;
use super::*;
use crate::models::nomic_bert::config::tests::V1;
use crate::models::nomic_bert::config::tests::V1_5;
#[test]
fn the_dynamic_base_matches_the_reference_formula_at_the_tabulated_lengths() {
for (seq, expected) in [
(2048usize, 1000.00),
(2049, 1001.01),
(3000, 1971.04),
(4096, 3108.22),
(8192, 7453.48),
] {
let base = dynamic_ntk_base(1000.0, 2.0, 2048, seq, 64);
assert!(
(base - expected).abs() < 0.01,
"seq {seq}: {base} vs {expected}"
);
}
}
#[test]
fn the_base_is_unscaled_at_or_below_the_trained_length() {
assert_eq!(dynamic_ntk_base(1000.0, 2.0, 2048, 2048, 64), 1000.0);
assert_eq!(dynamic_ntk_base(1000.0, 2.0, 2048, 7, 64), 1000.0);
}
#[test]
fn only_a_configured_factor_scales_the_table() {
let v1: NomicBertConfig = serde_json::from_str(V1).unwrap();
let v1_5: NomicBertConfig = serde_json::from_str(V1_5).unwrap();
let v1_rotary = NomicRotary::new(&v1, DType::F32, &Device::Cpu).unwrap();
let v1_5_rotary = NomicRotary::new(&v1_5, DType::F32, &Device::Cpu).unwrap();
let reference = RotaryEmbedding::new(64, 3000, 1000.0, DType::F32, &Device::Cpu).unwrap();
let column = 64 / 2 - 1;
let position = 2999;
let value_at =
|rope: &RotaryEmbedding| rope.table().0.to_vec2::<f32>().unwrap()[position][column];
let reference_value = value_at(&reference);
let v1_value = value_at(v1_rotary.for_length(3000).unwrap().as_rope());
let v1_5_value = value_at(v1_5_rotary.for_length(3000).unwrap().as_rope());
assert_eq!(
v1_5_value, reference_value,
"v1.5 col {column} pos {position}: {v1_5_value} vs unscaled reference {reference_value}"
);
assert!(
(v1_value - reference_value).abs() > 0.1,
"v1 col {column} pos {position}: {v1_value} vs unscaled reference {reference_value}, diff {}",
(v1_value - reference_value).abs()
);
}
#[test]
fn short_lengths_use_the_cached_table() {
let v1: NomicBertConfig = serde_json::from_str(V1).unwrap();
let v1_5: NomicBertConfig = serde_json::from_str(V1_5).unwrap();
for config in [&v1, &v1_5] {
let rotary = NomicRotary::new(config, DType::F32, &Device::Cpu).unwrap();
assert!(matches!(rotary.for_length(100).unwrap(), Table::Cached(_)));
}
}
#[test]
fn the_table_rotates_in_the_rotate_half_convention() {
let v1_5: NomicBertConfig = serde_json::from_str(V1_5).unwrap();
let rotary = NomicRotary::new(&v1_5, DType::F32, &Device::Cpu).unwrap();
let table = rotary.for_length(5).unwrap();
let rope = table.as_rope();
let q = Tensor::randn(0f32, 1.0, (2, 3, 5, 64), &Device::Cpu).unwrap();
let k = Tensor::randn(0f32, 1.0, (2, 1, 5, 64), &Device::Cpu).unwrap();
let (rq, rk) = rope.apply(&q, &k).unwrap();
let (cos, sin) = rope.table();
let cos = cos.narrow(0, 0, 5).unwrap();
let sin = sin.narrow(0, 0, 5).unwrap();
let slow_q = candle_nn::rotary_emb::rope_slow(&q, &cos, &sin).unwrap();
let slow_k = candle_nn::rotary_emb::rope_slow(&k, &cos, &sin).unwrap();
let diff = |a: &Tensor, b: &Tensor| {
(a - b)
.unwrap()
.abs()
.unwrap()
.flatten_all()
.unwrap()
.max(0)
.unwrap()
.to_scalar::<f32>()
.unwrap()
};
assert!(diff(&rq, &slow_q) < 1e-5 && diff(&rk, &slow_k) < 1e-5);
let first = |t: &Tensor| t.narrow(2, 0, 1).unwrap();
assert!(
diff(&first(&rq), &first(&q)) < 1e-6,
"position 0 must be unchanged"
);
}