The OpenD Programming Language

1 /++
2 This module contains algorithms for the $(LINK2 https://en.wikipedia.org/wiki/Bernoulli_distribution, Bernoulli Distribution).
3 
4 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
5 
6 Authors: John Michael Hall
7 
8 Copyright: 2022-3 Mir Stat Authors.
9 
10 +/
11 
12 module mir.stat.distribution.bernoulli;
13 
14 import mir.internal.utility: isFloatingPoint;
15 
16 /++
17 Computes the bernoulli probability mass function (PMF).
18 
19 Params:
20     x = value to evaluate PMF
21     p = `true` probability
22 
23 See_also:
24     $(LINK2 https://en.wikipedia.org/wiki/Bernoulli_distribution, Bernoulli Distribution)
25 +/
26 @safe pure nothrow @nogc
27 T bernoulliPMF(T)(const bool x, const T p)
28     if (isFloatingPoint!T)
29     in (p >= 0, "p must be greater than or equal to 0")
30     in (p <= 1, "p must be less than or equal to 1")
31 {
32     return p * x + (1 - p) * (1 - x);
33 }
34 
35 ///
36 version(mir_stat_test)
37 @safe pure nothrow @nogc
38 unittest {
39     import mir.math.common: approxEqual;
40 
41     assert(true.bernoulliPMF(0.5) == 0.5);
42     assert(false.bernoulliPMF(0.5) == 0.5);
43 
44     assert(true.bernoulliPMF(0.7).approxEqual(0.7));
45     assert(false.bernoulliPMF(0.7).approxEqual(0.3));
46 }
47 
48 /++
49 Computes the bernoulli cumulatve distribution function (CDF).
50 
51 Params:
52     x = value to evaluate CDF
53     p = `true` probability
54 
55 See_also:
56     $(LINK2 https://en.wikipedia.org/wiki/Bernoulli_distribution, Bernoulli Distribution)
57 +/
58 @safe pure nothrow @nogc
59 T bernoulliCDF(T)(const bool x, const T p)
60     if (isFloatingPoint!T)
61     in (p >= 0, "p must be greater than or equal to 0")
62     in (p <= 1, "p must be less than or equal to 1")
63 {
64     return x + (1 - x) * (1 - p);
65 }
66 
67 ///
68 version(mir_stat_test)
69 @safe pure nothrow @nogc
70 unittest {
71     import mir.math.common: approxEqual;
72 
73     assert(true.bernoulliCDF(0.5) == 1);
74     assert(false.bernoulliCDF(0.5) == 0.5);
75 
76     assert(true.bernoulliCDF(0.7) == 1);
77     assert(false.bernoulliCDF(0.7).approxEqual(0.3));
78 }
79 
80 /++
81 Computes the bernoulli complementary cumulative distribution function (CCDF).
82 
83 Params:
84     x = value to evaluate CCDF
85     p = `true` probability
86 
87 See_also:
88     $(LINK2 https://en.wikipedia.org/wiki/Bernoulli_distribution, Bernoulli Distribution)
89 +/
90 @safe pure nothrow @nogc
91 T bernoulliCCDF(T)(const bool x, const T p)
92     if (isFloatingPoint!T)
93     in (p >= 0, "p must be greater than or equal to 0")
94     in (p <= 1, "p must be less than or equal to 1")
95 {
96     return (1 - x) * p;
97 }
98 
99 ///
100 version(mir_stat_test)
101 @safe pure nothrow @nogc
102 unittest {
103     import mir.math.common: approxEqual;
104 
105     assert(true.bernoulliCCDF(0.5) == 0);
106     assert(false.bernoulliCCDF(0.5) == 0.5);
107 
108     assert(true.bernoulliCCDF(0.7) == 0);
109     assert(false.bernoulliCCDF(0.7).approxEqual(0.7));
110 }
111 
112 /++
113 Computes the bernoulli inverse cumulative distribution function (InvCDF).
114 
115 Params:
116     q = value to evaluate InvCDF
117     p = `true` probability
118 
119 See_also:
120     $(LINK2 https://en.wikipedia.org/wiki/Bernoulli_distribution, Bernoulli Distribution)
121 +/
122 @safe pure nothrow @nogc
123 bool bernoulliInvCDF(T)(const T q, const T p)
124     if (isFloatingPoint!T)
125     in (q >= 0, "q must be greater than or equal to 0")
126     in (q <= 1, "q must be less than or equal to 1")
127     in (p >= 0, "p must be greater than or equal to 0")
128     in (p <= 1, "p must be less than or equal to 1")
129 {
130     return q > p; // this ensures bernoulliInvCDF(a, a) == false, which is consistent with bernoulliCDF(false, a) == a
131 }
132 
133 ///
134 version(mir_stat_test)
135 @safe pure nothrow @nogc
136 unittest {
137     assert(0.25.bernoulliInvCDF(0.5) == false);
138     assert(0.5.bernoulliInvCDF(0.5) == false);
139     assert(0.75.bernoulliInvCDF(0.5) == true);
140 
141     assert(0.3.bernoulliInvCDF(0.7) == false);
142     assert(0.7.bernoulliInvCDF(0.7) == false);
143     assert(0.9.bernoulliInvCDF(0.7) == true);
144 }
145 
146 version(mir_stat_test)
147 @safe pure nothrow @nogc
148 unittest {
149     assert(0.0.bernoulliInvCDF(0.5) == false);
150     assert(1.0.bernoulliInvCDF(0.5) == true);
151     assert(0.0.bernoulliInvCDF(0.7) == false);
152     assert(1.0.bernoulliInvCDF(0.7) == true);
153 }
154 
155 /++
156 Computes the bernoulli log probability mass function (LPMF).
157 
158 Params:
159     x = value to evaluate LPDF
160     p = `true` probability
161 
162 See_also:
163     $(LINK2 https://en.wikipedia.org/wiki/Bernoulli_distribution, Bernoulli Distribution)
164 +/
165 @safe pure nothrow @nogc
166 T bernoulliLPMF(T)(const bool x, const T p)
167     if (isFloatingPoint!T)
168     in (p >= 0, "p must be greater than or equal to 0")
169     in (p <= 1, "p must be less than or equal to 1")
170 {
171     import mir.math.internal.xlogy: xlogy, xlog1py;
172 
173     return xlogy(x, p) + xlog1py(1 - x, -p);
174 }
175 
176 ///
177 version(mir_stat_test)
178 @safe pure nothrow @nogc
179 unittest {
180     import mir.math.common: approxEqual, log;
181 
182     assert(true.bernoulliLPMF(0.5).approxEqual(log(bernoulliPMF(true, 0.5))));
183     assert(false.bernoulliLPMF(0.5).approxEqual(log(bernoulliPMF(false, 0.5))));
184 
185     assert(true.bernoulliLPMF(0.7).approxEqual(log(bernoulliPMF(true, 0.7))));
186     assert(false.bernoulliLPMF(0.7).approxEqual(log(bernoulliPMF(false, 0.7))));
187 }