LMMC
Lamina Mathematic core
载入中...
搜索中...
未找到
statistics_internal.c
浏览该文件的文档.
2
3#define SPECIAL_FUNCTION_MAX_ITERATIONS 1000
4#define SPECIAL_FUNCTION_TOLERANCE 1e-15
5#define LENTZ_TINY 1e-300
6
7static lmmc_real_t lentz_nonzero(lmmc_real_t value) {
8 if (fabs(value) < LENTZ_TINY) {
9 return copysign(LENTZ_TINY, value == 0.0 ? 1.0 : value);
10 }
11 return value;
12}
13
15 if (x < 16.0) {
16 return lgamma(x + 1.0) -
17 (x + 0.5) * log(x) + x -
19 }
20
21 {
22 const lmmc_real_t inverse = 1.0 / x;
23 const lmmc_real_t inverse_squared = inverse * inverse;
24 return inverse *
25 (1.0 / 12.0 -
26 inverse_squared *
27 (1.0 / 360.0 -
28 inverse_squared *
29 (1.0 / 1260.0 -
30 inverse_squared / 1680.0)));
31 }
32}
33
34
36 if (x < 16.0) {
37 return lgamma(x) -
38 (x - 0.5) * log(x) + x -
40 }
41
42 {
43 const lmmc_real_t inverse = 1.0 / x;
44 const lmmc_real_t inverse_squared = inverse * inverse;
45 return inverse *
46 (1.0 / 12.0 -
47 inverse_squared *
48 (1.0 / 360.0 -
49 inverse_squared *
50 (1.0 / 1260.0 -
51 inverse_squared / 1680.0)));
52 }
53}
55 lmmc_real_t x, lmmc_real_t mean) {
56 if (x == mean) { return 0.0; }
57 if (x == 0.0) { return mean; }
58 if (mean == 0.0) { return INFINITY; }
59
60 if (fabs(x - mean) < 0.1 * x + 0.1 * mean) {
61 const lmmc_real_t difference = x - mean;
63 const lmmc_real_t ratio = (difference / x) / (1.0 + mean / x);
64 const lmmc_real_t ratio_squared = ratio * ratio;
65 lmmc_real_t sum = 0.5 * difference * ratio;
66 lmmc_real_t term = x * ratio;
67 size_t order;
68
69 for (order = 1; order < 100; ++order) {
70 const lmmc_real_t previous = sum;
71 term *= ratio_squared;
72 sum += term / (lmmc_real_t)(2 * order + 1);
73 if (sum == previous) { break; }
74 }
75 return 2.0 * sum;
76 }
77
78 {
79 const lmmc_real_t quotient = x / mean;
80 const lmmc_real_t log_ratio =
81 quotient > 0.0 && isfinite(quotient) ? log(quotient) :
82 log(x) - log(mean);
83 return x > mean ?
84 x * (log_ratio - 1.0) + mean :
85 x * log_ratio + mean - x;
86 }
87}
88
89static lmmc_status_t store_regularized_probability(
90 lmmc_real_t value, lmmc_real_t* out) {
91 if (!out) { return LMMC_STATUS_INVALID_ARGUMENT; }
92 if (!isfinite(value) || value < 0.0 || value > 1.0) { return LMMC_STATUS_NUMERICAL_FAILURE; }
93 *out = value;
94 return LMMC_STATUS_OK;
95}
96
97/*
98 * DLMF 8.12.3: leading uniform large-a expansion for Q(a, x).
99 * The continued fraction and power series require O(sqrt(a)) work around
100 * x == a; this expansion is both bounded-cost and stable in that region.
101 */
102static int regularized_gamma_large_central_upper(
103 lmmc_real_t a, lmmc_real_t x, lmmc_real_t* upper) {
104 lmmc_real_t delta, eta, eta_squared, c0, correction, value;
105
106 if (a < 10000.0 || x <= 0.0) { return 0; }
107 delta = x / a - 1.0;
108 if (fabs(delta) > 0.1) { return 0; }
109
110 eta_squared = 2.0 * (delta - log1p(delta));
111 if (eta_squared < 0.0) {
112 if (eta_squared > -DBL_EPSILON) {
113 eta_squared = 0.0;
114 } else {
115 return 0;
116 }
117 }
118 eta = copysign(sqrt(eta_squared), delta);
119 if (fabs(eta) * sqrt(a) > 8.0) { return 0; }
120
121 if (fabs(eta) < 1e-3) {
122 c0 = -1.0 / 3.0 + eta / 12.0 -
123 2.0 * eta * eta / 135.0;
124 } else {
125 c0 = 1.0 / delta - 1.0 / eta;
126 }
127 correction =
128 exp(-0.5 * a * eta_squared) * c0 /
129 (LMMC_SQRT_2PI * sqrt(a));
130 value = 0.5 * erfc(eta * sqrt(0.5 * a)) + correction;
131 if (!isfinite(value) || value < 0.0 || value > 1.0) { return 0; }
132
133 *upper = value;
134 return 1;
135}
136
143 if (!out) { return LMMC_STATUS_INVALID_ARGUMENT; }
144 if (x <= 0.0) { return store_regularized_probability(0.0, out); }
145 {
146 lmmc_real_t upper;
147 if (regularized_gamma_large_central_upper(a, x, &upper)) { return store_regularized_probability(1.0 - upper, out); }
148 }
149 if (x < a + 1.0) {
150 lmmc_real_t sum = 1.0 / a;
151 lmmc_real_t term = sum;
152 lmmc_real_t ap = a;
153 int i;
154 for (i = 0; i < SPECIAL_FUNCTION_MAX_ITERATIONS; ++i) {
155 ap += 1.0;
156 term *= x / ap;
157 sum += term;
158 if (!isfinite(term) || !isfinite(sum)) { return LMMC_STATUS_NUMERICAL_FAILURE; }
159 if (fabs(term) < fabs(sum) * SPECIAL_FUNCTION_TOLERANCE) { break; }
160 }
162 return store_regularized_probability(
163 sum * exp(-x + a * log(x) - lgamma(a)), out);
164 } else {
165 lmmc_real_t upper;
166 lmmc_status_t status = regularized_gamma_upper_cf(a, x, &upper);
167 if (status != LMMC_STATUS_OK) { return status; }
168 return store_regularized_probability(1.0 - upper, out);
169 }
170}
171
178 lmmc_real_t f, c, d, delta;
179 int i;
180
181 if (!out) { return LMMC_STATUS_INVALID_ARGUMENT; }
182 f = x - a + 1.0;
183 if (!isfinite(f)) { return LMMC_STATUS_NUMERICAL_FAILURE; }
184 f = lentz_nonzero(f);
185 c = f;
186 d = 0.0;
187
188 for (i = 1; i <= SPECIAL_FUNCTION_MAX_ITERATIONS; ++i) {
189 lmmc_real_t an = (lmmc_real_t)i * (a - (lmmc_real_t)i);
190 lmmc_real_t bn = x - a + 2.0 * (lmmc_real_t)i + 1.0;
191 if (!isfinite(an) || !isfinite(bn)) { return LMMC_STATUS_NUMERICAL_FAILURE; }
192
193 d = bn + an * d;
194 d = lentz_nonzero(d);
195 d = 1.0 / d;
196 c = bn + an / c;
197 c = lentz_nonzero(c);
198 delta = c * d;
199 f *= delta;
200 if (!isfinite(c) || !isfinite(d) || !isfinite(delta) || !isfinite(f)) { return LMMC_STATUS_NUMERICAL_FAILURE; }
201 if (fabs(delta - 1.0) < SPECIAL_FUNCTION_TOLERANCE) { break; }
202 }
204 return store_regularized_probability(
205 exp(-x + a * log(x) - lgamma(a)) / f, out);
206}
207
213 if (!out) { return LMMC_STATUS_INVALID_ARGUMENT; }
214 if (x <= 0.0) { return store_regularized_probability(1.0, out); }
215 {
216 lmmc_real_t upper;
217 if (regularized_gamma_large_central_upper(a, x, &upper)) { return store_regularized_probability(upper, out); }
218 }
219 if (x < a + 1.0) {
220 lmmc_real_t lower;
221 lmmc_status_t status = regularized_gamma_lower(a, x, &lower);
222 if (status != LMMC_STATUS_OK) { return status; }
223 return store_regularized_probability(1.0 - lower, out);
224 }
225 return regularized_gamma_upper_cf(a, x, out);
226}
227
231static lmmc_status_t regularized_beta_cf(
232 lmmc_real_t x, lmmc_real_t complement_x,
234 const lmmc_real_t qap = a + 1.0;
235 const lmmc_real_t qam = a - 1.0;
236 lmmc_real_t c = 1.0;
237 lmmc_real_t d, f;
238 int m;
239
240 if (!out) { return LMMC_STATUS_INVALID_ARGUMENT; }
241 d = complement_x + (1.0 - b) * x / qap;
242 d = lentz_nonzero(d);
243 d = 1.0 / d;
244 f = d;
245
246 for (m = 1; m <= SPECIAL_FUNCTION_MAX_ITERATIONS; ++m) {
247 const lmmc_real_t order = (lmmc_real_t)m;
248 const lmmc_real_t m2 = 2.0 * order;
249 const lmmc_real_t even =
250 ((b - order) * x / (a + m2)) * (order / (qam + m2));
255 const lmmc_real_t one_plus_odd =
256 complement_x +
257 (a / (a + m2)) * ((1.0 + m2 - b) * x / (qap + m2)) +
258 (order / (a + m2)) *
259 ((2.0 + 3.0 * order - b) * x / (qap + m2));
260 const lmmc_real_t c_offset = even / c;
261 const lmmc_real_t d_offset = even * d;
262 lmmc_real_t c_numerator = one_plus_odd + c_offset;
263 lmmc_real_t d_denominator = one_plus_odd + d_offset;
264 lmmc_real_t c_denominator = 1.0 + c_offset;
265 lmmc_real_t d_numerator = 1.0 + d_offset;
266 lmmc_real_t delta;
267
268 c_numerator = lentz_nonzero(c_numerator);
269 d_denominator = lentz_nonzero(d_denominator);
270 c_denominator = lentz_nonzero(c_denominator);
271 d_numerator = lentz_nonzero(d_numerator);
272 c = c_numerator / c_denominator;
273 d = d_numerator / d_denominator;
274 delta = c_numerator / d_denominator;
275 f *= delta;
276 if (!isfinite(even) || !isfinite(one_plus_odd)) {
278 }
279 if (!isfinite(c) || !isfinite(d)) {
281 }
282 if (!isfinite(delta) || !isfinite(f)) {
284 }
285 if (fabs(delta - 1.0) < SPECIAL_FUNCTION_TOLERANCE) { break; }
286 }
288 if (!(f > 0.0)) { return LMMC_STATUS_NUMERICAL_FAILURE; }
289 *out = f;
290 return LMMC_STATUS_OK;
291}
292
296static double beta_stirling_inverse(double inverse) {
297 const double square = inverse * inverse;
298 return inverse * (1.0 / 12.0 + square * (-1.0 / 360.0 +
299 square * (1.0 / 1260.0 - square / 1680.0)));
300}
301
306static double beta_scaled_log1pmx(double small, double large) {
307 const double ratio = small / large;
308 if (ratio <= 0.125) {
309 double polynomial = -1.0 / 32.0;
310 int k;
311 for (k = 31; k >= 2; --k) {
312 polynomial = fma(polynomial, ratio,
313 (k % 2 ? 1.0 : -1.0) / (double)k);
314 }
315 return (small * ratio) * polynomial;
316 }
317 return large * (log1p(ratio) - ratio);
318}
319
320static double beta_ratio_correction(double small, double large) {
321 const double ratio = small / large;
322 return (small - 0.5) * log1p(ratio) +
323 beta_scaled_log1pmx(small, large) +
324 beta_stirling_inverse((1.0 / large) / (1.0 + ratio)) -
326}
327
328lmmc_status_t lmmc_log_beta(double a, double b, double* out_log_beta) {
329 double value;
330 const double small = fmin(a, b), large = fmax(a, b);
331 if (!out_log_beta || !isfinite(a) || !isfinite(b) || a <= 0.0 || b <= 0.0) {
333 }
334 if (large < 16.0) {
335 value = lgamma(small) + lgamma(large) - lgamma(small + large);
336 } else if (small < 16.0) {
337 value = lgamma(small) -
338 (small * log(large) + beta_ratio_correction(small, large));
339 } else {
340 const double ratio = small / large;
341 const double logarithm = log1p(ratio);
342 const double log_ratio = ratio > 0.0
343 ? log(ratio) : log(small) - log(large);
344 value = small * (log_ratio - logarithm) - large * logarithm +
345 0.5 * (logarithm - log(small)) + LMMC_LOG_SQRT_2PI +
348 beta_stirling_inverse((1.0 / large) / (1.0 + ratio));
349 }
350 if (isnan(value) || value == INFINITY) { return LMMC_STATUS_NUMERICAL_FAILURE; }
351 *out_log_beta = value;
352 return LMMC_STATUS_OK;
353}
354
355static int beta_log_kernel_arguments_valid(
356 double x, double complement_x, double a, double b, const double* out_log_kernel) {
357 if (!out_log_kernel || !isfinite(a) || !isfinite(b) || a <= 0.0 || b <= 0.0) {
358 return 0;
359 }
360 if (!isfinite(x) || !isfinite(complement_x)) {
361 return 0;
362 }
363 if (x <= 0.0 || x > 1.0 || complement_x <= 0.0 || complement_x > 1.0) {
364 return 0;
365 }
366 return 1;
367}
368
369static lmmc_status_t beta_log_kernel_large_shapes(
370 double x, double complement_x, double a, double b, double total, double* value) {
371 if (isfinite(total)) {
372 *value = lmmc_log_gamma_stirling_error(total) -
375 lmmc_deviance_part(a, total * x) -
376 lmmc_deviance_part(b, total * complement_x) +
377 0.5 * (log(a) + log(b) - log(total)) - LMMC_LOG_SQRT_2PI;
378 } else if (a == b && x == 0.5 && complement_x == 0.5) {
379 *value = 0.5 * (log(a) - log(LMMC_PI)) - log(2.0) -
380 (1.0 / a) / 8.0;
381 } else {
383 }
384 return LMMC_STATUS_OK;
385}
386
387static double beta_log_kernel_one_large_shape(
388 double x, double complement_x, double a, double b,
389 double log_x, double log_complement) {
390 const double small = fmin(a, b), large = fmax(a, b);
391 const double small_x = a < b ? x : complement_x;
392 const double log_small_x = a < b ? log_x : log_complement;
393 const double log_large_x = a < b ? log_complement : log_x;
394 const double product = small_x * large;
395 const double log_product = product > 0.0
396 ? log(product) : log_small_x + log(large);
397 return small * log_product + large * log_large_x - lgamma(small) +
398 beta_ratio_correction(small, large);
399}
400
402 double x, double complement_x, double a, double b, double* out_log_kernel) {
403 const double total = a + b;
404 double value;
405 double log_x, log_complement;
406 if (!beta_log_kernel_arguments_valid(x, complement_x, a, b, out_log_kernel)) {
408 }
409 log_x = x <= 0.5 ? log(x) : log1p(-complement_x);
410 log_complement = complement_x <= 0.5 ? log(complement_x) : log1p(-x);
411 if (a >= 16.0 && b >= 16.0) {
412 const lmmc_status_t status =
413 beta_log_kernel_large_shapes(x, complement_x, a, b, total, &value);
414 if (status != LMMC_STATUS_OK) { return status; }
415 } else if (a >= 16.0 || b >= 16.0) {
416 value = beta_log_kernel_one_large_shape(x, complement_x, a, b, log_x, log_complement);
417 } else {
418 double logarithm;
419 const lmmc_status_t status = lmmc_log_beta(a, b, &logarithm);
420 if (status != LMMC_STATUS_OK) { return status; }
421 value = a * log_x + b * log_complement - logarithm;
422 }
423 if (isnan(value) || value == INFINITY) { return LMMC_STATUS_NUMERICAL_FAILURE; }
424 *out_log_kernel = value;
425 return LMMC_STATUS_OK;
426}
427
428static lmmc_status_t regularized_beta_pair(
429 lmmc_real_t x, lmmc_real_t complement_x,
431 lmmc_real_t log_bt, fraction, result, shape;
432 lmmc_status_t status;
433 const int lower = x * (b + 1.0) < complement_x * (a + 1.0);
434 if (x == 0.5 && a == b) { return store_regularized_probability(0.5, out); }
435 if (!isfinite(a + b)) { return LMMC_STATUS_NUMERICAL_FAILURE; }
436
437 status = lmmc_beta_log_kernel(x, complement_x, a, b, &log_bt);
438 if (status != LMMC_STATUS_OK) { return status; }
439 if (isnan(log_bt) || log_bt > log(DBL_MAX)) { return LMMC_STATUS_NUMERICAL_FAILURE; }
440 if (lower) {
441 status = regularized_beta_cf(x, complement_x, a, b, &fraction);
442 shape = a;
443 } else {
444 status = regularized_beta_cf(complement_x, x, b, a, &fraction);
445 shape = b;
446 }
447 if (status != LMMC_STATUS_OK) { return status; }
449 result = exp(log_bt + log(fraction) - log(shape));
450 if (!isfinite(result) || result < 0.0 || result > 1.0) { return LMMC_STATUS_NUMERICAL_FAILURE; }
451 return store_regularized_probability(lower ? result : 1.0 - result, out);
452}
453
456 if (!out) { return LMMC_STATUS_INVALID_ARGUMENT; }
457 if (x <= 0.0) { return store_regularized_probability(0.0, out); }
458 if (x >= 1.0) { return store_regularized_probability(1.0, out); }
459 return regularized_beta_pair(x, 1.0 - x, a, b, out);
460}
461
464 if (!out) { return LMMC_STATUS_INVALID_ARGUMENT; }
465 if (x <= 0.0) { return store_regularized_probability(1.0, out); }
466 if (x >= 1.0) { return store_regularized_probability(0.0, out); }
467 return regularized_beta_pair(1.0 - x, x, b, a, out);
468}
double lmmc_real_t
Public and internal LMMC real type.
#define LMMC_PI
常量.
#define SPECIAL_FUNCTION_TOLERANCE
lmmc_status_t lmmc_log_beta(double a, double b, double *out_log_beta)
稳定计算 log B(a,b) 和 log(x^a complement_x^b / B(a,b))。 独立传入补数以保留微小上尾;负无穷表示有效下溢,失败时输出保持不变。
lmmc_real_t lmmc_stirling_error(lmmc_real_t x)
lmmc_status_t regularized_gamma_lower(lmmc_real_t a, lmmc_real_t x, lmmc_real_t *out)
正则化不完全伽马函数下尾 P(a, x) = gamma(a, x) / Gamma(a)。 使用级数展开或连分数展开。
lmmc_status_t regularized_gamma_upper_cf(lmmc_real_t a, lmmc_real_t x, lmmc_real_t *out)
正则化不完全伽马函数上尾 Q(a, x) = 1 - P(a, x)。 使用 Lentz 连分数展开。
lmmc_status_t regularized_beta(lmmc_real_t x, lmmc_real_t a, lmmc_real_t b, lmmc_real_t *out)
lmmc_status_t regularized_beta_upper(lmmc_real_t x, lmmc_real_t a, lmmc_real_t b, lmmc_real_t *out)
计算上尾;1 - x 舍入至端点时仍保留 x。
#define LENTZ_TINY
lmmc_real_t lmmc_log_gamma_stirling_error(lmmc_real_t x)
#define SPECIAL_FUNCTION_MAX_ITERATIONS
lmmc_status_t regularized_gamma_upper(lmmc_real_t a, lmmc_real_t x, lmmc_real_t *out)
正则化不完全伽马函数上尾 Q(a, x)。
lmmc_real_t lmmc_deviance_part(lmmc_real_t x, lmmc_real_t mean)
lmmc_status_t lmmc_beta_log_kernel(double x, double complement_x, double a, double b, double *out_log_kernel)
#define LMMC_LOG_SQRT_2PI
#define LMMC_SQRT_2PI
lmmc_status_t
LMMC 库统一返回的状态码.
@ LMMC_STATUS_CONVERGENCE_FAILED
@ LMMC_STATUS_OK
@ LMMC_STATUS_NUMERICAL_FAILURE
@ LMMC_STATUS_INVALID_ARGUMENT