1 /++ 2 This module contains algorithms for the $(LINK2 https://en.wikipedia.org/wiki/Laplace_distribution, Laplace 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.laplace; 13 14 import mir.internal.utility: isFloatingPoint; 15 16 /++ 17 Computes the Laplace probability density function (PDF). 18 19 Params: 20 x = value to evaluate PDF 21 22 See_also: 23 $(LINK2 https://en.wikipedia.org/wiki/Laplace_distribution, Laplace Distribution) 24 +/ 25 @safe pure nothrow @nogc 26 T laplacePDF(T)(const T x) 27 if (isFloatingPoint!T) 28 { 29 import mir.math.common: exp, fabs; 30 31 return 0.5 * exp(-fabs(x)); 32 } 33 34 /++ 35 Ditto, with location and scale parameters (by standardizing `x`). 36 37 Params: 38 x = value to evaluate PDF 39 location = location parameter 40 scale = scale parameter 41 +/ 42 @safe pure nothrow @nogc 43 T laplacePDF(T)(const T x, const T location, const T scale) 44 if (isFloatingPoint!T) 45 in (scale > 0, "scale must be greater than zero") 46 { 47 return laplacePDF((x - location) / scale) / scale; 48 } 49 50 /// 51 version(mir_stat_test) 52 @safe pure nothrow @nogc 53 unittest 54 { 55 import mir.test: shouldApprox; 56 57 laplacePDF(-2.0).shouldApprox == 0.06766764; 58 laplacePDF(-1.0).shouldApprox == 0.1839397; 59 laplacePDF(-0.5).shouldApprox == 0.3032653; 60 laplacePDF(0.0).shouldApprox == 0.5; 61 laplacePDF(0.5).shouldApprox == 0.3032653; 62 laplacePDF(1.0).shouldApprox == 0.1839397; 63 laplacePDF(2.0).shouldApprox == 0.06766764; 64 65 // Can also provide location/scale parameters 66 laplacePDF(-1.0, 2.0, 3.0).shouldApprox == 0.06131324; 67 laplacePDF(1.0, 2.0, 3.0).shouldApprox == 0.1194219; 68 laplacePDF(4.0, 2.0, 3.0).shouldApprox == 0.08556952; 69 } 70 71 /++ 72 Computes the Laplace cumulative distribution function (CDF). 73 74 Params: 75 x = value to evaluate CDF 76 77 See_also: 78 $(LINK2 https://en.wikipedia.org/wiki/Laplace_distribution, Laplace Distribution) 79 +/ 80 @safe pure nothrow @nogc 81 T laplaceCDF(T)(const T x) 82 if (isFloatingPoint!T) 83 { 84 import mir.math.common: exp; 85 86 if (x <= 0) { 87 return 0.5 * exp(x); 88 } else { 89 return 1 - 0.5 * exp(-x); 90 } 91 } 92 93 /++ 94 Ditto, with location and scale parameters (by standardizing `x`). 95 96 Params: 97 x = value to evaluate CDF 98 location = location parameter 99 scale = scale parameter 100 +/ 101 @safe pure nothrow @nogc 102 T laplaceCDF(T)(const T x, const T location, const T scale) 103 if (isFloatingPoint!T) 104 in (scale > 0, "scale must be greater than zero") 105 { 106 return laplaceCDF((x - location) / scale); 107 } 108 109 /// 110 version(mir_stat_test) 111 @safe pure nothrow @nogc 112 unittest 113 { 114 import mir.test: shouldApprox; 115 116 laplaceCDF(-2.0).shouldApprox == 0.06766764; 117 laplaceCDF(-1.0).shouldApprox == 0.1839397; 118 laplaceCDF(-0.5).shouldApprox == 0.3032653; 119 laplaceCDF(0.0).shouldApprox == 0.5; 120 laplaceCDF(0.5).shouldApprox == 0.6967347; 121 laplaceCDF(1.0).shouldApprox == 0.8160603; 122 laplaceCDF(2.0).shouldApprox == 0.9323324; 123 124 // Can also provide location/scale parameters 125 laplaceCDF(-1.0, 2.0, 3.0).shouldApprox == 0.1839397; 126 laplaceCDF(1.0, 2.0, 3.0).shouldApprox == 0.3582657; 127 laplaceCDF(4.0, 2.0, 3.0).shouldApprox == 0.7432914; 128 } 129 130 /++ 131 Computes the Laplace complementary cumulative distribution function (CCDF). 132 133 Params: 134 x = value to evaluate CCDF 135 136 See_also: 137 $(LINK2 https://en.wikipedia.org/wiki/Laplace_distribution, Laplace Distribution) 138 +/ 139 @safe pure nothrow @nogc 140 T laplaceCCDF(T)(const T x) 141 if (isFloatingPoint!T) 142 { 143 import mir.math.common: exp; 144 145 if (x <= 0) { 146 return 1 - 0.5 * exp(x); 147 } else { 148 return 0.5 * exp(-x); 149 } 150 } 151 152 /++ 153 Ditto, with location and scale parameters (by standardizing `x`). 154 155 Params: 156 x = value to evaluate CCDF 157 location = location parameter 158 scale = scale parameter 159 +/ 160 T laplaceCCDF(T)(const T x, const T location, const T scale) 161 if (isFloatingPoint!T) 162 in (scale > 0, "scale must be greater than zero") 163 { 164 return laplaceCCDF((x - location) / scale); 165 } 166 167 /// 168 version(mir_stat_test) 169 @safe pure nothrow @nogc 170 unittest 171 { 172 import mir.test: shouldApprox; 173 174 laplaceCCDF(-2.0).shouldApprox == 0.9323324; 175 laplaceCCDF(-1.0).shouldApprox == 0.8160603; 176 laplaceCCDF(-0.5).shouldApprox == 0.6967347; 177 laplaceCCDF(0.0).shouldApprox == 0.5; 178 laplaceCCDF(0.5).shouldApprox == 0.3032653; 179 laplaceCCDF(1.0).shouldApprox == 0.1839397; 180 laplaceCCDF(2.0).shouldApprox == 0.06766764; 181 182 // Can also provide location/scale parameters 183 laplaceCCDF(-1.0, 2.0, 3.0).shouldApprox == 0.8160603; 184 laplaceCCDF(1.0, 2.0, 3.0).shouldApprox == 0.6417343; 185 laplaceCCDF(4.0, 2.0, 3.0).shouldApprox == 0.2567086; 186 } 187 188 /++ 189 Computes the Laplace inverse cumulative distribution function (InvCDF). 190 191 Params: 192 p = value to evaluate InvCDF 193 194 See_also: 195 $(LINK2 https://en.wikipedia.org/wiki/Laplace_distribution, Laplace Distribution) 196 +/ 197 @safe pure nothrow @nogc 198 T laplaceInvCDF(T)(const T p) 199 if (isFloatingPoint!T) 200 in (p >= 0, "p must be greater than or equal to 0") 201 in (p <= 1, "p must be less than or equal to 1") 202 { 203 import mir.math.common: log; 204 205 if (p <= 0.5) { 206 return log(2 * p); 207 } else { 208 return -log(2 - 2 * p); 209 } 210 } 211 212 /++ 213 Ditto, with location and scale parameters (by standardizing `x`). 214 215 Params: 216 p = value to evaluate InvCDF 217 location = location parameter 218 scale = scale parameter 219 220 See_also: 221 $(LINK2 https://en.wikipedia.org/wiki/Logistic_distribution, Logistic probability distribution) 222 +/ 223 T laplaceInvCDF(T)(const T p, const T location, const T scale) 224 if (isFloatingPoint!T) 225 in (p >= 0, "p must be greater than or equal to 0") 226 in (p <= 1, "p must be less than or equal to 1") 227 in (scale > 0, "scale must be greater than zero") 228 { 229 return location + scale * laplaceInvCDF(p); 230 } 231 232 /// 233 version(mir_stat_test) 234 @safe pure nothrow @nogc 235 unittest { 236 import mir.test: shouldApprox; 237 238 laplaceInvCDF(0.0).shouldApprox == -double.infinity; 239 laplaceInvCDF(0.25).shouldApprox == -0.6931472; 240 laplaceInvCDF(0.5).shouldApprox == 0.0; 241 laplaceInvCDF(0.75).shouldApprox == 0.6931472; 242 laplaceInvCDF(1.0).shouldApprox == double.infinity; 243 244 // Can also provide location/scale parameters 245 laplaceInvCDF(0.2, 2, 3).shouldApprox == -0.7488722; 246 laplaceInvCDF(0.4, 2, 3).shouldApprox == 1.330569; 247 laplaceInvCDF(0.6, 2, 3).shouldApprox == 2.669431; 248 laplaceInvCDF(0.8, 2, 3).shouldApprox == 4.748872; 249 } 250 251 /++ 252 Computes the Laplace log probability density function (LPDF). 253 254 Params: 255 x = value to evaluate LPDF 256 257 See_also: 258 $(LINK2 https://en.wikipedia.org/wiki/Laplace_distribution, Laplace Distribution) 259 +/ 260 @safe pure nothrow @nogc 261 T laplaceLPDF(T)(const T x) 262 if (isFloatingPoint!T) 263 { 264 import mir.math.common: fabs; 265 import mir.math.constant: LN2; 266 267 return -T(LN2) - fabs(x); 268 } 269 270 /++ 271 Ditto, with location and scale parameters (by standardizing `x`). 272 273 Params: 274 x = value to evaluate LPDF 275 location = location parameter 276 scale = scale parameter 277 278 See_also: 279 $(LINK2 https://en.wikipedia.org/wiki/Laplace_distribution, Laplace Distribution) 280 +/ 281 T laplaceLPDF(T)(const T x, const T location, const T scale) 282 if (isFloatingPoint!T) 283 in (scale > 0, "shape must be greater than zero") 284 { 285 import mir.math.common: log; 286 287 return laplaceLPDF((x - location) / scale) - log(scale); 288 } 289 290 /// 291 version(mir_stat_test) 292 @safe pure nothrow @nogc 293 unittest 294 { 295 import mir.math.common: log; 296 import mir.test: shouldApprox; 297 298 laplaceLPDF(-2.0).shouldApprox == log(0.06766764); 299 laplaceLPDF(-1.0).shouldApprox == log(0.1839397); 300 laplaceLPDF(-0.5).shouldApprox == log(0.3032653); 301 laplaceLPDF(0.0).shouldApprox == log(0.5); 302 laplaceLPDF(0.5).shouldApprox == log(0.3032653); 303 laplaceLPDF(1.0).shouldApprox == log(0.1839397); 304 laplaceLPDF(2.0).shouldApprox == log(0.06766764); 305 306 // Can also provide location/scale parameters 307 laplaceLPDF(-1.0, 2.0, 3.0).shouldApprox == log(0.06131324); 308 laplaceLPDF(1.0, 2.0, 3.0).shouldApprox == log(0.1194219); 309 laplaceLPDF(4.0, 2.0, 3.0).shouldApprox == log(0.08556952); 310 }