1 /**
2 Authors: [Ilya Yaroshenko](http://9il.github.io)
3 
4 Copyright: © 2014-2015 [Ilya Yaroshenko](http://9il.github.io)
5 
6 License: MIT
7 */
8 module atmosphere.statistic;
9 
10 import core.stdc.tgmath;
11 
12 import std.traits;
13 import std.typecons;
14 import std.math : LN2;
15 import std.algorithm : map;
16 //import std.numeric : sumOfLog2s;
17 import atmosphere.math: sumOfLog2s;
18 
19 /++
20 Minimal sufficient and complete statistic for the generalized inverse Gaussin disributoin.
21 +/
22 struct GeneralizedInverseGaussinStatistic(T)
23 	if(isFloatingPoint!T)
24 {
25 	///`Σ weights[j] * sample[j] / Σ weights[j]`
26 	T mean;
27 	///`Σ weights[j] / sample[j] / Σ weights[j]`
28 	T meani;
29 	///`Σ weights[j] * log(sample[j]) / Σ weights[j]`
30 	T meanl;
31 
32 	///
33 	this(in T[] sample)
34 	in {
35 		assert(positiveSampleCheck(sample));
36 	}
37 	body {
38 		immutable n = sample.length;
39 		mean = sample.wfsum() / n;
40 		meani = sample.map!"1/a".wfsum() / n;
41 		meanl = T(LN2) * sample.map!(x => cast(Unqual!T)x).sumOfLog2s() / n;
42 	}
43 
44 	///
45 	this(in T[] sample, in T[] weights)
46 	in {
47 		assert(positiveSampleCheck(sample, weights));
48 	}
49 	body {
50 		immutable n = weights.wfsum;
51 		mean = sample.wfsum(weights) / n;
52 		meani = sample.map!"1/a".wfsum(weights) / n;
53 		meanl = T(LN2) * sample.map!log2.wfsum(weights) / n;
54 	}
55 
56 	///
57 	C opCast(C : GeneralizedInverseGaussinFixedLambdaStatistic!T)() pure const
58 	{
59 		GeneralizedInverseGaussinFixedLambdaStatistic!T stat = void;
60 		stat.mean = mean;
61 		stat.meani = meani;
62 		return stat;
63 	}
64 
65 	///
66 	C opCast(C : GammaStatistic!T)() pure const
67 	{
68 		GammaStatistic!T stat = void;
69 		stat.mean = mean;
70 		stat.meanl = meanl;
71 		return stat;
72 	}
73 
74 	///
75 	C opCast(C : InverseGammaStatistic!T)() pure const
76 	{
77 		InverseGammaStatistic!T stat = void;
78 		stat.meani = meani;
79 		stat.meanl = meanl;
80 		return stat;
81 	}
82 }
83 
84 unittest {
85 	alias st = GeneralizedInverseGaussinStatistic!double;
86 }
87 
88 /++
89 Minimal sufficient and complete statistic for the generalized inverse Gaussin disributoin with fixed lambda.
90 +/
91 struct GeneralizedInverseGaussinFixedLambdaStatistic(T)
92 	if(isFloatingPoint!T)
93 {
94 	///`Σ weights[j] * sample[j] / Σ weights[j]`
95 	T mean;
96 	///`Σ weights[j] / sample[j] / Σ weights[j]`
97 	T meani;
98 
99 	///
100 	this(in T[] sample)
101 	in {
102 		assert(positiveSampleCheck(sample));
103 	}
104 	body {
105 		immutable n = sample.length;
106 		mean = sample.wfsum() / n;
107 		meani = sample.map!"1/a".wfsum() / n;
108 	}
109 
110 	///
111 	this(in T[] sample, in T[] weights)
112 	in {
113 		assert(positiveSampleCheck(sample, weights));
114 	}
115 	body {
116 		immutable n = weights.wfsum;
117 		mean = sample.wfsum(weights) / n;
118 		meani = sample.map!"1/a".wfsum(weights) / n;
119 	}
120 }
121 
122 unittest {
123 	alias st = GeneralizedInverseGaussinFixedLambdaStatistic!double;
124 }
125 
126 
127 /++
128 Minimal sufficient and complete statistic for the generalized gamma disributoin with fixed `power` parameter.
129 +/
130 struct GeneralizedGammaFixedPowerStatistic(T)
131 	if(isFloatingPoint!T)
132 {
133 	///`Σ weights[j] * sample[j] ^^ power / Σ weights[j]`
134 	T meanp;
135 	///`Σ weights[j] * log(sample[j]) / Σ weights[j]`
136 	T meanl;
137 
138 	///
139 	this(T power, in T[] sample)
140 	in {
141 		assert(positiveSampleCheck(sample));
142 	}
143 	body {
144 		immutable n = sample.length;
145 		meanp = sample.map!(x => x.pow(power)).wfsum / n;
146 		meanl = T(LN2) * sample.map!log2.wfsum / n;
147 	}
148 
149 	///
150 	this(T power, in T[] sample, in T[] weights)
151 	in {
152 		assert(positiveSampleCheck(sample, weights));
153 	}
154 	body {
155 		immutable n = weights.wfsum;
156 		meanp = sample.map!(x => x.pow(power)).wfsum(weights) / n;
157 		meanl = T(LN2) * sample.map!log2.wfsum(weights) / n;
158 	}
159 }
160 
161 unittest {
162 	alias st = GeneralizedGammaFixedPowerStatistic!double;
163 }
164 
165 
166 /++
167 Minimal sufficient and complete statistic for the gamma disributoin.
168 +/
169 struct GammaStatistic(T)
170 	if(isFloatingPoint!T)
171 {
172 	///`Σ weights[j] * sample[j] / Σ weights[j]`
173 	T mean;
174 	///`Σ weights[j] * log(sample[j]) / Σ weights[j]`
175 	T meanl;
176 
177 	///
178 	this(in T[] sample)
179 	in {
180 		assert(positiveSampleCheck(sample));
181 	}
182 	body {
183 		immutable n = sample.length;
184 		mean = sample.wfsum / n;
185 		meanl = T(LN2) * sample.map!(x => cast(Unqual!T)x).sumOfLog2s / n;
186 	}
187 
188 	///
189 	this(in T[] sample, in T[] weights)
190 	in {
191 		assert(positiveSampleCheck(sample, weights));
192 	}
193 	body {
194 		immutable n = weights.wfsum;
195 		mean = sample.wfsum(weights) / n;
196 		meanl = T(LN2) * sample.map!log2.wfsum(weights) / n;
197 	}
198 }
199 
200 unittest {
201 	alias st = GammaStatistic!double;
202 }
203 
204 
205 /++
206 Minimal sufficient and complete statistic for the inverse-gamma disributoin.
207 +/
208 struct InverseGammaStatistic(T)
209 	if(isFloatingPoint!T)
210 {
211 	///`Σ weights[j] / sample[j] / Σ weights[j]`
212 	T meani;
213 	///`Σ weights[j] * log(sample[j]) / Σ weights[j]`
214 	T meanl;
215 
216 	///
217 	this(in T[] sample)
218 	in {
219 		assert(positiveSampleCheck(sample));
220 	}
221 	body {
222 		immutable n = sample.length;
223 		meani = sample.map!"1/a".wfsum / n;
224 		meanl = T(LN2) * sample.map!(x => cast(Unqual!T)x).sumOfLog2s / n;
225 	}
226 
227 	///
228 	this(in T[] sample, in T[] weights)
229 	in {
230 		assert(positiveSampleCheck(sample, weights));
231 	}
232 	body {
233 		immutable n = weights.wfsum;
234 		meani = sample.map!"1/a".wfsum(weights) / n;
235 		meanl = T(LN2) * sample.map!log2.wfsum(weights) / n;
236 	}
237 }
238 
239 package:
240 
241 unittest {
242 	alias st = InverseGammaStatistic!double;
243 }
244 
245 
246 bool positiveSampleCheck(T)(in T[] sample)
247 {
248 	import std.algorithm : all;
249 	return sample.all!"a > 0 && isNormal(a)";
250 }
251 
252 bool positiveSampleCheck(T)(in T[] sample, in T[] weights)
253 {
254 	import std.algorithm : all, any;
255 	return 
256 	   weights.length == sample.length
257 	&& sample.all!"a > 0 && isNormal(a)"
258 	&& weights.all!"a >= 0 && isFinite(a)"
259 	&& weights.any!"a > 0";
260 }
261 
262 auto wfsum(Range)(Range sample)
263 {
264 	import atmosphere.summation;
265 	return sample.fsum!(Summation.KB2);
266 }
267 
268 unittest {
269 	assert(wfsum([1.0, 2]) == 3);
270 }
271 
272 T wfsum(Range, T)(Range sample, in T[] weights)
273 {
274 	import atmosphere.summation;
275 	import std.range;
276 	assert(sample.length == weights.length);
277 	Summator!(T, Summation.KB2) s = 0;
278 	foreach(i, w; weights)
279 	{
280 		s += sample.front * w;
281 		sample.popFront;
282 	}
283 	return s.sum;
284 }
285 
286 unittest {
287 	assert(wfsum([1.0, 2], [3.0, 4]) == 11);
288 }