1 module lbfgsd.distribution;
2 
3 import std.math : PI;
4 import lbfgsd.math;
5 
6 struct NormalDistribution(T)
7 {
8 public:
9     this(T mu,  T sigma)
10     {
11         this.mu = mu;
12         this.sigma = sigma;
13     }
14 
15 public:
16     auto pdf(U)(U x)
17     {
18         return exp(-square(x - mu) / square(sigma)) / sqrt(2 * PI * square(sigma));
19     }
20 
21     auto cdf(U)(U x)
22     {
23         import std.mathspecial: normalDistribution;
24         return normalDistribution(x);
25     }
26 
27     auto cdfInverse(U)(U p)
28     {
29         import std.mathspecial: normalDistributionInverse;
30         return normalDistributionInverse(x);
31     }
32 
33     auto logLikehood(U)(U x)
34     {
35         return -square(x - mu) / square(sigma) - log(sigma) - (log(PI) + log(2)) / 2;
36     }
37 public:
38     T mu; //average
39     T sigma; //standard devision
40 }
41 
42 struct JohnsonSUDistribution(T)
43 {
44 public:
45     this(T gamma, T delta, T lambda, T xi)
46     {
47         this.gamma = gamma;
48         this.delta = delta;
49         this.lambda = lambda;
50         this.xi = xi;
51     }
52 
53 public:
54     auto pdf(U)(U x)
55     {
56         auto z = (x - xi) / lambda;
57         return delta * exp(-0.5 * square(gamma + delta * asinh(z))) / (sqrt(2 * PI) * (lambda * sqrt(square(z) + 1)));
58     }
59 
60     auto cdf(U)(U x)
61     {
62         import std.mathspecial: normalDistribution;
63         return normalDistribution(gamma + delta * asinh((x - xi) / lambda));
64     }
65 
66 	auto cdfInverse(U)(U p)
67 	{
68 		import std.mathspecial : normalDistributionInverse;
69 		auto x = normalDistributionInverse(p);
70 		return xi + lambda * sinh((x - gamma) / delta);
71 	}
72 
73     auto logLikehood(U)(U x)
74     {
75         auto xs = xi - x;
76         return -0.5 * (log((2 * PI * (square(lambda) + square(xs))) / square(delta)) + square(delta * asinh(xs / lambda) - gamma));
77     }
78 
79 public:
80     T gamma;
81     T delta;
82     T lambda;
83     T xi;
84 }
85 
86 JohnsonSUDistribution!T johnsonSUDistribution(T)(T gamma, T delta, T lambda, T xi)
87 {
88     return typeof(return)(gamma, delta, lambda, xi);
89 }
90 
91 unittest
92 {
93     auto dist = johnsonSUDistribution(1.0, 1.0, 1.0, 1.0);
94     auto x = dist.pdf(0.0);
95     auto p = dist.cdf(0.5);
96     auto c = dist.cdfInverse(p);
97     auto l = dist.logLikehood(0.0);
98 
99     import std.math;
100     assert(approxEqual(log(x), l));
101     assert(approxEqual(c, 0.5));
102 }
103 unittest
104 {
105     import lbfgsd.autodiff;
106     alias Var = Variable!(double, 4);
107 
108     auto gamma = Var(1, 0);
109     auto lambda = Var(1, 0);
110     auto delta = Var(1, 0);
111     auto xi = Var(1, 0);
112     auto x = Var(0);
113 
114     auto dist = johnsonSUDistribution(gamma, lambda, delta, xi);
115     auto tx = log(dist.pdf(x));
116     auto tl = dist.logLikehood(x);
117 
118     import std.algorithm, std.math : approxEqual;
119     assert(approxEqual(tx.a, tl.a));
120     assert(equal!approxEqual(tx.d[], tl.d[]));
121 
122     auto x1 = log(dist.pdf(0.0));
123     auto x2 = dist.logLikehood(0.0);
124     assert(approxEqual(x1.a, x2.a));
125     assert(equal!approxEqual(x1.d[], x2.d[]));
126 }
127 
128 struct GumbelDistribution(T)
129 {
130 public:
131     this(in T mu, in T eta)
132     {
133         this.mu = mu;
134         this.eta = eta;
135     }
136 
137 public:
138     auto pdf(U)(U x)
139     {
140         auto y = exp((mu - x) / eta);
141         return exp(-y) * y / eta;
142     }
143 
144     auto cdf(U)(U p)
145     {
146         return exp(-exp((mu - p) / eta));
147     }
148 
149     auto cdfInverse(U)(U x)
150     {
151         return mu - eta * log(-log(x));
152     }
153 
154     auto logLikehood(U)(U x)
155     {
156         auto z = (mu - x) / eta;
157         return z - exp(z) - log(eta);
158     }
159 
160 public:
161     T mu;
162     T eta;
163 }
164 
165 GumbelDistribution!T gumbelDistribution(T)(T mu, T eta)
166 {
167     return typeof(return)(mu, eta);
168 }
169 
170 unittest
171 {
172     auto dist = gumbelDistribution(1.0, 1.0);
173     auto p = dist.pdf(0.0);
174     auto x = dist.cdf(0.5);
175     auto c = dist.cdfInverse(x);
176     auto l = dist.logLikehood(0.0);
177 
178     import std.math;
179     assert(approxEqual(log(p), l));
180     assert(approxEqual(c, 0.5));
181 }
182 
183 unittest
184 {
185     import lbfgsd.autodiff;
186     alias Var = Variable!(double, 4);
187 
188     auto mu = Var(1, 0);
189     auto eta = Var(1, 0);
190     auto x = Var(0.1);
191     auto p = Var(0.25);
192 
193     auto dist = gumbelDistribution(mu, eta);
194     auto tx = dist.pdf(x);
195     auto tl = dist.logLikehood(x);
196 
197     auto lx = log(tx);
198     import std.algorithm, std.math : approxEqual;
199     assert(approxEqual(lx.a, tl.a));
200     assert(equal!approxEqual(lx.d[], tl.d[]));
201 
202     auto x1 = log(dist.pdf(0.30));
203     auto x2 = dist.logLikehood(0.30);
204     assert(approxEqual(x1.a, x2.a));
205     assert(equal!approxEqual(x1.d[], x2.d[]));
206 
207     auto c = dist.cdf(p);
208     auto tp = dist.cdfInverse(c);
209     assert(approxEqual(p.a, tp.a));
210     assert(equal!approxEqual(p.d[], tp.d[]));
211 }