static void acb_hypgeom_mag_Cn(mag_t Cn, int R, const mag_t nu, const mag_t sigma, ulong n) { if (R == 1) { mag_one(Cn); } else { acb_hypgeom_mag_chi(Cn, n); if (R == 3) { mag_t tmp; mag_init(tmp); mag_mul(tmp, nu, nu); mag_mul(tmp, tmp, sigma); if (n != 1) mag_mul_ui(tmp, tmp, n); mag_add(Cn, Cn, tmp); mag_pow_ui(tmp, nu, n); mag_mul(Cn, Cn, tmp); mag_clear(tmp); } } }
void acb_hypgeom_mag_chi(mag_t chi, ulong n) { mag_t p, q; ulong k; mag_init(p); mag_init(q); if (n % 2 == 0) { mag_one(p); mag_one(q); } else { /* upper bound for pi/2 */ mag_set_ui_2exp_si(p, 843314857, -28); mag_one(q); } for (k = n; k >= 2; k -= 2) { mag_mul_ui(p, p, k); mag_mul_ui_lower(q, q, k - 1); } mag_div(chi, p, q); mag_clear(p); mag_clear(q); }
/* The error for eta(s) is bounded by 3/(3+sqrt(8))^n */ void mag_borwein_error(mag_t err, slong n) { /* upper bound for 1/(3+sqrt(8)) */ mag_set_ui_2exp_si(err, 736899889, -32); mag_pow_ui(err, err, n); mag_mul_ui(err, err, 3); }
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); }
/* derivatives: |8/sqrt(pi) sin(2z^2)|, |8/sqrt(pi) cos(2z^2)| <= 5 exp(4|xy|) */ void acb_hypgeom_fresnel_erf_error(acb_t res1, acb_t res2, const acb_t z, slong prec) { mag_t re; mag_t im; acb_t zmid; mag_init(re); mag_init(im); acb_init(zmid); if (arf_cmpabs_ui(arb_midref(acb_realref(z)), 1000) < 0 && arf_cmpabs_ui(arb_midref(acb_imagref(z)), 1000) < 0) { arb_get_mag(re, acb_realref(z)); arb_get_mag(im, acb_imagref(z)); mag_mul(re, re, im); mag_mul_2exp_si(re, re, 2); mag_exp(re, re); mag_mul_ui(re, re, 5); } else { arb_t t; arb_init(t); arb_mul(t, acb_realref(z), acb_imagref(z), prec); arb_abs(t, t); arb_mul_2exp_si(t, t, 2); arb_exp(t, t, prec); arb_get_mag(re, t); mag_mul_ui(re, re, 5); arb_clear(t); } mag_hypot(im, arb_radref(acb_realref(z)), arb_radref(acb_imagref(z))); mag_mul(re, re, im); if (arb_is_zero(acb_imagref(z))) { mag_set_ui(im, 8); /* For real x, |S(x)| < 4, |C(x)| < 4. */ mag_min(re, re, im); mag_zero(im); } else if (arb_is_zero(acb_realref(z))) { mag_set_ui(im, 8); mag_min(im, re, im); mag_zero(re); } else { mag_set(im, re); } arf_set(arb_midref(acb_realref(zmid)), arb_midref(acb_realref(z))); arf_set(arb_midref(acb_imagref(zmid)), arb_midref(acb_imagref(z))); acb_hypgeom_fresnel_erf(res1, res2, zmid, prec); if (res1 != NULL) { arb_add_error_mag(acb_realref(res1), re); arb_add_error_mag(acb_imagref(res1), im); } if (res2 != NULL) { arb_add_error_mag(acb_realref(res2), re); arb_add_error_mag(acb_imagref(res2), im); } mag_clear(re); mag_clear(im); acb_clear(zmid); }
slong hypgeom_bound(mag_t error, int r, slong A, slong B, slong K, const mag_t TK, const mag_t z, slong tol_2exp) { mag_t Tn, t, u, one, tol, num, den; slong n, m; mag_init(Tn); mag_init(t); mag_init(u); mag_init(one); mag_init(tol); mag_init(num); mag_init(den); mag_one(one); mag_set_ui_2exp_si(tol, UWORD(1), -tol_2exp); /* approximate number of needed terms */ n = hypgeom_estimate_terms(z, r, tol_2exp); /* required for 1 + O(1/k) part to be decreasing */ n = FLINT_MAX(n, K + 1); /* required for z^k / (k!)^r to be decreasing */ m = hypgeom_root_bound(z, r); n = FLINT_MAX(n, m); /* We now have |R(k)| <= G(k) where G(k) is monotonically decreasing, and can bound the tail using a geometric series as soon as soon as G(k) < 1. */ /* bound T(n-1) */ hypgeom_term_bound(Tn, TK, K, A, B, r, z, n-1); while (1) { /* bound R(n) */ mag_mul_ui(num, z, n); mag_mul_ui(num, num, n - B); mag_set_ui_lower(den, n - A); mag_mul_ui_lower(den, den, n - 2*B); if (r != 0) { mag_set_ui_lower(u, n); mag_pow_ui_lower(u, u, r); mag_mul_lower(den, den, u); } mag_div(t, num, den); /* multiply bound for T(n-1) by bound for R(n) to bound T(n) */ mag_mul(Tn, Tn, t); /* geometric series termination check */ /* u = max(1-t, 0), rounding down [lower bound] */ mag_sub_lower(u, one, t); if (!mag_is_zero(u)) { mag_div(u, Tn, u); if (mag_cmp(u, tol) < 0) { mag_set(error, u); break; } } /* move on to next term */ n++; } mag_clear(Tn); mag_clear(t); mag_clear(u); mag_clear(one); mag_clear(tol); mag_clear(num); mag_clear(den); return n; }
int main() { slong iter; flint_rand_t state; flint_printf("root_bound_fujiwara...."); fflush(stdout); flint_randinit(state); for (iter = 0; iter < 10000 * arb_test_multiplier(); iter++) { arb_poly_t a; arb_ptr roots; arb_t t; mag_t mag1, mag2; slong i, deg, prec; prec = 10 + n_randint(state, 400); deg = n_randint(state, 10); arb_init(t); arb_poly_init(a); mag_init(mag1); mag_init(mag2); roots = _arb_vec_init(deg); for (i = 0; i < deg; i++) arb_randtest(roots + i, state, prec, 1 + n_randint(state, 20)); arb_poly_product_roots(a, roots, deg, prec); arb_randtest(t, state, prec, 1 + n_randint(state, 20)); arb_poly_scalar_mul(a, a, t, prec); arb_poly_root_bound_fujiwara(mag1, a); for (i = 0; i < deg; i++) { arb_get_mag(mag2, roots + i); /* arb_get_mag gives an upper bound which due to rounding could be larger than mag1, so we pick a slightly smaller number */ mag_mul_ui(mag2, mag2, 10000); mag_div_ui(mag2, mag2, 10001); if (mag_cmp(mag2, mag1) > 0) { flint_printf("FAIL\n"); flint_printf("a = "); arb_poly_printd(a, 15); flint_printf("\n\n"); flint_printf("root = "); arb_printd(roots + i, 15); flint_printf("\n\n"); flint_printf("mag1 = "); mag_printd(mag1, 10); flint_printf("\n\n"); flint_printf("mag2 = "); mag_printd(mag2, 10); flint_printf("\n\n"); abort(); } } _arb_vec_clear(roots, deg); arb_clear(t); arb_poly_clear(a); mag_clear(mag1); mag_clear(mag2); } flint_randclear(state); flint_cleanup(); flint_printf("PASS\n"); return EXIT_SUCCESS; }