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}