Skip to main content

gamlss_transform/transforms/
log.rs

1use crate::{TargetTransform, TransformError, map_slice_into, validate_positive};
2
3/// Log transform for strictly positive targets.
4#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
5pub struct Log;
6
7/// State for [`Log`].
8#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
9pub struct LogState;
10
11impl TargetTransform for Log {
12    type State = LogState;
13
14    fn fit(y: &[f64]) -> Result<Self::State, TransformError> {
15        validate_positive(y)?;
16        Ok(LogState)
17    }
18
19    #[inline(always)]
20    fn transform(_: &Self::State, y: f64) -> f64 {
21        y.ln()
22    }
23
24    #[inline(always)]
25    fn inverse(_: &Self::State, value: f64) -> f64 {
26        value.exp()
27    }
28
29    #[inline]
30    fn transform_slice(state: &Self::State, y: &[f64]) -> Result<Vec<f64>, TransformError> {
31        let mut out = vec![0.0; y.len()];
32        Self::transform_into(state, y, &mut out)?;
33        Ok(out)
34    }
35
36    #[inline]
37    fn transform_into(
38        state: &Self::State,
39        y: &[f64],
40        out: &mut [f64],
41    ) -> Result<(), TransformError> {
42        map_slice_into(y, out, validate_positive, |value| {
43            Self::transform(state, value)
44        })
45    }
46}
47
48#[cfg(test)]
49mod tests {
50    use approx::assert_relative_eq;
51
52    use crate::{Log, TargetTransform, TransformError};
53
54    #[test]
55    fn round_trips_positive_values() {
56        let y = [1.0, 2.5];
57        let (state, transformed) = Log::fit_transform(&y).unwrap();
58        let restored = Log::inverse_slice(&state, &transformed).unwrap();
59
60        for (actual, expected) in restored.iter().zip(y) {
61            assert_relative_eq!(*actual, expected);
62        }
63    }
64
65    #[test]
66    fn rejects_invalid_domain() {
67        assert_eq!(
68            Log::fit(&[1.0, 0.0]).unwrap_err(),
69            TransformError::NonPositiveValue
70        );
71        assert_eq!(
72            Log::fit(&[f64::INFINITY]).unwrap_err(),
73            TransformError::NonFiniteValue
74        );
75    }
76}