1 /++ 2 This module contains algorithms for the $(LINK2 https://en.wikipedia.org/wiki/Rayleigh_distribution, Rayleigh Distribution). 3 4 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0) 5 6 Authors: John Michael Hall 7 8 Copyright: 2023 Mir Stat Authors. 9 10 +/ 11 12 module mir.stat.distribution.rayleigh; 13 14 import mir.internal.utility: isFloatingPoint; 15 16 /++ 17 Computes the Rayleigh probability density function (PDF). 18 19 Params: 20 x = value to evaluate PDF 21 22 See_also: 23 $(LINK2 https://en.wikipedia.org/wiki/Rayleigh_distribution, Rayleigh Distribution) 24 +/ 25 @safe pure nothrow @nogc 26 T rayleighPDF(T)(const T x) 27 if (isFloatingPoint!T) 28 { 29 import mir.math.common: exp; 30 31 return x * exp(-0.5 * x * x); 32 } 33 34 /++ 35 Ditto, with scale parameter. 36 37 Params: 38 x = value to evaluate PDF 39 scale = scale parameter 40 +/ 41 @safe pure nothrow @nogc 42 T rayleighPDF(T)(const T x, const T scale) 43 if (isFloatingPoint!T) 44 in (scale > 0, "scale must be greater than zero") 45 { 46 import mir.math.common: exp; 47 48 const T scale2 = scale * scale; 49 return x / scale2 * exp(-0.5 * x * x / scale2); 50 } 51 52 /// 53 version(mir_stat_test) 54 @safe pure nothrow @nogc 55 unittest 56 { 57 import mir.test: shouldApprox; 58 59 0.0.rayleighPDF.shouldApprox == 0.0; 60 0.5.rayleighPDF.shouldApprox == 0.4412485; 61 1.0.rayleighPDF.shouldApprox == 0.6065307; 62 2.0.rayleighPDF.shouldApprox == 0.2706706; 63 64 // Can also provide scale parameter 65 0.5.rayleighPDF(2.0).shouldApprox == 0.1211541; 66 1.0.rayleighPDF(2.0).shouldApprox == 0.2206242; 67 4.0.rayleighPDF(2.0).shouldApprox == 0.1353353; 68 } 69 70 /++ 71 Computes the Rayleigh cumulative distribution function (CDF). 72 73 Params: 74 x = value to evaluate CDF 75 76 See_also: 77 $(LINK2 https://en.wikipedia.org/wiki/Rayleigh_distribution, Rayleigh Distribution) 78 +/ 79 @safe pure nothrow @nogc 80 T rayleighCDF(T)(const T x) 81 if (isFloatingPoint!T) 82 { 83 import mir.math.common: exp; 84 85 return 1 - exp(-0.5 * x * x); 86 } 87 88 /++ 89 Ditto, with scale parameter. 90 91 Params: 92 x = value to evaluate CDF 93 scale = scale parameter 94 +/ 95 T rayleighCDF(T)(const T x, const T scale) 96 if (isFloatingPoint!T) 97 in (scale > 0, "scale must be greater than zero") 98 { 99 return rayleighCDF(x / scale); 100 } 101 102 /// 103 version(mir_stat_test) 104 @safe pure nothrow @nogc 105 unittest 106 { 107 import mir.test: shouldApprox; 108 109 0.0.rayleighCDF.shouldApprox == 0.0; 110 0.5.rayleighCDF.shouldApprox == 0.1175031; 111 1.0.rayleighCDF.shouldApprox == 0.3934693; 112 2.0.rayleighCDF.shouldApprox == 0.8646647; 113 114 // Can also provide scale parameter 115 0.5.rayleighCDF(2.0).shouldApprox == 0.03076677; 116 1.0.rayleighCDF(2.0).shouldApprox == 0.1175031; 117 4.0.rayleighCDF(2.0).shouldApprox == 0.8646647; 118 } 119 120 /++ 121 Computes the Rayleigh complementary cumulative distribution function (CCDF). 122 123 Params: 124 x = value to evaluate CCDF 125 126 See_also: 127 $(LINK2 https://en.wikipedia.org/wiki/Rayleigh_distribution, Rayleigh Distribution) 128 +/ 129 @safe pure nothrow @nogc 130 T rayleighCCDF(T)(const T x) 131 if (isFloatingPoint!T) 132 { 133 import mir.math.common: exp; 134 135 return exp(-0.5 * x * x); 136 } 137 138 /++ 139 Ditto, with scale parameter. 140 141 Params: 142 x = value to evaluate CCDF 143 scale = scale parameter 144 +/ 145 @safe pure nothrow @nogc 146 T rayleighCCDF(T)(const T x, const T scale) 147 if (isFloatingPoint!T) 148 in (scale > 0, "scale must be greater than zero") 149 { 150 return rayleighCCDF(x / scale); 151 } 152 153 /// 154 version(mir_stat_test) 155 @safe pure nothrow @nogc 156 unittest 157 { 158 import mir.test: shouldApprox; 159 160 0.0.rayleighCCDF.shouldApprox == 1.0; 161 0.5.rayleighCCDF.shouldApprox == 0.8824969; 162 1.0.rayleighCCDF.shouldApprox == 0.6065307; 163 2.0.rayleighCCDF.shouldApprox == 0.1353353; 164 165 // Can also provide scale parameter 166 0.5.rayleighCCDF(2.0).shouldApprox == 0.9692332; 167 1.0.rayleighCCDF(2.0).shouldApprox == 0.8824969; 168 4.0.rayleighCCDF(2.0).shouldApprox == 0.1353353; 169 } 170 171 /++ 172 Computes the Rayleigh inverse cumulative distribution function (InvCDF). 173 174 Params: 175 p = value to evaluate InvCDF 176 177 See_also: 178 $(LINK2 https://en.wikipedia.org/wiki/Rayleigh_distribution, Rayleigh Distribution) 179 +/ 180 @safe pure nothrow @nogc 181 T rayleighInvCDF(T)(const T p) 182 if (isFloatingPoint!T) 183 in (p >= 0, "p must be greater than or equal to 0") 184 in (p <= 1, "p must be less than or equal to 1") 185 { 186 import mir.math.common: log, sqrt; 187 188 return sqrt(-2 * log(1 - p)); 189 } 190 191 /++ 192 Ditto, with scale parameter. 193 194 Params: 195 p = value to evaluate InvCDF 196 scale = scale parameter 197 198 See_also: 199 $(LINK2 https://en.wikipedia.org/wiki/Logistic_distribution, Logistic probability distribution) 200 +/ 201 @safe pure nothrow @nogc 202 T rayleighInvCDF(T)(const T p, const T scale) 203 if (isFloatingPoint!T) 204 in (p >= 0, "p must be greater than or equal to 0") 205 in (p <= 1, "p must be less than or equal to 1") 206 in (scale > 0, "scale must be greater than zero") 207 { 208 return scale * rayleighInvCDF(p); 209 } 210 211 /// 212 version(mir_stat_test) 213 @safe pure nothrow @nogc 214 unittest { 215 import mir.test: shouldApprox; 216 217 rayleighInvCDF(0.0).shouldApprox == 0.0; 218 rayleighInvCDF(0.25).shouldApprox == 0.7585276; 219 rayleighInvCDF(0.5).shouldApprox == 1.17741; 220 rayleighInvCDF(0.75).shouldApprox == 1.665109; 221 rayleighInvCDF(1.0).shouldApprox == double.infinity; 222 223 // Can also provide scale parameter 224 rayleighInvCDF(0.2, 2).shouldApprox == 1.336094; 225 rayleighInvCDF(0.4, 2).shouldApprox == 2.021535; 226 rayleighInvCDF(0.6, 2).shouldApprox == 2.707457; 227 rayleighInvCDF(0.8, 2).shouldApprox == 3.588245; 228 } 229 230 /++ 231 Computes the Rayleigh log probability density function (LPDF). 232 233 Params: 234 x = value to evaluate LPDF 235 236 See_also: 237 $(LINK2 https://en.wikipedia.org/wiki/Rayleigh_distribution, Rayleigh Distribution) 238 +/ 239 @safe pure nothrow @nogc 240 T rayleighLPDF(T)(const T x) 241 if (isFloatingPoint!T) 242 { 243 import mir.math.common: log; 244 245 return log(x) - 0.5 * x * x; 246 } 247 248 /++ 249 Ditto, with scale parameter. 250 251 Params: 252 x = value to evaluate LPDF 253 scale = scale parameter 254 255 See_also: 256 $(LINK2 https://en.wikipedia.org/wiki/Rayleigh_distribution, Rayleigh Distribution) 257 +/ 258 @safe pure nothrow @nogc 259 T rayleighLPDF(T)(const T x, const T scale) 260 if (isFloatingPoint!T) 261 in (scale > 0, "shape must be greater than zero") 262 { 263 import mir.math.common: log; 264 265 return log(x) - 2 * log(scale) - 0.5 * x * x / (scale * scale); 266 } 267 268 /// 269 version(mir_stat_test) 270 @safe pure nothrow @nogc 271 unittest 272 { 273 import mir.math.common: log; 274 import mir.test: shouldApprox; 275 276 0.0.rayleighLPDF.shouldApprox == -double.infinity; 277 0.5.rayleighLPDF.shouldApprox == log(0.4412485); 278 1.0.rayleighLPDF.shouldApprox == log(0.6065307); 279 2.0.rayleighLPDF.shouldApprox == log(0.2706706); 280 281 // Can also provide scale parameter 282 0.5.rayleighLPDF(2.0).shouldApprox == log(0.1211541); 283 1.0.rayleighLPDF(2.0).shouldApprox == log(0.2206242); 284 4.0.rayleighLPDF(2.0).shouldApprox == log(0.1353353); 285 }