Sieved power sums with dfloat balls
#include <flint/dfloat.h>int gr_powsum_sieved(acb_t res, const acb_t s, ulong N, gr_ctx_t T, gr_ctx_t P, gr_ctx_t F, double dm, double da, ulong M)sourceSets res to an enclosure of , as acb_dirichlet_powsum_sieved with len = 1, computed with the ball ring for the terms, the ball ring for the phases and optionally the plain ring for the composite terms and block sums (NULL: all balls), whose products and sums have relative errors at most dm and da. M is the table bound (0 for the default). Returns a gr status.
The dfloat instantiations of gr_powsum_sieved, with terms in dTb and phases in dPb for = (tn, pn) one of , , , , . dfloat_powsum_sieved chooses and for about prec bits after the binary point. They return 1 on success and 0, leaving res undefined, for unsupported arguments.
These functions compute the partial sums
that make up the main sum of the Riemann–Siegel formula for at large height . gr_powsum_sieved is a generic implementation over three rings given at run time, and dfloat_powsum_sieved instantiates it with dfloat balls and plain expansions. acb_dirichlet_zeta_rs_r calls dfloat_powsum_sieved for and falls back to acb_dirichlet_powsum_sieved when it returns 0; this makes the Riemann–Siegel evaluations used to isolate zeros 17 to 18 times faster at the heights of the -th to -th zeros.
The algorithm#
The algorithm is that of acb_dirichlet_powsum_sieved. The function is completely multiplicative, so the term of a composite is the product with the smallest prime factor of , taken from a table of for odd ; only the prime terms need logarithms, sines, cosines and exponentials. The even are handled by Horner's rule in over the dyadic ranges of the odd part.
The implementation rearranges this so that all the bulk work goes through vector operations of the generic rings:
- the odd numbers are processed in blocks of 4096 with a segmented sieve that carries, for each small prime, its next multiple and the cofactor of that multiple, so that no divisions are needed;
- the primes of a block are evaluated together:
_gr_vec_login the phase ring , the phase reduced by subtracting a nearby integer exactly, rounded to the term ring with_gr_vec_set_other, then_gr_vec_sin_cos,_gr_vec_expand_gr_vec_mul; on the critical line the magnitudes come from_gr_vec_rsqrtinstead of the exponential; - the composite terms of a block are gathered from the table with
_gr_vec_gather(real and imaginary parts interleaved, so that a lookup touches one cache line), multiplied with four_gr_vec_mul, and scattered back with_gr_vec_scatter; - the terms of each dyadic range are summed pairwise with
_gr_vec_add.
The phase ring needs about more bits than the term ring , because the phase is reduced modulo .
Bounded memory: the two-phase version#
The table of for takes bytes per double of the table ring, which limits the plain algorithm. With a table bound , the odd are processed as above, and among the odd only the roots are computed: the primes and the composites with cofactor . Every whose odd part exceeds is uniquely with a root and an integer whose odd part has no prime factor above (the chain of divisions of by its smallest prime factor reaches as its last element above ). Hence
where is the sum of over the whose odd part has no prime factor above , and for a prime . With , the values of form a table of about entries. The second phase gathers for each root and needs one complex product per root; all other composites (all but a fraction of about ) are never computed.
The default table bound (M = 0) is the full table for and otherwise. Besides bounding the memory by , this is faster, since the table fits in the cache and fewer products are needed: at , with , the time per term drops from 24.6 to 15.8 ns and the memory from 262 MB to 11 MB.
The second phase, all but of the work, is split into chunks of at least odd numbers, each with its own sieve state, and run with flint_parallel_do on flint_get_num_threads threads. The partial sums are added in the order of the chunks, so the result does not depend on the number of threads.
Error bounds#
With F = NULL, every term is a ball and the result is rigorous by construction. With a plain ring , only the prime terms are balls, and the error of the composite terms and block sums is bounded a priori and added per dyadic range. With the largest relative radius of a prime term (obtained with _gr_vec_get_interval_mid_rad) and the relative error of a plain complex product, a term with prime factors has a relative error of at most , where ; the pairwise sums of at most 4096 terms add times the sum of the magnitudes, which is bounded by an integral. In the two-phase version, the sum over the roots of times the sum of over the corresponding is exactly the sum of over the with odd part above , so the bound is as tight as in the first phase.
The dfloat instantiation uses for d1 and , for d2, , from the error analysis of the kernels (Double-word and multi-double arithmetic); the phase context is strong (canonical), so that rounding the reduced phases to is a truncation.
Choosing the precisions#
dfloat_powsum_sieved chooses so that the terms have bits (the accumulated error grows like , so that the result has about prec bits after the binary point), and so that the phase leaves bits after the binary point. With , a phase short by up to 8 bits is accepted. It returns 0 if no pair in fits, if , or , if the tables would take more than DFLOAT_POWSUM_MAX_BYTES, or if dfloat_is_supported returns 0. The plain composites are used for ; _dfloat_powsum_sieved_ball keeps every term a ball (and is the only variant for ): its radii are about 10 times smaller and it is 20 to 30% slower.
Performance#
At the height of the -th zero (, , one core), acb_dirichlet_powsum_sieved at 179 bits takes 2.4 s, while the full-table dfloat version takes 0.13 s with (radius ), 0.27 s with (radius ) and 0.60 s with (radius ); the default two-phase version is another 25 to 30% faster. About half of the time goes to the prime terms, a quarter to the composites (dominated by cache misses of the table lookups), and the rest to the sieve and the sums. At low heights the fixed setup cost of a few microseconds makes the arb_t version faster for , which is why acb_dirichlet_zeta_rs_r uses the dfloat version only from on.
The FLINT team, “Sieved power sums with dfloat balls (dfloat.h),” FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev, 2026. https://flintlib.org/doc/dfloat/powsum.html (generated 2026-11-02).
@misc{flint-doc-dfloat-powsum,
author = {{The FLINT team}},
title = {{Sieved power sums with dfloat balls (dfloat.h)}},
howpublished = {FLINT: Fast Library for Number Theory, documentation, version 3.7.0-dev},
year = {2026},
url = {https://flintlib.org/doc/dfloat/powsum.html},
note = {Generated 2026-11-02}
}