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 fn transform(_: &Self::State, y: f64) -> f64 {
20 y.ln()
21 }
22
23 fn inverse(_: &Self::State, value: f64) -> f64 {
24 value.exp()
25 }
26
27 fn transform_slice(state: &Self::State, y: &[f64]) -> Result<Vec<f64>, TransformError> {
28 let mut out = vec![0.0; y.len()];
29 Self::transform_into(state, y, &mut out)?;
30 Ok(out)
31 }
32
33 fn transform_into(
34 state: &Self::State,
35 y: &[f64],
36 out: &mut [f64],
37 ) -> Result<(), TransformError> {
38 map_slice_into(y, out, validate_positive, |value| {
39 Self::transform(state, value)
40 })
41 }
42}
43
44#[cfg(test)]
45mod tests {
46 use approx::assert_relative_eq;
47
48 use crate::{Log, TargetTransform, TransformError};
49
50 #[test]
51 fn round_trips_positive_values() {
52 let y = [1.0, 2.5];
53 let (state, transformed) = Log::fit_transform(&y).unwrap();
54 let restored = Log::inverse_slice(&state, &transformed).unwrap();
55
56 for (actual, expected) in restored.iter().zip(y) {
57 assert_relative_eq!(*actual, expected);
58 }
59 }
60
61 #[test]
62 fn rejects_invalid_domain() {
63 assert_eq!(
64 Log::fit(&[1.0, 0.0]).unwrap_err(),
65 TransformError::NonPositiveValue
66 );
67 assert_eq!(
68 Log::fit(&[f64::INFINITY]).unwrap_err(),
69 TransformError::NonFiniteValue
70 );
71 }
72}