use crate::mesh::MeshStructure;
use crate::objective::Goal;
use crate::state::EquilibriumState;
use crate::structure::Structure;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum ErrorKind {
#[default]
Squared,
MeanSquared,
RootMeanSquared,
Absolute,
Prediction,
LogMax,
}
#[derive(Clone, Debug)]
pub struct Loss {
pub goals: Vec<Goal>,
pub error: ErrorKind,
}
impl Loss {
pub fn new(goals: Vec<Goal>) -> Self {
Self {
goals,
error: ErrorKind::default(),
}
}
pub fn with_error(mut self, error: ErrorKind) -> Self {
self.error = error;
self
}
pub fn scalar(
&self,
state: &EquilibriumState,
structure: &Structure,
is_support: &[bool],
mesh: Option<&MeshStructure>,
) -> f64 {
let n = self.goals.len().max(1);
let mut sum = 0.0;
for g in &self.goals {
let pred = g.prediction_with_structure(state, structure, is_support, mesh);
let target = g.target();
let w = g.weight();
sum += term(self.error, pred, target, w);
}
match self.error {
ErrorKind::MeanSquared | ErrorKind::RootMeanSquared => sum / n as f64,
_ => sum,
}
}
pub fn root_mean_squared(goals: Vec<Goal>) -> Self {
Self::new(goals).with_error(ErrorKind::RootMeanSquared)
}
}
fn term(error: ErrorKind, pred: f64, target: f64, weight: f64) -> f64 {
match error {
ErrorKind::Squared | ErrorKind::MeanSquared | ErrorKind::RootMeanSquared => {
weight * (pred - target).powi(2)
}
ErrorKind::Absolute => weight * (pred - target).abs(),
ErrorKind::Prediction => weight * pred,
ErrorKind::LogMax => {
let v = (pred - target).max(0.0);
weight * (1.0 + v).ln()
}
}
}
pub fn losses_total(
losses: &[Loss],
state: &EquilibriumState,
structure: &Structure,
is_support: &[bool],
mesh: Option<&MeshStructure>,
) -> f64 {
losses
.iter()
.map(|l| l.scalar(state, structure, is_support, mesh))
.sum()
}