rand_simple/distributions/
inverse_gaussian.rs

1use crate::create_state;
2use crate::standard_distributions::{standard_normal, xorshift160_0_to_1};
3
4/// Represents an Inverse Gaussian (IG) distribution.
5///
6/// # Example Usage
7/// ```
8/// // Create a new InverseGaussian instance with a seed
9/// let mut inverse_gaussian = rand_simple::InverseGaussian::new([1192u32, 765u32, 1543u32]);
10/// assert_eq!(format!("{inverse_gaussian}"), "IG(Mean, Shape) = IG(1, 1)");
11/// println!(
12///     "Generate a random number from the standard Inverse Gaussian distribution with mean μ = 1 and shape parameter λ = 1 -> {}",
13///     inverse_gaussian.sample()
14/// );
15///
16/// // Modify the distribution parameters
17/// let mean: f64 = 1.5f64;
18/// let shape: f64 = 2f64;
19/// let result: Result<(f64, f64), &str> = inverse_gaussian.try_set_params(mean, shape);
20/// assert_eq!(format!("{inverse_gaussian}"), "IG(Mean, Shape) = IG(1.5, 2)");
21/// println!(
22///     "Generate a random number from the Inverse Gaussian distribution with mean μ = {}, shape λ = {} -> {}",
23///     mean, shape, inverse_gaussian.sample()
24/// );
25/// ```
26pub struct InverseGaussian {
27    xyzuv_u: [u32; 5],    // 状態変数
28    xyzuv_hn_0: [u32; 5], // 状態変数
29    xyzuv_hn_1: [u32; 5], // 状態変数
30    mean: f64,            // 平均
31    shape: f64,           // 形状母数
32}
33
34impl InverseGaussian {
35    /// Constructs a new `InverseGaussian` instance.
36    ///
37    /// # Parameters
38    /// - `seeds`: An array of three `u32` values used to initialize the random number generator states.
39    ///   To ensure good randomness and avoid duplicate seeds, the constructor internally adjusts these values.
40    ///
41    /// # Notes
42    /// - The `mean` parameter is initialized to `1.0`.
43    /// - The `shape` parameter is initialized to `1.0`.
44    /// - Internal state variables (`xyzuv_u`, `xyzuv_hn_0`, `xyzuv_hn_1`) are derived from the adjusted seeds.
45    pub fn new(seeds: [u32; 3]) -> Self {
46        let adjusted_seeds = crate::adjust_seeds!(seeds);
47        Self {
48            xyzuv_u: create_state(adjusted_seeds[0]),
49            xyzuv_hn_0: create_state(adjusted_seeds[1]),
50            xyzuv_hn_1: create_state(adjusted_seeds[2]),
51            mean: 1_f64,
52            shape: 1_f64,
53        }
54    }
55
56    /// Generates a random number following the inverse Gaussian distribution.
57    ///
58    /// # Algorithm
59    /// Implements Algorithm 3.94 for generating samples from the inverse Gaussian distribution.
60    ///
61    /// ## Steps
62    /// 1. **Preprocessing**: Calculate intermediate values `p` and `q` based on the distribution's parameters.
63    /// 2. **Generate a standard normal random variable** `z`. If `z` is zero, return the mean as the result.
64    /// 3. **Compute candidate value** `x_1` based on the adjusted mean and shape parameters.
65    /// 4. **Acceptance-Rejection Step**:
66    ///    - Generate a uniform random variable `u`.
67    ///    - Depending on the relationship between `u`, `x_1`, and the mean, decide whether to accept `x_1` or use an alternative value.
68    /// 5. **Return the final value** as the sample.
69    pub fn sample(&mut self) -> f64 {
70        // アルゴリズム 3.94
71        // 前処理
72        let p = self.mean.powi(2);
73        let q = p / (2_f64 * self.shape);
74
75        // step 1
76        let z = standard_normal(&mut self.xyzuv_hn_0, &mut self.xyzuv_hn_1).abs();
77        if z == 0_f64 {
78            // step 1 -> step 5
79            self.mean
80        } else {
81            // step 2
82            let v = self.mean + q * z.powi(2);
83            let x_1 = v + (v.powi(2) - p).sqrt();
84
85            // step 3
86            let u = xorshift160_0_to_1(&mut self.xyzuv_u);
87            if u * (x_1 + self.mean) <= self.mean {
88                // step 3 -> step 5
89                x_1
90            } else {
91                // step 4 -> step 5
92                p / x_1
93            }
94        }
95    }
96
97    /// Updates the parameters of the inverse Gaussian random variable.
98    ///
99    /// # Parameters
100    /// - `mean`: The mean parameter (μ) of the distribution. Must be positive.
101    /// - `shape`: The shape parameter (λ) of the distribution. Must be positive.
102    ///
103    /// # Returns
104    /// - `Ok((mean, shape))`: If both parameters are valid, the new values are set, and they are returned as a tuple.
105    /// - `Err(&str)`: If either parameter is invalid, an error message is returned, and the previous parameter values are retained.
106    ///
107    /// # Validations
108    /// - `mean > 0`: Ensures the mean is strictly positive. If this condition is violated, an error is returned.
109    /// - `shape > 0`: Ensures the shape parameter is strictly positive. If this condition is violated, an error is returned.
110    ///
111    /// # Example
112    /// ```rust
113    /// let mut inverse_gaussian = rand_simple::InverseGaussian::new([1192u32, 765u32, 1543u32]);
114    /// assert_eq!(format!("{inverse_gaussian}"), "IG(Mean, Shape) = IG(1, 1)");
115    ///
116    /// // Attempt to update with valid parameters.
117    /// let result = inverse_gaussian.try_set_params(1.5, 2.0);
118    /// assert!(result.is_ok());
119    /// assert_eq!(format!("{inverse_gaussian}"), "IG(Mean, Shape) = IG(1.5, 2)");
120    ///
121    /// // Attempt to update with an invalid mean.
122    /// let result = inverse_gaussian.try_set_params(-1.0, 2.0);
123    /// assert!(result.is_err());
124    /// println!("{}", result.unwrap_err()); // Output: "平均が0以下です..."
125    /// ```
126    pub fn try_set_params(&mut self, mean: f64, shape: f64) -> Result<(f64, f64), &str> {
127        if mean <= 0_f64 {
128            Err("Mean is less than or equal to 0. The previous parameter values are retained.")
129        } else if shape <= 0_f64 {
130            Err("Shape parameter is less than or equal to 0. The previous parameter values are retained.")
131        } else {
132            self.mean = mean;
133            self.shape = shape;
134            core::result::Result::Ok((mean, shape))
135        }
136    }
137}
138
139impl core::fmt::Display for InverseGaussian {
140    /// Formatter for displaying in functions like println! macro
141    /// * Mean
142    /// * Standard deviation
143    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
144        write!(f, "IG(Mean, Shape) = IG({}, {})", self.mean, self.shape)?;
145        Ok(())
146    }
147}