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