178 using fitdetail::Cplx;
179 using fitdetail::cplx_pow_real;
180 using fitdetail::cplx_sqrt;
181 using fitdetail::num_sqrt;
190 if (e1 <= zero)
throw InputError(
"aph_fit: the first moment must be positive");
191 if (nmax < 2)
throw InputError(
"aph_fit: nmax must be at least 2");
195 const T slack = fitdetail::aph_boundary_slack<T>();
197 const T scv = (e2 - e1 * e1) / (e1 * e1);
198 if (
num_abs(T(scv - one)) < tol &&
num_abs(T(e3 - six * pw(e1, 3))) < tol) {
204 T n2 = e2 / (e1 * e1);
207 bool n2_feas =
false, n3_ubfeas =
false, n3_lbfeas =
false;
211 while ((!n2_feas || !n3_lbfeas || !n3_ubfeas) && n < nmax) {
216 const T uarg = fitdetail::read_nonneg_radicand(
217 T(one + nn * (n2 - two) / (nn - one)), slack, T(nn * (n2 - two) / (nn - one)));
218 un = uarg < zero ? zero
219 : (one / (nn * nn * n2)) *
220 (two * (nn - two) * (nn * n2 - nn - one) * num_sqrt(uarg) +
221 (nn + two) * (three * nn * n2 - two * nn - two));
223 if (fitdetail::ge_slack(n2, T((nn + one) / nn), slack) &&
228 const T pn = ((nn + one) * (n2 - two) / (three * n2 * (nn - one))) *
229 (-two * num_sqrt(T(nn + one)) /
233 const T arad = fitdetail::read_nonneg_radicand(
234 T(pn * pn + pn * nn * (n2 - two) / (nn - one)), slack, T(pn * pn));
235 const T an = (n2 - two) / (pn * (one - n2) + num_sqrt(arad));
236 const T
ln = ((three + an) * (nn - one) + two * an) / ((nn - one) * (one + an * pn)) -
237 (two * an * (nn + one)) /
238 (two * (nn - one) + an * pn * (nn * an + two * nn - two));
239 if (fitdetail::ge_slack(n3,
ln, slack)) n3_lbfeas =
true;
243 if (fitdetail::ge_slack(n3, T(n2 * (nn + one) / nn), slack)) n3_lbfeas =
true;
245 if (fitdetail::ge_slack(n2, T((nn + one) / nn), slack) &&
246 fitdetail::le_slack(n2, T(nn / (nn - one)), slack)) {
248 if (fitdetail::le_slack(n3, un, slack)) n3_ubfeas =
true;
249 }
else if (fitdetail::ge_slack(n2, T(nn / (nn - one)), slack)) {
255 if (!n2_feas || !n3_lbfeas || !n3_ubfeas || n == nmax) {
257 n2 = (nn + one) / nn;
265 std::vector<T> alpha(n, zero);
267 const T lambda = one;
269 if (n2 <= nn / (nn - one) || n3 <= two * n2 - one) {
271 const T rad = fitdetail::read_nonneg_radicand(
277 if (rad < zero)
throw NumericError(
"aph_fit: negative discriminant in case 1");
280 const T a = (b * n2 - two) * (nn - one) * b / ((b - one) * nn);
281 if (a == zero)
throw NumericError(
"aph_fit: degenerate case-1 parameter");
282 const T p = (b - one) / a;
283 const T mu = lambda * (nn - one) / a;
285 alpha[n - 1] = one - p;
286 for (std::size_t i = 0; i < n; ++i) Tm(i, i) = -mu;
287 for (std::size_t i = 0; i + 1 < n; ++i) Tm(i, i + 1) = mu;
288 Tm(n - 1, n - 1) = -lambda;
289 }
else if (n2 > nn / (nn - one) && n3 > un_1) {
291 const T K1 = nn - one;
292 const T K2 = nn - two;
293 const T K3 = three * n2 - two * n3;
294 const T K4 = n3 - three;
295 const T K5 = nn - n2;
296 const T K6 = one + n2 - n3;
297 const T K7 = nn + n2 - nn * n2;
298 const T K8 = three + three * n2 * n2 + n3 - three * n2 * n3;
299 if (K3 == zero || K4 == zero)
throw NumericError(
"aph_fit: degenerate case-2 parameter");
302 K1 * K1 * K2 * K4 * K4 * nn * n2 * n2 +
304 (K4 * nn * nn - three * K6 * n2 + K8 * nn);
305 const Cplx<T> K9inner =
309 K1 * K1 * K2 * K4 * K4 * nn * n2 * n2 +
311 (K5 * K5 - three * K2 * K6 * nn * n2)))) *
314 const T third = one / three;
315 const Cplx<T> K9cbrt = cplx_pow_real(K9, third);
316 const T c2 = fitdetail::num_exp(T(third * fitdetail::num_log(two)));
319 fitdetail::cplx_inv(K9cbrt) * T(c2 * (three * K5 * K5 + K2 * (K3 + two * K4) * nn * n2) / (K3 * n2));
320 const Cplx<T> K12 = K9cbrt / T(three * c7 * K1 * K1 * K3 * n2);
321 const Cplx<T> K13 = cplx_sqrt(K11 + K12 + Cplx<T>(K10));
323 K1 * K1 * pw(K4, 3) * n2;
326 const T K15 = -K4 / (two * K3);
327 const Cplx<T> twoK10(T(two * K10));
328 const Cplx<T> K16 = cplx_sqrt(twoK10 - K11 - K12 - K14);
329 const Cplx<T> K17 = cplx_sqrt(twoK10 - K11 - K12 + K14);
332 K1 * K2 * K4 * K4 * nn * n2 * n2;
336 pw(T(three * K5 * K5 + two * K2 * K4 * nn * n2), 3)))) +
340 const Cplx<T> K18cbrt = cplx_pow_real(K18, third);
341 const T c23 = fitdetail::num_exp(T((two / three) * fitdetail::num_log(two)));
342 const T c31 = fitdetail::num_exp(T(third * fitdetail::num_log(three)));
343 const T c62 = fitdetail::num_exp(T((two / three) * fitdetail::num_log(six)));
345 Cplx<T>(T(-K5 / (K1 * K4 * n2))) -
346 fitdetail::cplx_inv(K18cbrt) *
347 T(c23 * (three * K5 * K5 + two * K2 * K4 * nn * n2) / (c31 * K1 * K4 * n2)) -
348 K18cbrt / T(c62 * K1 * K4 * n2);
349 const Cplx<T> K21 = K11 + K12 + Cplx<T>(T(K5 / (two * nn * K1 * K3)));
350 const Cplx<T> K22 = cplx_sqrt(
356 if (n3 > un_1 && n3 < three * n2 / two) {
357 fC = K13 + Cplx<T>(K15) - K17;
359 }
else if (n3 == two * n2 / two) {
362 }
else if (n3 > three * n2 / two && K20 > zero) {
363 fC = -K13 + Cplx<T>(K15) + K16;
365 }
else if (K20 == zero) {
366 fC = K22 + Cplx<T>(K15);
368 }
else if (K20 < zero) {
369 fC = K13 + Cplx<T>(K15) + K17;
372 if (!picked)
throw NumericError(
"aph_fit: no branch of the case-2 selection applies");
374 const T denom = (nn - one) * (n2 * f * f - two * f + two) - nn;
375 if (denom == zero)
throw NumericError(
"aph_fit: degenerate case-2 denominator");
376 const T a = two * (f - one) * (nn - one) / denom;
377 if (a == zero)
throw NumericError(
"aph_fit: degenerate case-2 parameter");
378 const T p = (f - one) * a;
379 const T mu = lambda * (nn - one) / a;
382 for (std::size_t i = 0; i < n; ++i) Tm(i, i) = -mu;
383 for (std::size_t i = 0; i + 1 < n; ++i) Tm(i, i + 1) = mu;
387 throw NumericError(
"aph_fit: the moment set cannot be matched with an APH distribution");
390 res.
aph = fitdetail::aph_canonical(Tm, alpha, e1);