void acb_hypgeom_erf_propagated_error(mag_t re, mag_t im, const acb_t z) { mag_t x, y; mag_init(x); mag_init(y); /* |exp(-(x+y)^2)| = exp(y^2-x^2) */ arb_get_mag(y, acb_imagref(z)); mag_mul(y, y, y); arb_get_mag_lower(x, acb_realref(z)); mag_mul_lower(x, x, x); if (mag_cmp(y, x) >= 0) { mag_sub(re, y, x); mag_exp(re, re); } else { mag_sub_lower(re, x, y); mag_expinv(re, re); } /* Radius. */ mag_hypot(x, arb_radref(acb_realref(z)), arb_radref(acb_imagref(z))); mag_mul(re, re, x); /* 2/sqrt(pi) < 289/256 */ mag_mul_ui(re, re, 289); mag_mul_2exp_si(re, re, -8); if (arb_is_zero(acb_imagref(z))) { /* todo: could bound magnitude even for complex numbers */ mag_set_ui(y, 2); mag_min(re, re, y); mag_zero(im); } else if (arb_is_zero(acb_realref(z))) { mag_swap(im, re); mag_zero(re); } else { mag_set(im, re); } mag_clear(x); mag_clear(y); }
void arb_sinc(arb_t z, const arb_t x, slong prec) { mag_t c, r; mag_init(c); mag_init(r); mag_set_ui_2exp_si(c, 5, -1); arb_get_mag_lower(r, x); if (mag_cmp(c, r) < 0) { /* x is not near the origin */ _arb_sinc_direct(z, x, prec); } else if (mag_cmp_2exp_si(arb_radref(x), 1) < 0) { /* determine error magnitude using the derivative bound */ if (arb_is_exact(x)) { mag_zero(c); } else { _arb_sinc_derivative_bound(r, x); mag_mul(c, arb_radref(x), r); } /* evaluate sinc at the midpoint of x */ if (arf_is_zero(arb_midref(x))) { arb_one(z); } else { arb_get_mid_arb(z, x); _arb_sinc_direct(z, z, prec); } /* add the error */ mag_add(arb_radref(z), arb_radref(z), c); } else { /* x has a large radius and includes points near the origin */ arf_zero(arb_midref(z)); mag_one(arb_radref(z)); } mag_clear(c); mag_clear(r); }
void arb_root_ui_algebraic(arb_t res, const arb_t x, ulong k, slong prec) { mag_t r, msubr, m1k, t; if (arb_is_exact(x)) { arb_root_arf(res, arb_midref(x), k, prec); return; } if (!arb_is_nonnegative(x)) { arb_indeterminate(res); return; } mag_init(r); mag_init(msubr); mag_init(m1k); mag_init(t); /* x = [m-r, m+r] */ mag_set(r, arb_radref(x)); /* m - r */ arb_get_mag_lower(msubr, x); /* m^(1/k) */ arb_root_arf(res, arb_midref(x), k, prec); /* bound for m^(1/k) */ arb_get_mag(m1k, res); /* C = min(1, log(1+r/(m-r))/k) */ mag_div(t, r, msubr); mag_log1p(t, t); mag_div_ui(t, t, k); if (mag_cmp_2exp_si(t, 0) > 0) mag_one(t); /* C m^(1/k) */ mag_mul(t, m1k, t); mag_add(arb_radref(res), arb_radref(res), t); mag_clear(r); mag_clear(msubr); mag_clear(m1k); mag_clear(t); }
void arb_div(arb_t z, const arb_t x, const arb_t y, long prec) { mag_t zr, xm, ym, yl, yw; int inexact; if (arb_is_exact(y)) { arb_div_arf(z, x, arb_midref(y), prec); } else if (mag_is_inf(arb_radref(x)) || mag_is_inf(arb_radref(y))) { arf_div(arb_midref(z), arb_midref(x), arb_midref(y), prec, ARB_RND); mag_inf(arb_radref(z)); } else { mag_init_set_arf(xm, arb_midref(x)); mag_init_set_arf(ym, arb_midref(y)); mag_init(zr); mag_init(yl); mag_init(yw); /* (|x|*yrad + |y|*xrad)/(y*(|y|-yrad)) */ mag_mul(zr, xm, arb_radref(y)); mag_addmul(zr, ym, arb_radref(x)); arb_get_mag_lower(yw, y); arf_get_mag_lower(yl, arb_midref(y)); mag_mul_lower(yl, yl, yw); mag_div(zr, zr, yl); inexact = arf_div(arb_midref(z), arb_midref(x), arb_midref(y), prec, ARB_RND); if (inexact) arf_mag_add_ulp(arb_radref(z), zr, arb_midref(z), prec); else mag_swap(arb_radref(z), zr); mag_clear(xm); mag_clear(ym); mag_clear(zr); mag_clear(yl); mag_clear(yw); } }
void arb_atan(arb_t z, const arb_t x, slong prec) { if (arb_is_exact(x)) { arb_atan_arf(z, arb_midref(x), prec); } else { mag_t t, u; mag_init(t); mag_init(u); arb_get_mag_lower(t, x); if (mag_is_zero(t)) { mag_set(t, arb_radref(x)); } else { mag_mul_lower(t, t, t); mag_one(u); mag_add_lower(t, t, u); mag_div(t, arb_radref(x), t); } if (mag_cmp_2exp_si(t, 0) > 0) { mag_const_pi(u); mag_min(t, t, u); } arb_atan_arf(z, arb_midref(x), prec); mag_add(arb_radref(z), arb_radref(z), t); mag_clear(t); mag_clear(u); } }
int main() { slong iter; flint_rand_t state; flint_printf("frobenius_norm...."); fflush(stdout); flint_randinit(state); /* compare to the exact rational norm */ for (iter = 0; iter < 10000 * arb_test_multiplier(); iter++) { fmpq_mat_t Q; fmpq_t q; arb_mat_t A; slong n, qbits, prec; n = n_randint(state, 8); qbits = 1 + n_randint(state, 100); prec = 2 + n_randint(state, 200); fmpq_mat_init(Q, n, n); fmpq_init(q); arb_mat_init(A, n, n); fmpq_mat_randtest(Q, state, qbits); _fmpq_mat_sum_of_squares(q, Q); arb_mat_set_fmpq_mat(A, Q, prec); /* check that the arb interval contains the exact value */ { arb_t a; arb_init(a); arb_mat_frobenius_norm(a, A, prec); arb_mul(a, a, a, prec); if (!arb_contains_fmpq(a, q)) { flint_printf("FAIL (containment, iter = %wd)\n", iter); flint_printf("n = %wd, prec = %wd\n", n, prec); flint_printf("\n"); flint_printf("Q = \n"); fmpq_mat_print(Q); flint_printf("\n\n"); flint_printf("frobenius_norm(Q)^2 = \n"); fmpq_print(q); flint_printf("\n\n"); flint_printf("A = \n"); arb_mat_printd(A, 15); flint_printf("\n\n"); flint_printf("frobenius_norm(A)^2 = \n"); arb_printd(a, 15); flint_printf("\n\n"); flint_printf("frobenius_norm(A)^2 = \n"); arb_print(a); flint_printf("\n\n"); abort(); } arb_clear(a); } /* check that the upper bound is not less than the exact value */ { mag_t b; fmpq_t y; mag_init(b); fmpq_init(y); arb_mat_bound_frobenius_norm(b, A); mag_mul(b, b, b); mag_get_fmpq(y, b); if (fmpq_cmp(q, y) > 0) { flint_printf("FAIL (bound, iter = %wd)\n", iter); flint_printf("n = %wd, prec = %wd\n", n, prec); flint_printf("\n"); flint_printf("Q = \n"); fmpq_mat_print(Q); flint_printf("\n\n"); flint_printf("frobenius_norm(Q)^2 = \n"); fmpq_print(q); flint_printf("\n\n"); flint_printf("A = \n"); arb_mat_printd(A, 15); flint_printf("\n\n"); flint_printf("bound_frobenius_norm(A)^2 = \n"); mag_printd(b, 15); flint_printf("\n\n"); flint_printf("bound_frobenius_norm(A)^2 = \n"); mag_print(b); flint_printf("\n\n"); abort(); } mag_clear(b); fmpq_clear(y); } fmpq_mat_clear(Q); fmpq_clear(q); arb_mat_clear(A); } /* check trace(A^T A) = frobenius_norm(A)^2 */ for (iter = 0; iter < 10000 * arb_test_multiplier(); iter++) { slong m, n, prec; arb_mat_t A, AT, ATA; arb_t t; prec = 2 + n_randint(state, 200); m = n_randint(state, 10); n = n_randint(state, 10); arb_mat_init(A, m, n); arb_mat_init(AT, n, m); arb_mat_init(ATA, n, n); arb_init(t); arb_mat_randtest(A, state, 2 + n_randint(state, 100), 10); arb_mat_transpose(AT, A); arb_mat_mul(ATA, AT, A, prec); arb_mat_trace(t, ATA, prec); arb_sqrt(t, t, prec); /* check the norm bound */ { mag_t low, frobenius; mag_init(low); arb_get_mag_lower(low, t); mag_init(frobenius); arb_mat_bound_frobenius_norm(frobenius, A); if (mag_cmp(low, frobenius) > 0) { flint_printf("FAIL (bound)\n", iter); flint_printf("m = %wd, n = %wd, prec = %wd\n", m, n, prec); flint_printf("\n"); flint_printf("A = \n"); arb_mat_printd(A, 15); flint_printf("\n\n"); flint_printf("lower(sqrt(trace(A^T A))) = \n"); mag_printd(low, 15); flint_printf("\n\n"); flint_printf("bound_frobenius_norm(A) = \n"); mag_printd(frobenius, 15); flint_printf("\n\n"); abort(); } mag_clear(low); mag_clear(frobenius); } /* check the norm interval */ { arb_t frobenius; arb_init(frobenius); arb_mat_frobenius_norm(frobenius, A, prec); if (!arb_overlaps(t, frobenius)) { flint_printf("FAIL (overlap)\n", iter); flint_printf("m = %wd, n = %wd, prec = %wd\n", m, n, prec); flint_printf("\n"); flint_printf("A = \n"); arb_mat_printd(A, 15); flint_printf("\n\n"); flint_printf("sqrt(trace(A^T A)) = \n"); arb_printd(t, 15); flint_printf("\n\n"); flint_printf("frobenius_norm(A) = \n"); arb_printd(frobenius, 15); flint_printf("\n\n"); abort(); } arb_clear(frobenius); } arb_mat_clear(A); arb_mat_clear(AT); arb_mat_clear(ATA); arb_clear(t); } flint_randclear(state); flint_cleanup(); flint_printf("PASS\n"); return EXIT_SUCCESS; }
void acb_inv(acb_t res, const acb_t z, slong prec) { mag_t am, bm; slong hprec; #define a arb_midref(acb_realref(z)) #define b arb_midref(acb_imagref(z)) #define x arb_radref(acb_realref(z)) #define y arb_radref(acb_imagref(z)) /* choose precision for the floating-point approximation of a^2+b^2 so that the double rounding result in less than 2 ulp error; also use at least MAG_BITS bits since the value will be recycled for error bounds */ hprec = FLINT_MAX(prec + 3, MAG_BITS); if (arb_is_zero(acb_imagref(z))) { arb_inv(acb_realref(res), acb_realref(z), prec); arb_zero(acb_imagref(res)); return; } if (arb_is_zero(acb_realref(z))) { arb_inv(acb_imagref(res), acb_imagref(z), prec); arb_neg(acb_imagref(res), acb_imagref(res)); arb_zero(acb_realref(res)); return; } if (!acb_is_finite(z)) { acb_indeterminate(res); return; } if (mag_is_zero(x) && mag_is_zero(y)) { int inexact; arf_t a2b2; arf_init(a2b2); inexact = arf_sosq(a2b2, a, b, hprec, ARF_RND_DOWN); if (arf_is_special(a2b2)) { acb_indeterminate(res); } else { _arb_arf_div_rounded_den(acb_realref(res), a, a2b2, inexact, prec); _arb_arf_div_rounded_den(acb_imagref(res), b, a2b2, inexact, prec); arf_neg(arb_midref(acb_imagref(res)), arb_midref(acb_imagref(res))); } arf_clear(a2b2); return; } mag_init(am); mag_init(bm); /* first bound |a|-x, |b|-y */ arb_get_mag_lower(am, acb_realref(z)); arb_get_mag_lower(bm, acb_imagref(z)); if ((mag_is_zero(am) && mag_is_zero(bm))) { acb_indeterminate(res); } else { /* The propagated error in the real part is given exactly by (a+x')/((a+x')^2+(b+y'))^2 - a/(a^2+b^2) = P / Q, P = [(b^2-a^2) x' - a (x'^2+y'^2 + 2y'b)] Q = [(a^2+b^2)((a+x')^2+(b+y')^2)] where |x'| <= x and |y'| <= y, and analogously for the imaginary part. */ mag_t t, u, v, w; arf_t a2b2; int inexact; mag_init(t); mag_init(u); mag_init(v); mag_init(w); arf_init(a2b2); inexact = arf_sosq(a2b2, a, b, hprec, ARF_RND_DOWN); /* compute denominator */ /* t = (|a|-x)^2 + (|b|-x)^2 (lower bound) */ mag_mul_lower(t, am, am); mag_mul_lower(u, bm, bm); mag_add_lower(t, t, u); /* u = a^2 + b^2 (lower bound) */ arf_get_mag_lower(u, a2b2); /* t = ((|a|-x)^2 + (|b|-x)^2)(a^2 + b^2) (lower bound) */ mag_mul_lower(t, t, u); /* compute numerator */ /* real: |a^2-b^2| x + |a| ((x^2 + y^2) + 2 |b| y)) */ /* imag: |a^2-b^2| y + |b| ((x^2 + y^2) + 2 |a| x)) */ /* am, bm = upper bounds for a, b */ arf_get_mag(am, a); arf_get_mag(bm, b); /* v = x^2 + y^2 */ mag_mul(v, x, x); mag_addmul(v, y, y); /* u = |a| ((x^2 + y^2) + 2 |b| y) */ mag_mul_2exp_si(u, bm, 1); mag_mul(u, u, y); mag_add(u, u, v); mag_mul(u, u, am); /* v = |b| ((x^2 + y^2) + 2 |a| x) */ mag_mul_2exp_si(w, am, 1); mag_addmul(v, w, x); mag_mul(v, v, bm); /* w = |b^2 - a^2| (upper bound) */ if (arf_cmpabs(a, b) >= 0) mag_mul(w, am, am); else mag_mul(w, bm, bm); mag_addmul(u, w, x); mag_addmul(v, w, y); mag_div(arb_radref(acb_realref(res)), u, t); mag_div(arb_radref(acb_imagref(res)), v, t); _arb_arf_div_rounded_den_add_err(acb_realref(res), a, a2b2, inexact, prec); _arb_arf_div_rounded_den_add_err(acb_imagref(res), b, a2b2, inexact, prec); arf_neg(arb_midref(acb_imagref(res)), arb_midref(acb_imagref(res))); mag_clear(t); mag_clear(u); mag_clear(v); mag_clear(w); arf_clear(a2b2); } mag_clear(am); mag_clear(bm); #undef a #undef b #undef x #undef y }
/* computes the factors that are independent of n (all are upper bounds) */ void acb_hypgeom_u_asymp_bound_factors(int * R, mag_t alpha, mag_t nu, mag_t sigma, mag_t rho, mag_t zinv, const acb_t a, const acb_t b, const acb_t z) { mag_t r, u, zre, zim, zlo, sigma_prime; acb_t t; mag_init(r); mag_init(u); mag_init(zre); mag_init(zim); mag_init(zlo); mag_init(sigma_prime); acb_init(t); /* lower bounds for |re(z)|, |im(z)|, |z| */ arb_get_mag_lower(zre, acb_realref(z)); arb_get_mag_lower(zim, acb_imagref(z)); acb_get_mag_lower(zlo, z); /* todo: hypot */ /* upper bound for 1/|z| */ mag_one(u); mag_div(zinv, u, zlo); /* upper bound for r = |b - 2a| */ acb_mul_2exp_si(t, a, 1); acb_sub(t, b, t, MAG_BITS); acb_get_mag(r, t); /* determine region */ *R = 0; if (mag_cmp(zlo, r) >= 0) { int znonneg = arb_is_nonnegative(acb_realref(z)); if (znonneg && mag_cmp(zre, r) >= 0) { *R = 1; } else if (mag_cmp(zim, r) >= 0 || znonneg) { *R = 2; } else { mag_mul_2exp_si(u, r, 1); if (mag_cmp(zlo, u) >= 0) *R = 3; } } if (R == 0) { mag_inf(alpha); mag_inf(nu); mag_inf(sigma); mag_inf(rho); } else { /* sigma = |(b-2a)/z| */ mag_mul(sigma, r, zinv); /* nu = (1/2 + 1/2 sqrt(1-4 sigma^2))^(-1/2) <= 1 + 2 sigma^2 */ if (mag_cmp_2exp_si(sigma, -1) <= 0) { mag_mul(nu, sigma, sigma); mag_mul_2exp_si(nu, nu, 1); mag_one(u); mag_add(nu, nu, u); } else { mag_inf(nu); } /* modified sigma for alpha, beta, rho when in R3 */ if (*R == 3) mag_mul(sigma_prime, sigma, nu); else mag_set(sigma_prime, sigma); /* alpha = 1/(1-sigma') */ mag_one(alpha); mag_sub_lower(alpha, alpha, sigma_prime); mag_one(u); mag_div(alpha, u, alpha); /* rho = |2a^2-2ab+b|/2 + sigma'*(1+sigma'/4)/(1-sigma')^2 */ mag_mul_2exp_si(rho, sigma_prime, -2); mag_one(u); mag_add(rho, rho, u); mag_mul(rho, rho, sigma_prime); mag_mul(rho, rho, alpha); mag_mul(rho, rho, alpha); acb_sub(t, a, b, MAG_BITS); acb_mul(t, t, a, MAG_BITS); acb_mul_2exp_si(t, t, 1); acb_add(t, t, b, MAG_BITS); acb_get_mag(u, t); mag_mul_2exp_si(u, u, -1); mag_add(rho, rho, u); } mag_clear(r); mag_clear(u); mag_clear(zre); mag_clear(zim); mag_clear(zlo); mag_clear(sigma_prime); acb_clear(t); }