use crate::float::Float;
use crate::value::Value01;
use core::marker::PhantomData;
use witnessed::Witnessed;
pub fn metric(name: &'static str) -> MetricName {
MetricName { name }
}
pub struct MetricName {
name: &'static str,
}
impl MetricName {
pub fn measure(self) -> MeasureStage {
MeasureStage { name: self.name }
}
}
pub struct MeasureStage {
name: &'static str,
}
impl MeasureStage {
pub fn by<I, Raw, M>(self, measure: M) -> MeasureSet<I, Raw, M> {
MeasureSet {
name: self.name,
measure,
_phantom: PhantomData,
}
}
}
pub struct MeasureSet<I, Raw, M> {
name: &'static str,
measure: M,
_phantom: PhantomData<(I, Raw)>,
}
impl<I, Raw, M> MeasureSet<I, Raw, M> {
pub fn map01(self) -> Map01Stage<I, Raw, M> {
Map01Stage {
name: self.name,
measure: self.measure,
_phantom: PhantomData,
}
}
}
pub struct Map01Stage<I, Raw, M> {
name: &'static str,
measure: M,
_phantom: PhantomData<(I, Raw)>,
}
impl<I, Raw, M> Map01Stage<I, Raw, M> {
pub fn by<T: Float, F>(self, map01: F) -> Metric<T, I, Raw, M, F> {
Metric {
name: self.name,
measure: self.measure,
map01,
_phantom: PhantomData,
}
}
}
impl<I, T: Float, M> Map01Stage<I, T, M> {
pub fn identity(self) -> Metric<T, I, T, M, impl Fn(&T, &I) -> Witnessed<T, Value01>> {
self.by(|raw: &T, _: &I| Value01::witness(raw.min(T::one()).max(T::zero())).unwrap())
}
pub fn linear(self, max: T) -> Metric<T, I, T, M, impl Fn(&T, &I) -> Witnessed<T, Value01>> {
self.by(move |raw: &T, _: &I| {
Value01::witness((*raw / max).min(T::one()).max(T::zero())).unwrap()
})
}
pub fn sigmoid(
self,
low: T,
high: T,
) -> Metric<T, I, T, M, impl Fn(&T, &I) -> Witnessed<T, Value01>> {
let two = T::from_f64(2.0);
let eps = T::from_f64(10.0) * T::epsilon();
let x0 = (low + high) / two;
let k = two * (T::one() / eps - T::one()).ln() / (high - low);
self.by(move |raw: &T, _: &I| {
let v = T::one() / (T::one() + (-k * (*raw - x0)).exp());
Value01::witness(v).unwrap()
})
}
pub fn cauchy(
self,
left: T,
right: T,
) -> Metric<T, I, T, M, impl Fn(&T, &I) -> Witnessed<T, Value01>> {
self.by(move |raw: &T, _: &I| {
let h = if *raw < T::zero() { left } else { right };
let v = T::one() / (T::one() + (*raw / h) * (*raw / h));
Value01::witness(v).unwrap()
})
}
}
pub struct Metric<T, I, Raw, M, F> {
pub(crate) name: &'static str,
pub(crate) measure: M,
pub(crate) map01: F,
_phantom: PhantomData<(T, I, Raw)>,
}
impl<T: Float, I, Raw, M, F> Metric<T, I, Raw, M, F>
where
M: Fn(&I) -> Raw,
F: Fn(&Raw, &I) -> Witnessed<T, Value01>,
{
#[inline]
pub fn eval(&self, input: &I) -> Witnessed<T, Value01> {
let raw = (self.measure)(input);
(self.map01)(&raw, input)
}
#[inline]
pub fn name(&self) -> &str {
self.name
}
}
impl<T: Float, I: 'static, Raw: 'static, M: 'static, F: 'static> Metric<T, I, Raw, M, F>
where
M: Fn(&I) -> Raw,
F: Fn(&Raw, &I) -> Witnessed<T, Value01>,
{
#[inline]
pub fn boxed(self) -> Box<dyn crate::Scorable<T, I>> {
Box::new(self)
}
}
impl<T, I, Raw, M: Clone, F: Clone> Clone for Metric<T, I, Raw, M, F> {
fn clone(&self) -> Self {
Self {
name: self.name,
measure: self.measure.clone(),
map01: self.map01.clone(),
_phantom: PhantomData,
}
}
}
#[cfg(test)]
mod tests_for_metric;