use crate::core::math::Scalar;
use std::sync::Arc;
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[non_exhaustive]
pub enum ViolationMeasure {
L1,
L2,
SquaredL2,
LInf,
Custom(String),
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[non_exhaustive]
pub enum ViolationScaling {
Unscaled,
Custom(String),
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[non_exhaustive]
pub enum ViolationTransformation {
Identity,
PrimaModeration,
NonFiniteToInfinity,
Custom(String),
}
#[derive(Clone, PartialEq)]
#[non_exhaustive]
pub struct ViolationReport<F: Scalar = f64> {
pub value: F,
metadata: MetadataRef,
}
impl<F: Scalar> std::fmt::Debug for ViolationReport<F> {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter
.debug_struct("ViolationReport")
.field("value", &self.value)
.field("measure", self.measure())
.field("scaling", self.scaling())
.field("transformation", self.transformation())
.finish()
}
}
#[derive(Clone, Debug, PartialEq)]
struct Metadata {
measure: ViolationMeasure,
scaling: ViolationScaling,
transformation: ViolationTransformation,
}
impl Metadata {
const fn native(
measure: ViolationMeasure,
transformation: ViolationTransformation,
) -> Self {
Self {
measure,
scaling: ViolationScaling::Unscaled,
transformation,
}
}
}
static NATIVE_METADATA: [[Metadata; 3]; 4] = [
[
Metadata::native(
ViolationMeasure::L1,
ViolationTransformation::Identity,
),
Metadata::native(
ViolationMeasure::L1,
ViolationTransformation::PrimaModeration,
),
Metadata::native(
ViolationMeasure::L1,
ViolationTransformation::NonFiniteToInfinity,
),
],
[
Metadata::native(
ViolationMeasure::L2,
ViolationTransformation::Identity,
),
Metadata::native(
ViolationMeasure::L2,
ViolationTransformation::PrimaModeration,
),
Metadata::native(
ViolationMeasure::L2,
ViolationTransformation::NonFiniteToInfinity,
),
],
[
Metadata::native(
ViolationMeasure::SquaredL2,
ViolationTransformation::Identity,
),
Metadata::native(
ViolationMeasure::SquaredL2,
ViolationTransformation::PrimaModeration,
),
Metadata::native(
ViolationMeasure::SquaredL2,
ViolationTransformation::NonFiniteToInfinity,
),
],
[
Metadata::native(
ViolationMeasure::LInf,
ViolationTransformation::Identity,
),
Metadata::native(
ViolationMeasure::LInf,
ViolationTransformation::PrimaModeration,
),
Metadata::native(
ViolationMeasure::LInf,
ViolationTransformation::NonFiniteToInfinity,
),
],
];
#[derive(Clone, Debug, PartialEq)]
enum MetadataRef {
Native { measure: u8, transformation: u8 },
Shared(Arc<Metadata>),
}
impl MetadataRef {
#[inline]
fn native(metadata: &Metadata) -> Option<Self> {
if !matches!(metadata.scaling, ViolationScaling::Unscaled) {
return None;
}
let measure = match metadata.measure {
ViolationMeasure::L1 => 0,
ViolationMeasure::L2 => 1,
ViolationMeasure::SquaredL2 => 2,
ViolationMeasure::LInf => 3,
ViolationMeasure::Custom(_) => return None,
};
let transformation = match metadata.transformation {
ViolationTransformation::Identity => 0,
ViolationTransformation::PrimaModeration => 1,
ViolationTransformation::NonFiniteToInfinity => 2,
ViolationTransformation::Custom(_) => return None,
};
Some(Self::Native {
measure,
transformation,
})
}
#[inline]
fn new(metadata: Metadata) -> Self {
match Self::native(&metadata) {
Some(native) => native,
None => Self::Shared(Arc::new(metadata)),
}
}
#[inline]
fn get(&self) -> &Metadata {
match self {
Self::Native {
measure,
transformation,
} => &NATIVE_METADATA[*measure as usize][*transformation as usize],
Self::Shared(metadata) => metadata,
}
}
#[cold]
fn update(self, update: impl FnOnce(&mut Metadata)) -> Self {
match self {
native @ Self::Native { .. } => {
let mut metadata = native.get().clone();
update(&mut metadata);
Self::new(metadata)
}
Self::Shared(mut metadata) => {
update(Arc::make_mut(&mut metadata));
match Self::native(&metadata) {
Some(native) => native,
None => Self::Shared(metadata),
}
}
}
}
}
impl<F: Scalar> ViolationReport<F> {
pub(crate) const fn unscaled_l1_nonfinite(value: F) -> Self {
Self {
value,
metadata: MetadataRef::Native {
measure: 0,
transformation: 2,
},
}
}
#[inline]
pub fn new(value: F, measure: ViolationMeasure) -> Self {
let measure = match measure {
ViolationMeasure::L1 => 0,
ViolationMeasure::L2 => 1,
ViolationMeasure::SquaredL2 => 2,
ViolationMeasure::LInf => 3,
ViolationMeasure::Custom(_) => {
return Self {
value,
metadata: MetadataRef::new(Metadata::native(
measure,
ViolationTransformation::Identity,
)),
};
}
};
Self {
value,
metadata: MetadataRef::Native {
measure,
transformation: 0,
},
}
}
#[inline]
pub fn measure(&self) -> &ViolationMeasure {
&self.metadata.get().measure
}
#[inline]
pub fn scaling(&self) -> &ViolationScaling {
&self.metadata.get().scaling
}
#[inline]
pub fn transformation(&self) -> &ViolationTransformation {
&self.metadata.get().transformation
}
#[inline]
pub fn with_measure(mut self, measure: ViolationMeasure) -> Self {
if let MetadataRef::Native {
measure: native, ..
} = &mut self.metadata
{
match measure {
ViolationMeasure::L1 => *native = 0,
ViolationMeasure::L2 => *native = 1,
ViolationMeasure::SquaredL2 => *native = 2,
ViolationMeasure::LInf => *native = 3,
ViolationMeasure::Custom(_) => {
self.metadata = self
.metadata
.update(|metadata| metadata.measure = measure);
}
}
return self;
}
self.metadata =
self.metadata.update(|metadata| metadata.measure = measure);
self
}
#[inline]
pub fn with_scaling(mut self, scaling: ViolationScaling) -> Self {
if matches!(self.metadata, MetadataRef::Native { .. })
&& matches!(scaling, ViolationScaling::Unscaled)
{
return self;
}
self.metadata =
self.metadata.update(|metadata| metadata.scaling = scaling);
self
}
#[inline]
pub fn with_transformation(
mut self,
transformation: ViolationTransformation,
) -> Self {
if let MetadataRef::Native {
transformation: native,
..
} = &mut self.metadata
{
match transformation {
ViolationTransformation::Identity => *native = 0,
ViolationTransformation::PrimaModeration => *native = 1,
ViolationTransformation::NonFiniteToInfinity => *native = 2,
ViolationTransformation::Custom(_) => {
self.metadata = self.metadata.update(|metadata| {
metadata.transformation = transformation
});
}
}
return self;
}
self.metadata = self
.metadata
.update(|metadata| metadata.transformation = transformation);
self
}
}
#[cfg(feature = "serde")]
impl<F: Scalar + serde::Serialize> serde::Serialize for ViolationReport<F> {
fn serialize<S: serde::Serializer>(
&self,
serializer: S,
) -> Result<S::Ok, S::Error> {
use serde::ser::SerializeStruct;
let mut record = serializer.serialize_struct("ViolationReport", 4)?;
record.serialize_field("value", &self.value)?;
record.serialize_field("measure", self.measure())?;
record.serialize_field("scaling", self.scaling())?;
record.serialize_field("transformation", self.transformation())?;
record.end()
}
}
#[cfg(feature = "serde")]
impl<'de, F: Scalar + serde::Deserialize<'de>> serde::Deserialize<'de>
for ViolationReport<F>
{
fn deserialize<D: serde::Deserializer<'de>>(
deserializer: D,
) -> Result<Self, D::Error> {
#[derive(serde::Deserialize)]
#[serde(rename = "ViolationReport")]
struct Stored<F> {
value: F,
measure: ViolationMeasure,
scaling: ViolationScaling,
transformation: ViolationTransformation,
}
let stored = Stored::<F>::deserialize(deserializer)?;
Ok(Self {
value: stored.value,
metadata: MetadataRef::new(Metadata {
measure: stored.measure,
scaling: stored.scaling,
transformation: stored.transformation,
}),
})
}
}