Skip to main content

score_set/
metric.rs

1use crate::float::Float;
2use crate::value::Value01;
3use core::marker::PhantomData;
4use witnessed::Witnessed;
5
6// ---------------------------------------------------------------------------
7// Metric builder — measure().by() → map01().by() → build()
8// ---------------------------------------------------------------------------
9
10/// Entry point: `metric("name")`.
11pub fn metric(name: &'static str) -> MetricName {
12    MetricName { name }
13}
14
15/// First stage: a name has been given, waiting for `.measure()`.
16pub struct MetricName {
17    name: &'static str,
18}
19
20impl MetricName {
21    /// Enter the measure stage.
22    pub fn measure(self) -> MeasureStage {
23        MeasureStage { name: self.name }
24    }
25}
26
27/// Second stage: ready for `.by(measure_closure)`.
28pub struct MeasureStage {
29    name: &'static str,
30}
31
32impl MeasureStage {
33    /// Supply the measure closure `Fn(I) -> Raw`.
34    pub fn by<I, Raw, M>(self, measure: M) -> MeasureSet<I, Raw, M> {
35        MeasureSet {
36            name: self.name,
37            measure,
38            _phantom: PhantomData,
39        }
40    }
41}
42
43/// Third stage: measure closure is set, waiting for `.map01()`.
44pub struct MeasureSet<I, Raw, M> {
45    name: &'static str,
46    measure: M,
47    _phantom: PhantomData<(I, Raw)>,
48}
49
50impl<I, Raw, M> MeasureSet<I, Raw, M> {
51    /// Enter the map01 stage.
52    pub fn map01(self) -> Map01Stage<I, Raw, M> {
53        Map01Stage {
54            name: self.name,
55            measure: self.measure,
56            _phantom: PhantomData,
57        }
58    }
59}
60
61/// Fourth stage: ready for `.by(map01_closure)`.
62pub struct Map01Stage<I, Raw, M> {
63    name: &'static str,
64    measure: M,
65    _phantom: PhantomData<(I, Raw)>,
66}
67
68impl<I, Raw, M> Map01Stage<I, Raw, M> {
69    /// Supply the map01 closure `Fn(&Raw, I) -> Witnessed<T, Value01>`.
70    ///
71    /// The type `T: Float` is inferred from the return type of the closure.
72    pub fn by<T: Float, F>(self, map01: F) -> Metric<T, I, Raw, M, F> {
73        Metric {
74            name: self.name,
75            measure: self.measure,
76            map01,
77            _phantom: PhantomData,
78        }
79    }
80}
81
82// ---------------------------------------------------------------------------
83// Metric — the built scoring operator
84// ---------------------------------------------------------------------------
85
86/// A scoring operator built via the `measure().by() → map01().by()` pipeline.
87///
88/// Stores two closures:
89/// - `measure: Fn(I) -> Raw` — maps input to an intermediate raw value.
90/// - `map01: Fn(&Raw, I) -> Witnessed<T, Value01>` — maps the raw value back
91///   alongside the original input to a validated `[0, 1]` score.
92///
93/// `eval(input)` composes the two closures.
94pub struct Metric<T, I, Raw, M, F> {
95    pub(crate) name: &'static str,
96    pub(crate) measure: M,
97    pub(crate) map01: F,
98    _phantom: PhantomData<(T, I, Raw)>,
99}
100
101impl<T: Float, I, Raw, M, F> Metric<T, I, Raw, M, F>
102where
103    M: Fn(&I) -> Raw,
104    F: Fn(&Raw, &I) -> Witnessed<T, Value01>,
105{
106    /// Evaluate this metric against an input, producing a `[0, 1]` score.
107    #[inline]
108    pub fn eval(&self, input: &I) -> Witnessed<T, Value01> {
109        let raw = (self.measure)(input);
110        (self.map01)(&raw, input)
111    }
112
113    /// Return the metric's name.
114    #[inline]
115    pub fn name(&self) -> &str {
116        self.name
117    }
118}
119
120// Make Metric clone-able when closures are clone-able.
121impl<T, I, Raw, M: Clone, F: Clone> Clone for Metric<T, I, Raw, M, F> {
122    fn clone(&self) -> Self {
123        Self {
124            name: self.name,
125            measure: self.measure.clone(),
126            map01: self.map01.clone(),
127            _phantom: PhantomData,
128        }
129    }
130}
131
132#[cfg(test)]
133mod tests_for_metric;