use crate::error::MattenMlprepError;
use crate::util::matrix_dims;
use matten::Tensor;
pub fn train_test_split(
x: &Tensor,
train_ratio: f64,
) -> Result<(Tensor, Tensor), MattenMlprepError> {
let (rows, cols) = matrix_dims(x)?;
if !train_ratio.is_finite() || train_ratio <= 0.0 || train_ratio >= 1.0 {
return Err(MattenMlprepError::InvalidRatio(train_ratio));
}
let n_train = (rows as f64 * train_ratio).floor() as usize;
if n_train == 0 {
return Err(MattenMlprepError::EmptySplit { rows, train_ratio });
}
let n_test = rows - n_train;
let data = x.as_slice();
let split = n_train * cols;
let train = Tensor::try_new(data[..split].to_vec(), &[n_train, cols])
.map_err(MattenMlprepError::Matten)?;
let test = Tensor::try_new(data[split..].to_vec(), &[n_test, cols])
.map_err(MattenMlprepError::Matten)?;
Ok((train, test))
}
struct SplitMix64(u64);
impl SplitMix64 {
fn new(seed: u64) -> Self {
Self(seed)
}
fn next_u64(&mut self) -> u64 {
self.0 = self.0.wrapping_add(0x9E37_79B9_7F4A_7C15);
let mut z = self.0;
z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
z ^ (z >> 31)
}
fn next_below(&mut self, bound: usize) -> usize {
(self.next_u64() % bound as u64) as usize
}
}
pub fn train_test_split_seeded(
x: &Tensor,
train_ratio: f64,
seed: u64,
) -> Result<(Tensor, Tensor), MattenMlprepError> {
let (rows, cols) = matrix_dims(x)?;
if !train_ratio.is_finite() || train_ratio <= 0.0 || train_ratio >= 1.0 {
return Err(MattenMlprepError::InvalidRatio(train_ratio));
}
let n_train = (rows as f64 * train_ratio).floor() as usize;
if n_train == 0 {
return Err(MattenMlprepError::EmptySplit { rows, train_ratio });
}
let mut order: Vec<usize> = (0..rows).collect();
let mut rng = SplitMix64::new(seed);
for i in (1..rows).rev() {
let j = rng.next_below(i + 1);
order.swap(i, j);
}
let data = x.as_slice();
let gather = |idx: &[usize]| -> Vec<f64> {
let mut out = Vec::with_capacity(idx.len() * cols);
for &r in idx {
out.extend_from_slice(&data[r * cols..(r + 1) * cols]);
}
out
};
let train = Tensor::try_new(gather(&order[..n_train]), &[n_train, cols])
.map_err(MattenMlprepError::Matten)?;
let test = Tensor::try_new(gather(&order[n_train..]), &[rows - n_train, cols])
.map_err(MattenMlprepError::Matten)?;
Ok((train, test))
}