use crate::error::{NeuralError, Result};
use crate::losses::Loss;
use scirs2_core::ndarray::{Array, Zip};
use scirs2_core::numeric::{Float, NumAssign};
use std::fmt::Debug;
#[derive(Debug, Clone, Copy)]
pub struct MeanSquaredError;
impl MeanSquaredError {
pub fn new() -> Self {
Self
}
}
impl Default for MeanSquaredError {
fn default() -> Self {
Self::new()
}
}
impl<F: Float + Debug + NumAssign> Loss<F> for MeanSquaredError {
fn forward(
&self,
predictions: &Array<F, scirs2_core::ndarray::IxDyn>,
targets: &Array<F, scirs2_core::ndarray::IxDyn>,
) -> Result<F> {
if predictions.shape() != targets.shape() {
return Err(NeuralError::InferenceError(format!(
"Shape mismatch in MSE: predictions {:?} vs targets {:?}",
predictions.shape(),
targets.shape()
)));
}
let n = F::from(predictions.len()).ok_or_else(|| {
NeuralError::InferenceError("Could not convert array length to float".to_string())
})?;
let mut sum_squared_diff = F::zero();
Zip::from(predictions).and(targets).for_each(|&p, &t| {
let diff = p - t;
sum_squared_diff += diff * diff;
});
let mse = sum_squared_diff / n;
Ok(mse)
}
fn backward(
&self,
predictions: &Array<F, scirs2_core::ndarray::IxDyn>,
targets: &Array<F, scirs2_core::ndarray::IxDyn>,
) -> Result<Array<F, scirs2_core::ndarray::IxDyn>> {
if predictions.shape() != targets.shape() {
return Err(NeuralError::InferenceError(format!(
"Shape mismatch in MSE gradient: predictions {:?} vs targets {:?}",
predictions.shape(),
targets.shape()
)));
}
let n = F::from(predictions.len()).ok_or_else(|| {
NeuralError::InferenceError("Could not convert array length to float".to_string())
})?;
let two = F::one() + F::one();
let mut gradients = predictions.clone();
Zip::from(&mut gradients).and(targets).for_each(|grad, &t| {
*grad = two * (*grad - t) / n;
});
Ok(gradients)
}
}