use std::sync::Arc;
use crate::outer_subsample::OuterScoreSubsample;
#[derive(Clone, Debug)]
pub struct RowSubsampleMask {
pub id: u64,
pub mask: Option<Arc<OuterScoreSubsample>>,
}
impl RowSubsampleMask {
pub fn full_data(n: usize) -> Self {
Self {
id: hash_full(n),
mask: None,
}
}
pub fn subsample(mask: Arc<OuterScoreSubsample>) -> Self {
let id = hash_subsample(&mask);
Self {
id,
mask: Some(mask),
}
}
}
fn splitmix64(x: u64) -> u64 {
gam_linalg::utils::splitmix64_hash(x)
}
const FULL_DATA_ROW_SUBSAMPLE_SENTINEL: u64 = 0xA5A5_5A5A_DEAD_BEEF;
fn hash_full(n: usize) -> u64 {
let mut h = splitmix64(FULL_DATA_ROW_SUBSAMPLE_SENTINEL ^ (n as u64));
if h == 0 {
h = 0x1234_5678_9ABC_DEF0;
}
h
}
fn hash_subsample(mask: &Arc<OuterScoreSubsample>) -> u64 {
let ptr = Arc::as_ptr(mask) as u64;
let mut h = splitmix64(ptr);
h ^= splitmix64(mask.n_full as u64);
h ^= splitmix64(mask.len() as u64);
h ^= splitmix64(mask.seed);
h ^= splitmix64((mask.weight_scale.to_bits()) ^ 0xC0FF_EE00_0000_0000);
if h == 0 {
h = 0xDEAD_BEEF_FEED_FACE;
}
h
}
#[cfg(test)]
mod tests {
use super::*;
use crate::outer_subsample::OuterScoreSubsample;
#[test]
fn full_data_id_is_stable_per_n() {
let a = RowSubsampleMask::full_data(100);
let b = RowSubsampleMask::full_data(100);
let c = RowSubsampleMask::full_data(101);
assert_eq!(a.id, b.id);
assert_ne!(a.id, c.id);
assert!(a.mask.is_none());
}
#[test]
fn subsample_id_matches_for_same_arc() {
let s = Arc::new(OuterScoreSubsample::from_uniform_inclusion_mask(
vec![1, 3, 5],
10,
42,
));
let a = RowSubsampleMask::subsample(Arc::clone(&s));
let b = RowSubsampleMask::subsample(Arc::clone(&s));
assert_eq!(a.id, b.id);
}
#[test]
fn subsample_id_differs_for_different_arcs() {
let s1 = Arc::new(OuterScoreSubsample::from_uniform_inclusion_mask(
vec![1, 3, 5],
10,
42,
));
let s2 = Arc::new(OuterScoreSubsample::from_uniform_inclusion_mask(
vec![1, 3, 5],
10,
42,
));
let a = RowSubsampleMask::subsample(s1);
let b = RowSubsampleMask::subsample(s2);
assert_ne!(a.id, b.id);
}
}