The OpenD Programming Language

1 /++
2 This module contains algorithms for the $(LINK2 https://en.wikipedia.org/wiki/Weibull_distribution, Weibull 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.weibull;
13 
14 import mir.internal.utility: isFloatingPoint;
15 
16 /++
17 Computes the Weibull probability density function (PDF).
18 
19 Params:
20     x = value to evaluate PDF
21     shape = shape parameter
22     scale = scale parameter
23 
24 See_also:
25     $(LINK2 https://en.wikipedia.org/wiki/Weibull_distribution, Weibull Distribution)
26 +/
27 @safe pure nothrow @nogc
28 T weibullPDF(T)(const T x, const T shape, const T scale = 1)
29     if (isFloatingPoint!T)
30     in (x >= 0, "x must be greater than or equal to zero")
31     in (shape > 0, "shape must be greater than zero")
32     in (scale > 0, "scale must be greater than zero")
33 {
34     import mir.math.common: exp, pow;
35 
36     const T x_scale = x / scale;
37     return (shape / scale) * pow(x_scale, shape - 1) * exp(-pow(x_scale, shape));
38 }
39 
40 ///
41 version(mir_stat_test)
42 @safe pure nothrow @nogc
43 unittest
44 {
45     import mir.test: shouldApprox;
46 
47     0.0.weibullPDF(3.0).shouldApprox == 0;
48     0.5.weibullPDF(3.0).shouldApprox == 0.6618727;
49     1.0.weibullPDF(3.0).shouldApprox == 1.103638;
50     1.5.weibullPDF(3.0).shouldApprox == 0.2309723;
51 
52     // Can also provide scale parameter
53     0.5.weibullPDF(2.0, 3.0).shouldApprox == 0.1080672;
54     1.0.weibullPDF(2.0, 3.0).shouldApprox == 0.1988532;
55     1.5.weibullPDF(2.0, 3.0).shouldApprox == 0.2596003;
56 }
57 
58 //
59 version(mir_stat_test)
60 @safe pure nothrow @nogc
61 unittest
62 {
63     import mir.test: shouldApprox;
64 
65     0.0.weibullPDF(1.0, 3.0).shouldApprox == 0.3333333;
66 }
67 
68 /++
69 Computes the Weibull cumulative distribution function (CDF).
70 
71 Params:
72     x = value to evaluate CDF
73     shape = shape parameter
74     scale = scale parameter
75 
76 See_also:
77     $(LINK2 https://en.wikipedia.org/wiki/Weibull_distribution, Weibull Distribution)
78 +/
79 @safe pure nothrow @nogc
80 T weibullCDF(T)(const T x, const T shape, const T scale = 1)
81     if (isFloatingPoint!T)
82     in (x >= 0, "x must be greater than or equal to zero")
83     in (shape > 0, "shape must be greater than zero")
84     in (scale > 0, "scale must be greater than zero")
85 {
86     import mir.math.common: exp, pow;
87 
88     return 1 - exp(-pow(x / scale, shape));
89 }
90 
91 ///
92 version(mir_stat_test)
93 @safe pure nothrow @nogc
94 unittest
95 {
96     import mir.test: shouldApprox;
97 
98     0.0.weibullCDF(3.0).shouldApprox == 0;
99     0.5.weibullCDF(3.0).shouldApprox == 0.1175031;
100     1.0.weibullCDF(3.0).shouldApprox == 0.6321206;
101     1.5.weibullCDF(3.0).shouldApprox == 0.9657819;
102 
103     // Can also provide scale parameter
104     0.5.weibullCDF(2.0, 3.0).shouldApprox == 0.02739552;
105     1.0.weibullCDF(2.0, 3.0).shouldApprox == 0.1051607;
106     1.5.weibullCDF(2.0, 3.0).shouldApprox == 0.2211992;
107 }
108 
109 /++
110 Computes the Weibull complementary cumulative distribution function (CCDF).
111 
112 Params:
113     x = value to evaluate CCDF
114     shape = shape parameter
115     scale = scale parameter
116 
117 See_also:
118     $(LINK2 https://en.wikipedia.org/wiki/Weibull_distribution, Weibull Distribution)
119 +/
120 @safe pure nothrow @nogc
121 T weibullCCDF(T)(const T x, const T shape, const T scale = 1)
122     if (isFloatingPoint!T)
123     in (x >= 0, "x must be greater than or equal to zero")
124     in (shape > 0, "shape must be greater than zero")
125     in (scale > 0, "scale must be greater than zero")
126 {
127     import mir.math.common: exp, pow;
128 
129     return exp(-pow(x / scale, shape));
130 }
131 
132 ///
133 version(mir_stat_test)
134 @safe pure nothrow @nogc
135 unittest
136 {
137     import mir.test: shouldApprox;
138 
139     0.0.weibullCCDF(3.0).shouldApprox == 1;
140     0.5.weibullCCDF(3.0).shouldApprox == 0.8824969;
141     1.0.weibullCCDF(3.0).shouldApprox == 0.3678794;
142     1.5.weibullCCDF(3.0).shouldApprox == 0.03421812;
143 
144     // Can also provide scale parameter
145     0.5.weibullCCDF(2.0, 3.0).shouldApprox == 0.9726045;
146     1.0.weibullCCDF(2.0, 3.0).shouldApprox == 0.8948393;
147     1.5.weibullCCDF(2.0, 3.0).shouldApprox == 0.7788008;
148 }
149 
150 /++
151 Computes the Weibull inverse cumulative distribution function (InvCDF).
152 
153 Params:
154     p = value to evaluate InvCDF
155     shape = shape parameter
156     scale = scale parameter
157 
158 See_also:
159     $(LINK2 https://en.wikipedia.org/wiki/Weibull_distribution, Weibull Distribution)
160 +/
161 @safe pure nothrow @nogc
162 T weibullInvCDF(T)(const T p, const T shape, const T scale = 1)
163     if (isFloatingPoint!T)
164     in (p >= 0, "p must be greater than or equal to 0")
165     in (p <= 1, "p must be less than or equal to 1")
166     in (shape > 0, "shape must be greater than zero")
167     in (scale > 0, "scale must be greater than zero")
168 {
169     import mir.math.common: log, pow;
170 
171     return scale * pow(-log(1 - p), T(1) / shape);
172 }
173 
174 ///
175 version(mir_stat_test)
176 @safe pure nothrow @nogc
177 unittest {
178     import mir.test: shouldApprox;
179 
180     weibullInvCDF(0.0, 3).shouldApprox == 0.0;
181     weibullInvCDF(0.25, 3).shouldApprox == 0.6601424;
182     weibullInvCDF(0.5, 3).shouldApprox == 0.884997;
183     weibullInvCDF(0.75, 3).shouldApprox == 1.115026;
184     weibullInvCDF(1.0, 3).shouldApprox == double.infinity;
185 
186     // Can also provide scale parameter
187     weibullInvCDF(0.2, 2, 3).shouldApprox == 1.417142;
188     weibullInvCDF(0.4, 2, 3).shouldApprox == 2.144162;
189     weibullInvCDF(0.6, 2, 3).shouldApprox == 2.871692;
190     weibullInvCDF(0.8, 2, 3).shouldApprox == 3.805909;
191 }
192 
193 
194 /++
195 Computes the Weibull log probability density function (LPDF).
196 
197 Params:
198     x = value to evaluate LPDF
199     shape = shape parameter
200     scale = scale parameter
201 
202 See_also:
203     $(LINK2 https://en.wikipedia.org/wiki/Weibull_distribution, Weibull Distribution)
204 +/
205 @safe pure nothrow @nogc
206 T weibullLPDF(T)(const T x, const T shape, const T scale = 1)
207     if (isFloatingPoint!T)
208     in (x >= 0, "x must be greater than or equal to zero")
209     in (shape > 0, "shape must be greater than zero")
210     in (scale > 0, "scale must be greater than zero")
211 {
212     import mir.math.common: log, pow;
213     import mir.math.internal.xlogy: xlogy;
214 
215     const T x_scale = x / scale;
216     return log(shape / scale) + xlogy(shape - 1, x_scale) - pow(x_scale, shape);
217 }
218 
219 ///
220 version(mir_stat_test)
221 @safe pure nothrow @nogc
222 unittest
223 {
224     import mir.math.common: log;
225     import mir.test: shouldApprox;
226 
227     0.0.weibullLPDF(3.0).shouldApprox == log(0.0);
228     0.5.weibullLPDF(3.0).shouldApprox == log(0.6618727);
229     1.0.weibullLPDF(3.0).shouldApprox == log(1.103638);
230     1.5.weibullLPDF(3.0).shouldApprox == log(0.2309723);
231 
232     // Can also provide scale parameter
233     0.5.weibullLPDF(2.0, 3.0).shouldApprox == log(0.1080672);
234     1.0.weibullLPDF(2.0, 3.0).shouldApprox == log(0.1988532);
235     1.5.weibullLPDF(2.0, 3.0).shouldApprox == log(0.2596003);
236 }