use std::cmp::Reverse;
use super::*;
#[test]
fn value_returns_the_number_it_was_built_from() {
let score = RerankScore::new(3.5);
assert_eq!(score.value(), 3.5);
}
#[test]
fn sorting_descending_ranks_the_best_match_first() {
let mut scores = [
RerankScore::new(-11.25),
RerankScore::new(8.85),
RerankScore::new(0.0),
];
scores.sort_by_key(|s| Reverse(*s));
assert_eq!(
scores.iter().map(RerankScore::value).collect::<Vec<_>>(),
vec![8.85, 0.0, -11.25]
);
}
#[test]
fn a_nan_score_compares_and_sorts_without_panicking() {
let nan = RerankScore::new(f32::NAN);
let ordinary = RerankScore::new(1.0);
let _ = nan.cmp(&ordinary);
let mut scores = [ordinary, nan, RerankScore::new(-1.0)];
scores.sort();
assert!(scores[0].value().is_nan());
assert_eq!(scores[1].value(), -1.0);
assert_eq!(scores[2].value(), 1.0);
}
fn nan(sign_bit: bool) -> f32 {
f32::from_bits(if sign_bit { 0xffc0_0000 } else { 0x7fc0_0000 })
}
#[test]
fn a_nan_of_either_sign_sorts_below_every_number() {
for sign_bit in [false, true] {
let value = nan(sign_bit);
assert_eq!(value.is_sign_negative(), sign_bit);
let nan = RerankScore::new(value);
for number in [f32::NEG_INFINITY, -11.25, -0.0, 0.0, 8.85, f32::INFINITY] {
let number = RerankScore::new(number);
assert_eq!(
nan.cmp(&number),
Ordering::Less,
"{value:?} against {number:?}"
);
assert_eq!(
number.cmp(&nan),
Ordering::Greater,
"{number:?} against {value:?}"
);
}
}
assert_eq!(
RerankScore::new(nan(false)).cmp(&RerankScore::new(nan(true))),
Ordering::Equal
);
}
#[test]
fn a_best_first_sort_ranks_a_nan_of_either_sign_last() {
let values = [8.85, nan(false), -11.25, nan(true), f32::NEG_INFINITY];
let mut ranked: Vec<(usize, RerankScore)> = values
.iter()
.copied()
.map(RerankScore::new)
.enumerate()
.collect();
ranked.sort_by_key(|&(_, score)| Reverse(score));
assert_eq!(
ranked.iter().map(|&(index, _)| index).collect::<Vec<_>>(),
vec![0, 2, 4, 1, 3]
);
}
#[test]
fn score_activation_serializes_snake_case() {
assert_eq!(
serde_json::to_string(&ScoreActivation::Identity).unwrap(),
"\"identity\""
);
assert_eq!(
serde_json::from_str::<ScoreActivation>("\"sigmoid\"").unwrap(),
ScoreActivation::Sigmoid
);
}
#[test]
fn identity_returns_the_raw_value_unchanged() {
assert_eq!(ScoreActivation::Identity.apply(8.845_856), 8.845_856);
assert_eq!(ScoreActivation::Identity.apply(-11.245_562), -11.245_562);
}
#[test]
fn sigmoid_bounds_the_value_to_zero_one_and_matches_the_textbook_formula() {
assert_eq!(ScoreActivation::Sigmoid.apply(0.0), 0.5);
let relevant = ScoreActivation::Sigmoid.apply(8.845_856);
assert!((relevant - 0.999_856_04).abs() < 1e-6, "{relevant}");
let irrelevant = ScoreActivation::Sigmoid.apply(-11.245_562);
assert!((irrelevant - 0.000_013_065).abs() < 1e-7, "{irrelevant}");
for raw in [-1000.0f32, 1000.0] {
let bounded = ScoreActivation::Sigmoid.apply(raw);
assert!((0.0..=1.0).contains(&bounded), "{raw} -> {bounded}");
}
}