use crate::latent::{Continuous, FrameSeq, Latent};
use crate::{Error, Result, Tensor};
pub(super) fn continuous_set(samples: &[Latent<Continuous>]) -> Result<Vec<&FrameSeq>> {
let Some(first) = samples.first() else {
return Err(Error::validation("no samples to measure"));
};
let expected = first.repr();
if expected.values().is_empty() {
return Err(Error::validation("samples must have at least one element"));
}
let mut seqs = Vec::with_capacity(samples.len());
for sample in samples {
let seq = sample.repr();
if !same_shape(seq, expected) {
return Err(Error::validation(format!(
"all samples must share a shape; expected {:?}, got {:?}",
shape(expected),
shape(seq)
)));
}
seqs.push(seq);
}
Ok(seqs)
}
pub(super) fn shape(seq: &FrameSeq) -> [usize; 2] {
[seq.frames(), seq.dim()]
}
fn same_shape(a: &FrameSeq, b: &FrameSeq) -> bool {
a.frames() == b.frames() && a.dim() == b.dim()
}
pub(super) fn mean_elementwise_variance(seqs: &[&FrameSeq]) -> f64 {
let n = seqs.len() as f64;
let len = seqs[0].values().len();
let mut total = 0.0f64;
for j in 0..len {
let mean = seqs.iter().map(|t| t.values()[j] as f64).sum::<f64>() / n;
total += seqs
.iter()
.map(|t| {
let d = t.values()[j] as f64 - mean;
d * d
})
.sum::<f64>()
/ n;
}
total / len as f64
}
pub(super) fn mean_squared_error(a: &Tensor, b: &Tensor) -> Result<f64> {
if a.shape() != b.shape() {
return Err(Error::validation(format!(
"loss operands must share a shape; got {:?} and {:?}",
a.shape(),
b.shape()
)));
}
if a.is_empty() {
return Err(Error::validation(
"loss operands must have at least one element",
));
}
let mut total = 0.0f64;
for (&x, &y) in a.data().iter().zip(b.data()) {
if !x.is_finite() || !y.is_finite() {
return Err(Error::validation(format!(
"loss operands must be finite; found {x} and {y}"
)));
}
let d = x as f64 - y as f64;
total += d * d;
}
Ok(total / a.len() as f64)
}
pub(super) fn l2_distance(a: &FrameSeq, b: &FrameSeq) -> f64 {
a.values()
.iter()
.zip(b.values())
.map(|(&x, &y)| {
let d = x as f64 - y as f64;
d * d
})
.sum::<f64>()
.sqrt()
}
pub(super) fn elementwise_mean(seqs: &[&FrameSeq]) -> FrameSeq {
let n = seqs.len() as f64;
let len = seqs[0].values().len();
let mean: Vec<f32> = (0..len)
.map(|j| (seqs.iter().map(|t| t.values()[j] as f64).sum::<f64>() / n) as f32)
.collect();
FrameSeq::new(seqs[0].frames(), seqs[0].dim(), mean).expect("mean preserves the common shape")
}