1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.rng.sampling.distribution;
19
20 import org.apache.commons.rng.UniformRandomProvider;
21 import org.apache.commons.rng.sampling.SharedStateSampler;
22
23
24
25
26
27
28 final class InternalUtils {
29
30
31
32
33
34
35 private static final double[] LOG_FACTORIALS = {
36 0,
37 0,
38 0.69314718055994530941723212145818,
39 1.7917594692280550008124773583807,
40 3.1780538303479456196469416012971,
41 4.7874917427820459942477009345232,
42 6.5792512120101009950601782929039,
43 8.5251613610654143001655310363471,
44 10.604602902745250228417227400722,
45 12.801827480081469611207717874567,
46 15.104412573075515295225709329251,
47 17.502307845873885839287652907216,
48 19.987214495661886149517362387055,
49 22.55216385312342288557084982862,
50 25.191221182738681500093434693522,
51 27.89927138384089156608943926367,
52 30.671860106080672803758367749503,
53 33.505073450136888884007902367376,
54 36.39544520803305357621562496268,
55 39.339884187199494036224652394567,
56 42.33561646075348502965987597071
57 };
58
59
60 private static final int BEGIN_LOG_FACTORIALS = 2;
61
62
63
64
65
66 private static final double DOUBLE_MULTIPLIER = 0x1.0p-53d;
67
68
69 private InternalUtils() {}
70
71
72
73
74
75
76
77 static double logFactorial(int n) {
78 return LOG_FACTORIALS[n];
79 }
80
81
82
83
84
85
86
87
88
89
90 static double validateProbabilities(double[] probabilities) {
91 if (probabilities == null || probabilities.length == 0) {
92 throw new IllegalArgumentException("Probabilities must not be empty.");
93 }
94
95 double sumProb = 0;
96 for (final double prob : probabilities) {
97 sumProb += requirePositiveFinite(prob, "probability");
98 }
99
100 return requireStrictlyPositiveFinite(sumProb, "sum of probabilities");
101 }
102
103
104
105
106
107
108
109
110
111 static double requireFinite(double x, String name) {
112 if (!Double.isFinite(x)) {
113 throw new IllegalArgumentException(name + " is not finite: " + x);
114 }
115 return x;
116 }
117
118
119
120
121
122
123
124
125
126
127 static double requirePositiveFinite(double x, String name) {
128 if (!(x >= 0 && x < Double.POSITIVE_INFINITY)) {
129 throw new IllegalArgumentException(
130 name + " is not positive and finite: " + x);
131 }
132 return x;
133 }
134
135
136
137
138
139
140
141
142
143 static double requireStrictlyPositiveFinite(double x, String name) {
144 if (!(x > 0 && x < Double.POSITIVE_INFINITY)) {
145 throw new IllegalArgumentException(
146 name + " is not strictly positive and finite: " + x);
147 }
148 return x;
149 }
150
151
152
153
154
155
156
157
158
159
160 static double requirePositive(double x, String name) {
161
162 if (!(x >= 0)) {
163 throw new IllegalArgumentException(name + " is not positive: " + x);
164 }
165 return x;
166 }
167
168
169
170
171
172
173
174
175
176 static double requireStrictlyPositive(double x, String name) {
177
178 if (!(x > 0)) {
179 throw new IllegalArgumentException(name + " is not strictly positive: " + x);
180 }
181 return x;
182 }
183
184
185
186
187
188
189
190
191
192
193
194 static double requireRange(double min, double max, double x, String name) {
195 if (!(min <= x && x < max)) {
196 throw new IllegalArgumentException(
197 String.format("%s not within range: %s <= %s < %s", name, min, x, max));
198 }
199 return x;
200 }
201
202
203
204
205
206
207
208
209
210
211
212 static double requireRangeClosed(double min, double max, double x, String name) {
213 if (!(min <= x && x <= max)) {
214 throw new IllegalArgumentException(
215 String.format("%s not within closed range: %s <= %s <= %s", name, min, x, max));
216 }
217 return x;
218 }
219
220
221
222
223
224
225
226
227
228
229
230
231 static NormalizedGaussianSampler newNormalizedGaussianSampler(
232 NormalizedGaussianSampler sampler,
233 UniformRandomProvider rng) {
234 if (!(sampler instanceof SharedStateSampler<?>)) {
235 throw new UnsupportedOperationException("The underlying sampler cannot share state");
236 }
237 final Object newSampler = ((SharedStateSampler<?>) sampler).withUniformRandomProvider(rng);
238 if (!(newSampler instanceof NormalizedGaussianSampler)) {
239 throw new UnsupportedOperationException(
240 "The underlying sampler did not create a normalized Gaussian sampler");
241 }
242 return (NormalizedGaussianSampler) newSampler;
243 }
244
245
246
247
248
249
250
251 static double makeDouble(long v) {
252
253
254 return (v >>> 11) * DOUBLE_MULTIPLIER;
255 }
256
257
258
259
260
261
262
263 static double makeNonZeroDouble(long v) {
264
265
266 return ((v >>> 11) + 1L) * DOUBLE_MULTIPLIER;
267 }
268
269
270
271
272
273
274
275 public static final class FactorialLog {
276
277
278
279
280 private final double[] logFactorials;
281
282
283
284
285
286
287
288
289 private FactorialLog(int numValues,
290 double[] cache) {
291 logFactorials = new double[numValues];
292
293 final int endCopy;
294 if (cache != null && cache.length > BEGIN_LOG_FACTORIALS) {
295
296 endCopy = Math.min(cache.length, numValues);
297 System.arraycopy(cache, BEGIN_LOG_FACTORIALS, logFactorials, BEGIN_LOG_FACTORIALS,
298 endCopy - BEGIN_LOG_FACTORIALS);
299 } else {
300
301 endCopy = BEGIN_LOG_FACTORIALS;
302 }
303
304
305 for (int i = endCopy; i < numValues; i++) {
306 if (i < LOG_FACTORIALS.length) {
307 logFactorials[i] = LOG_FACTORIALS[i];
308 } else {
309 logFactorials[i] = logFactorials[i - 1] + Math.log(i);
310 }
311 }
312 }
313
314
315
316
317
318
319 public static FactorialLog create() {
320 return new FactorialLog(0, null);
321 }
322
323
324
325
326
327
328
329
330
331 public FactorialLog withCache(final int cacheSize) {
332 return new FactorialLog(cacheSize, logFactorials);
333 }
334
335
336
337
338
339
340
341
342 public double value(final int n) {
343
344 if (n < logFactorials.length) {
345 return logFactorials[n];
346 }
347
348
349 if (n < LOG_FACTORIALS.length) {
350 return LOG_FACTORIALS[n];
351 }
352
353
354 return InternalGamma.logGamma(n + 1.0);
355 }
356 }
357 }