gamlss_transform/transforms/
log.rs1use crate::{TargetTransform, TransformError, map_slice_into, validate_positive};
2
3#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
5pub struct Log;
6
7#[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}