diff --git a/CMakeLists.txt b/CMakeLists.txt index c2053e26fb..f91484094c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -205,7 +205,7 @@ set(_BUILD_DIRS acb_mat acb_poly acb_calc acb_hypgeom arb_fmpz_poly arb_fpwrap acb_dft acb_elliptic acb_modular acb_dirichlet - dirichlet bernoulli hypgeom + acb_theta dirichlet bernoulli hypgeom gr gr_generic gr_vec gr_mat gr_poly gr_mpoly gr_special diff --git a/Makefile.in b/Makefile.in index b962a10670..4052880316 100644 --- a/Makefile.in +++ b/Makefile.in @@ -179,7 +179,7 @@ HEADER_DIRS := \ acb_mat acb_poly acb_calc acb_hypgeom \ arb_fmpz_poly arb_fpwrap \ acb_dft acb_elliptic acb_modular acb_dirichlet \ - dirichlet bernoulli hypgeom \ + acb_theta dirichlet bernoulli hypgeom \ \ gr gr_generic gr_vec gr_mat \ gr_poly gr_mpoly gr_special \ diff --git a/doc/source/acb_theta.rst b/doc/source/acb_theta.rst new file mode 100644 index 0000000000..d4bb6bb32c --- /dev/null +++ b/doc/source/acb_theta.rst @@ -0,0 +1,2198 @@ +.. _acb-theta: + +**acb_theta.h** -- Riemann theta functions +=============================================================================== + +This module provides methods for the numerical evaluation of theta functions in +any dimension `g\geq 1`. The algorithms will be detailed in the forthcoming paper +[EK2023]_. In the case `g=1`, we rely on, but also improve on functionality +from :ref:`acb_modular.h `. + +In the context of this module, *tau* or `\tau` always denotes an element of the +Siegel upper half-space `\mathbb{H}_g`, which consists of all symmetric +`g\times g` complex matrices with positive definite imaginary part. The letter +`z` denotes an element of `\mathbb{C}^g`. For each `a,b\in \{0,1\}^g`, the +Riemann theta function of characteristic `(a,b)` is the following analytic +function in `\tau\in \mathbb{H}_g` and `z\in \mathbb{C}^g`: + + .. math :: + + \theta_{a,b}(z,\tau) = \sum_{n\in \mathbb{Z}^{g} + \tfrac a2} \exp(\pi i n^T\tau n + 2\pi i n^T (z + \tfrac b2)), + +considering `a`, `b` and `z` as column vectors. + +We encode a theta characteristic `a\in \{0,1\}^g` as the :type:`ulong` between +`0` and `2^g-1` that has the corresponding expansion in base 2: thus `a = +(1,0,0)` for `g = 3` will be numbered `8`. We also use this encoding to order +vectors of theta values throughout. Similarly, a pair of characteristics +`(a,b)` is encoded as an :type:`ulong` between `0` and `2^{2g}-1`, where `a` +corresponds to the `g` more significant bits. With these conventions, the +output of :func:`acb_modular_theta` is +`(-\theta_3,\theta_2,\theta_0,\theta_1)`. + +The main user-facing function to evaluate theta functions is +:func:`acb_theta_all`. This function first reduces the input `(z,\tau)` using +the action of the Siegel modular group `\mathrm{Sp}_{2g}(\mathbb{Z})` on +`\mathbb{C}^g\times \mathbb{H}_g`, then uses a quasi-linear algorithm to +compute theta values on the reduced domain. At low precisions and when `\tau` +is reasonably reduced, one may also consider using "naive algorithms" directly, +which consist in evaluating a partial sum of the theta series. The main +functions to do so are :func:`acb_theta_naive_fixed_ab` and +:func:`acb_theta_naive_all`. We also provide functionality to evaluate +derivatives of theta functions, and to evaluate Siegel modular forms in terms +of theta functions when `g=2`. + +The numerical functions in this module compute certified error bounds: for +instance, if `\tau` is represented by an :type:`acb_mat_t` which is not +certainly positive definite at the chosen working precision, the output will +have an infinite radius. Throughout, `g` must be at least 1 (this is not +checked.) + +Main user functions +------------------------------------------------------------------------------- + +.. function:: void acb_theta_all(acb_ptr th, acb_srcptr z, const acb_mat_t tau, int sqr, slong prec) + + Sets *th* to the vector of theta values `\theta_{a,b}(z,\tau)` or + `\theta_{a,b}(z,\tau)^2` for all `a,b\in \{0,1\}^g`, depending on whether + *sqr* is 0 (false) or nonzero (true). + +.. function:: void acb_theta_naive_fixed_ab(acb_ptr th, ulong ab, acb_srcptr zs, slong nb, const acb_mat_t tau, slong prec) + +.. function:: void acb_theta_naive_all(acb_ptr th, acb_srcptr zs, slong nb, const acb_mat_t tau, slong prec) + + Assuming that *zs* is the concatenation of *nb* vectors `z` of length `g`, + evaluates `\theta_{a,b}(z, \tau)` using the naive algorithm, for either the + given value of `(a,b)` or all `(a,b)\in\{0,1\}^{2g}`. The result *th* will + be a concatenation of *nb* vectors of length `1` or `2^{2g}` + respectively. The user should ensure that `\tau` is reasonably reduced + before calling these functions. + +.. function:: void acb_theta_jet_all(acb_ptr dth, acb_srcptr z, const acb_mat_t tau, slong ord, slong prec) + + Sets *dth* to the partial derivatives with respect to `z` up to total order + *ord* of all functions `\theta_{a,b}` for `a,b\in \{0,1\}^g` at the given + point `(z,\tau)`, as a concatenation of `2^{2g}` vectors. (See below for + conventions on the numbering and normalization of derivatives.) + +.. function:: void acb_theta_jet_naive_fixed_ab(acb_ptr dth, ulong ab, acb_srcptr z, const acb_mat_t tau, slong ord, slong prec) + +.. function:: void acb_theta_jet_naive_all(acb_ptr dth, acb_srcptr z, const acb_mat_t tau, slong ord, slong prec) + + Sets *dth* to the partial derivatives with respect to `z` up to total order + *ord* of `\theta_{a,b}` for the given (resp. all) `(a,b)\in \{0,1\}^g` at + the given point `(z,\tau)` using the naive algorithm. The user should + ensure that `\tau` is reasonably reduced before calling these functions. + +Example of usage +------------------------------------------------------------------------------- + +The following code snippet constructs the period matrix `\tau = iI_2` for `g = +2`, computes the associated theta values at `z = 0` at 10000 bits of precision +in roughly 0.1s, and prints them. + +.. code-block:: c + + #include "acb_theta.h" + + int main() + { + acb_mat_t tau; + acb_ptr th, z; + slong prec = 10000; + + acb_mat_init(tau, 2, 2); + z = _acb_vec_init(2); + th = _acb_vec_init(16); + + acb_mat_onei(tau); + acb_theta_all(th, z, tau, 0, prec); + _acb_vec_printd(th, 16, 5); + + acb_mat_clear(tau); + _acb_vec_clear(z, 2); + _acb_vec_clear(th, 16); + flint_cleanup(); + return 0; + } + +:: + + (1.1803 + 0j) +/- (2.23e-3010, 1.23e-3010j), (0.99254 + 0j) +/- (1.73e-3010, 1.23e-3010j), (0.99254 + 0j) +/- (1.73e-3010, 1.23e-3010j), (0.83463 + 0j) +/- (1.73e-3010, 1.23e-3010j), (0.99254 + 0j) +/- (1.73e-3010, 1.23e-3010j), (0 + 0j) +/- (1.23e-3010, 1.23e-3010j), (0.83463 + 0j) +/- (1.73e-3010, 1.23e-3010j), (0 + 0j) +/- (1.23e-3010, 1.23e-3010j), (0.99254 + 0j) +/- (1.73e-3010, 1.23e-3010j), (0.83463 + 0j) +/- (1.73e-3010, 1.23e-3010j), (0 + 0j) +/- (1.23e-3010, 1.23e-3010j), (0 + 0j) +/- (1.23e-3010, 1.23e-3010j), (0.83463 + 0j) +/- (1.73e-3010, 1.23e-3010j), (0 + 0j) +/- (1.23e-3010, 1.23e-3010j), (0 + 0j) +/- (1.23e-3010, 1.23e-3010j), (0 + 0j) +/- (1.23e-3010, 1.23e-3010j) + + +The Siegel modular group +------------------------------------------------------------------------------- + +We use the type :type:`fmpz_mat_t` to handle matrices in +`\operatorname{Sp}_{2g}(\mathbb{Z})`. In addition to the functions in this +section, methods from :ref:`fmpz_mat.h ` such as +:func:`fmpz_mat_equal` can thus be used on symplectic matrices directly. + +In the following functions (with the exception of :func:`sp2gz_is_correct`) we +always assume that the input matrix *mat* is square of even size `2g`, and +write it as + + .. math :: + + m = \begin{pmatrix} \alpha&\beta\\ \gamma&\delta \end{pmatrix} + +where `\alpha,\beta,\gamma,\delta` are `g\times g` blocks. + +.. function:: slong sp2gz_dim(const fmpz_mat_t mat) + + Returns `g`, which is half the number of rows (or columns) of *mat*. + +.. function:: void sp2gz_set_blocks(fmpz_mat_t mat, const fmpz_mat_t alpha, const fmpz_mat_t beta, const fmpz_mat_t gamma, const fmpz_mat_t delta) + + Sets *mat* to `\bigl(\begin{smallmatrix} \alpha&\beta\\ \gamma&\delta + \end{smallmatrix}\bigr)`. The dimensions must match. + +.. function:: void sp2gz_j(fmpz_mat_t mat) + + Sets *mat* to the symplectic matrix `J = \Bigl(\begin{smallmatrix} + 0&I_g\\-I_g&0 \end{smallmatrix}\Bigr)`. + +.. function:: void sp2gz_block_diag(fmpz_mat_t mat, const fmpz_mat_t U) + + Sets *mat* to the symplectic matrix `\Bigl(\begin{smallmatrix} + U&0\\0&U^{-T} \end{smallmatrix}\Bigr)`. We require that `U\in + \operatorname{GL}_g(\mathbb{Z})`. + +.. function:: void sp2gz_trig(fmpz_mat_t mat, const fmpz_mat_t S) + + Sets *mat* to `\Bigl(\begin{smallmatrix} I_g&S\\0&I_g + \end{smallmatrix}\Bigr)`, where *S* is a symmetric `g\times g` matrix. + +.. function:: void sp2gz_embed(fmpz_mat_t res, const fmpz_mat_t mat) + + Assuming that *mat* is a symplectic matrix of size `2r\times 2r` and *res* + is square of size `2g\times 2g` for some `g\geq r`, sets *res* to the symplectic matrix + + .. math :: + + \begin{pmatrix} \alpha && \beta & \\ & I_{g-r} && 0_{g-r} \\ \gamma &&\delta &\\ & 0_{g-r} && I_{g-r} \end{pmatrix} + + where `\alpha,\beta,\gamma,\delta` are the `r\times r` blocks of *mat*. + +.. function:: void sp2gz_restrict(fmpz_mat_t res, const fmpz_mat_t mat) + + Assuming that *mat* is a symplectic matrix of size `2g\times 2g` and *res* + is square of size `2r\times 2r` for some `r\leq g`, sets *res* to the + matrix whose `r\times r` blocks are the upper left corners of the + corresponding `g\times g` block of *mat*. The result may not be a + symplectic matrix. + +.. function:: slong sp2gz_nb_fundamental(slong g) + + Returns the number of fundamental symplectic matrices used in the reduction + algorithm on `\mathbb{H}_g`. This number is 1 when `g=1` (the `J` matrix) + and 19 when `g=2` [Got1959]_. When `g>2`, a complete set of matrices + defining the boundary of a fundamental domain for the action of + `\mathrm{Sp}_{2g}(\mathbb{Z})` is not currently known. As a substitute, we + consider two types of matrices: the `19 g(g-1)/2` matrices obtained by + mimicking the `g=2` matrices on any pair of indices between 0 and `g-1`, + and the `2^g` matrices obtained by embedding a copy of a lower-dimensional + `J` matrix on any subset of indices. + +.. function:: void sp2gz_fundamental(fmpz_mat_t mat, slong j) + + Sets *mat* to the `j^{\text{th}}` fundamental symplectic matrix as defined + above. + +.. function:: int sp2gz_is_correct(const fmpz_mat_t mat) + + Returns true (nonzero) iff *mat* is a symplectic matrix. + +.. function:: int sp2gz_is_j(const fmpz_mat_t mat) + + Returns true (nonzero) iff the symplectic matrix *mat* is the `J` matrix. + +.. function:: int sp2gz_is_block_diag(const fmpz_mat_t mat) + + Returns true (nonzero) iff the symplectic matrix *mat* is of block-diagonal + form as in :func:`sp2gz_block_diag`. + +.. function:: int sp2gz_is_trig(const fmpz_mat_t mat) + + Returns true (nonzero) iff the sympletic matrix *mat* is of trigonal form + as in :func:`sp2gz_trig`. + +.. function:: int sp2gz_is_embedded(fmpz_mat_t res, const fmpz_mat_t mat) + + Assuming that *mat* is a `2g\times 2g` symplectic matrix and *res* is + square of size `2r` for some `r\leq g`, returns true (nonzero) iff *mat* + can be obtained as the result of :func:`sp2gz_embed` from a `2r\times 2r` + symplectic matrix, and store this matrix in *res*. Otherwise, returns + false (0) and leaves *res* undefined. + +.. function:: void sp2gz_inv(fmpz_mat_t inv, const fmpz_mat_t mat) + + Sets *inv* to the inverse of the symplectic matrix *mat*. + +.. function:: fmpz_mat_struct * sp2gz_decompose(slong * nb, const fmpz_mat_t mat) + + Returns a vector *res* of symplectic matrices and store its length in *nb* + such that the following holds: *mat* is the product of the elements of + *res* from left to right, and each element of *res* is block-diagonal, + trigonal, the `J` matrix, an embedded `J` matrix from a lower dimension, or + an embedded matrix from dimension 1. The output vector *res* will need to + be freed by the user as follows: + + .. code-block:: c + + slong k; + for (k = 0; k < *nb; k++) + { + fmpz_mat_clear(&res[k]); + } + flint_free(res); + +.. function:: void sp2gz_randtest(fmpz_mat_t mat, flint_rand_t state, slong bits) + + Sets *mat* to a random symplectic matrix whose coefficients have length + approximately *bits*, obtained as a product of block-diagonal and trigonal + symplectic matrices and the `J` matrix. + +The Siegel half space +------------------------------------------------------------------------------- + +We continue to denote by `\alpha,\beta,\gamma,\delta` the `g\times g` blocks of +*mat*, which is always assumed to be symplectic. + +.. function:: void acb_siegel_cocycle(acb_mat_t c, const fmpz_mat_t mat, const acb_mat_t tau, slong prec) + + Sets *c* to `\gamma\tau + \delta`. + +.. function:: void acb_siegel_transform_cocycle_inv(acb_mat_t w, acb_mat_t c, acb_mat_t cinv, const fmpz_mat_t mat, const acb_mat_t tau, slong prec) + + Sets *w*, *c* and *cinv* to `(\alpha\tau + \beta)(\gamma\tau + + \delta)^{-1}`, `\gamma\tau + \delta` and `(\gamma\tau + \delta)^{-1}` + respectively. + +.. function:: void acb_siegel_transform(acb_mat_t w, const fmpz_mat_t mat, const acb_mat_t tau, slong prec) + + Sets *w* to `(\alpha\tau + \beta)(\gamma\tau + \delta)^{-1}`. + +.. function:: void acb_siegel_transform_z(acb_ptr r, acb_mat_t w, const fmpz_mat_t mat, acb_srcptr z, const acb_mat_t tau, slong prec) + + Sets *w* to `(\alpha\tau + \beta)(\gamma\tau + \delta)^{-1}` and *r* to + `(\gamma\tau + \delta)^{-T}z`. + +.. function:: void acb_siegel_cho(arb_mat_t C, const acb_mat_t tau, slong prec) + + Sets *C* to an upper-triangular Cholesky matrix such that `\pi + \mathrm{Im}(\tau) = C^T C`. If one cannot determine that + `\mathrm{Im}(\tau)` is positive definite at the current working precision, + *C* is set to an indeterminate matrix. + +.. function:: void acb_siegel_yinv(arb_mat_t Yinv, const acb_mat_t tau, slong prec) + + Sets *Yinv* to the inverse of `\mathrm{Im}(\tau)`. If one cannot determine + that `\mathrm{Im}(\tau)` is invertible at the current working precision, + *Yinv* is set to an indeterminate matrix. + +.. function:: void acb_siegel_reduce(fmpz_mat_t mat, const acb_mat_t tau, slong prec) + + Sets *mat* to a symplectic matrix such that `\mathit{mat}\cdot\tau` is as + reduced as possible, repeatedly reducing the imaginary and real parts of + `\tau` and applying fundamental symplectic matrices. If the coefficients of + `\tau` do not have a reasonable size or if `\det \mathrm{Im}(\tau)` is + vanishingly small, we simply set *mat* to the identity. + +.. function:: int acb_siegel_is_reduced(const acb_mat_t tau, slong tol_exp, slong prec) + + Returns true (nonzero) iff it is certainly true that `\tau` belongs to the + reduced domain defined by the tolerance parameter `\varepsilon = + 2^{\mathit{tol\_exp}}`. This means the following: + `|\mathrm{Re}(\tau_{j,k})| < \frac12 + \varepsilon` for all `0\leq j,k < + g`; the imaginary part of `\tau` passes :func:`arb_mat_spd_is_lll_reduced` + with the same parameters; and for every matrix obtained from + :func:`sp2gz_fundamental`, the determinant of the corresponding cocycle is + at least `1-\varepsilon`. + +.. function:: void acb_siegel_randtest(acb_mat_t tau, flint_rand_t state, slong prec, slong mag_bits) + + Sets *tau* to a random matrix in `\mathbb{H}_g`, possibly far from being + reduced. + +.. function:: void acb_siegel_randtest_reduced(acb_mat_t tau, flint_rand_t state, slong prec, slong mag_bits) + + Sets *tau* to a random reduced matrix in `\mathbb{H}_g` that is likely to + trigger corner cases for several functions in this module. + +.. function:: void acb_siegel_randtest_vec(acb_ptr z, flint_rand_t state, slong g, slong prec) + + Sets *z* to a random vector of length *g* that is likely to trigger corner + cases for several functions in this module. + +Theta characteristics +------------------------------------------------------------------------------- + +.. function:: void acb_theta_char_get_slong(slong * n, ulong a, slong g) + + Sets each entry of *n* to the corresponding bit of *a*. + +.. function:: ulong acb_theta_char_get_a(const slong * n, slong g) + + Returns the unique characteristic *a* such that `n\in 2\mathbb{Z}^g + a`. + +.. function:: void acb_theta_char_get_arb(arb_ptr v, ulong a, slong g) + +.. function:: void acb_theta_char_get_acb(acb_ptr v, ulong a, slong g) + + Sets *v* to `a/2` seen as an element of `\mathbb{R}^g` or `\mathbb{C}^g` + respectively. + +.. function:: slong acb_theta_char_dot(ulong a, ulong b, slong g) + + Returns `\sum_{i=0}^{g-1} a_i b_i` modulo 4 as an integer between 0 and 3, + where `a_i, b_i` for `0\leq i < g` denote the bits of `a` and `b` + respectively. + +.. function:: slong acb_theta_char_dot_slong(ulong a, const slong * n, slong g) + + Returns `\sum_{i=0}^{g-1} a_i n_i` modulo 4 as an integer between 0 and 3. + +.. function:: void acb_theta_char_dot_acb(acb_t x, ulong a, acb_srcptr z, slong g, slong prec) + + Sets *x* to `\sum_{i=0}^{g-1} a_i z_i`. + +.. function:: int acb_theta_char_is_even(ulong ab, slong g) + + Returns true iff the characteristic `(a,b)` is even, i.e. `a^Tb` is divisible by 2. + +.. function:: int acb_theta_char_is_goepel(ulong ch1, ulong ch2, ulong ch3, ulong ch4, slong g) + + Returns true iff the given characteristics define a Göpel quadruple, + i.e. they are distinct even characteristics whose sum belongs to + `2\mathbb{Z}^g`. + +.. function:: int acb_theta_char_is_syzygous(ulong ch1, ulong ch2, ulong ch3, slong g) + + Returns true iff the given characteristics define a syzygous triple, + i.e. they can be completed into a Göpel quadruple. + +Ellipsoids: types and macros +------------------------------------------------------------------------------- + +Following [DHBHS2004]_, naive algorithms will compute a partial sum of theta +series over points `n` in the lattice `\mathbb{Z}^g` contained in certain +ellipsoids, and finally add an error bound coming from the tail. We first +gather methods to compute with ellipsoids themselves. + +Fix an upper-triangular matrix `C` with positive diagonal entries (henceforth +called a "Cholesky matrix"), a radius `R\geq 0`, a vector `v\in \mathbb{R}^g`, +and `1\leq d\leq g`. Consider the ellipsoid `E` consisting of points `n = +(n_0,\ldots,n_{g-1})` satisfying `(v + Cn)^T(v + Cn)\leq R^2` and such that +their last coordinates `n_{d},\ldots, n_{g-1}` are fixed. We encode `E` as +follows: we store the endpoints and midpoint of the interval of allowed values +for `n_{d-1}` as :type:`slong`'s, and if `d\geq 1`, we store a +`(d-1)`-dimensional "child" of `E` for each value of `n_{d-1}` as another +ellipsoid in a recursive way. Children are partitioned between left and right +children depending on the position of `n_{d-1}` relative to the midpoint (by +convention, the midpoint is a right child). When `d=g` and for a fixed Cholesky +matrix `C`, this representation uses `O(R^{g-1})` space for an ellipsoid of +radius `R` containing approximately `O(R^{g})` points. + +.. type:: acb_theta_eld_struct + +.. type:: acb_theta_eld_t + + An :type:`acb_theta_eld_t` is an array of length one of type + :type:`acb_theta_eld_struct` encoding an ellipsoid as described above, + permitting it to be passed by reference. + +The following macros are available after *E* of type :type:`acb_theta_eld_t` +has been initialized using :func:`acb_theta_eld_init` below. + +.. macro:: acb_theta_eld_dim(E) + + Macro returning `d`. + +.. macro:: acb_theta_eld_ambient_dim(E) + + Macro returning `g`. + +The following macros are available after *E* has been initialized and then +computed using :func:`acb_theta_eld_set` below. + +.. macro:: acb_theta_eld_coord(E, k) + + Macro returning the common coordinate `n_k` of the points in `E`. This + requires `d \leq k < g`. + +.. macro:: acb_theta_eld_min(E) + +.. macro:: acb_theta_eld_mid(E) + +.. macro:: acb_theta_eld_max(E) + + Macros returning the minimum, midpoint, and maximum of `n_{d-1}` in `E` + respectively. + +.. macro:: acb_theta_eld_nr(E) + +.. macro:: acb_theta_eld_nl(E) + + Macros returning the number of right and left children of `E` + respectively. + +.. macro:: acb_theta_eld_rchild(E, k) + +.. macro:: acb_theta_eld_lchild(E, k) + + Macros returning a pointer to the `k^{\text{th}}` right (resp. left) child + of `E` as an :type:`acb_theta_eld_t`. + +.. macro:: acb_theta_eld_nb_pts(E) + + Macro returning the number of points contained in `E`. + +.. macro:: acb_theta_eld_nb_border(E) + + Macro returning the number of points in the border of `E`, defined as + follows. If `d=1`, then it consists of the two points with `n_0` equal to + `m - 1` and `M + 1`, where `m` and `M` are the result of + :macro:`acb_theta_eld_max` and :macro:`acb_theta_eld_min` respectively. If + `d\geq 2`, then it is the reunion of the borders of all children of + `E`. This is only used for testing. + +.. macro:: acb_theta_eld_box(E, k) + + Macro returning the smallest nonnegative integer `M_k` such that all the + points in `E` satisfy `|n_k|\leq M_k`. This requires `0\leq k < d`. + +Ellipsoids: memory management and computations +------------------------------------------------------------------------------- + +.. function:: void acb_theta_eld_init(acb_theta_eld_t E, slong d, slong g) + + Initializes *E* as a *d*-dimensional ellipsoid in ambient dimension *g*. We + require `1\leq d\leq g`. + +.. function:: void acb_theta_eld_clear(acb_theta_eld_t E) + + Clears *E* as well as any recursive data contained in it. + +.. function:: int acb_theta_eld_set(acb_theta_eld_t E, const arb_mat_t C, const arf_t R2, arb_srcptr v) + + Assuming that *C* is upper-triangular with positive diagonal entries, + attempts to set *E* to represent an ellipsoid as defined above, where *R2* + indicates `R^2`, and returns 1 upon success. If the ellipsoid points do not + fit in :type:`slong`'s or if the ellipsoid is unreasonably large, returns 0 + instead and leaves *E* undefined. + +The following functions are available after :func:`acb_theta_eld_set` has been +called successfully. + +.. function:: void acb_theta_eld_points(slong * pts, const acb_theta_eld_t E) + + Sets *pts* to the list of all the points in `E`, as a concatenation of + vectors of length *g*. + +.. function:: void acb_theta_eld_border(slong * pts, const acb_theta_eld_t E) + + Sets *pts* to the list of all the points in the border of `E`. + +.. function:: int acb_theta_eld_contains(const acb_theta_eld_t E, slong * pt) + + Returns true (nonzero) iff *pt* is contained in `E`. The vector *pt* must + be of length *g*. + +.. function:: void acb_theta_eld_print(const acb_theta_eld_t E) + + Prints a faithful description of `E`. This may be unwieldy in high + dimensions. + +Naive algorithms: error bounds +------------------------------------------------------------------------------- + +By [EK2023]_, for any `v\in \mathbb{R}^g` and any upper-triangular Cholesky +matrix `C`, and any `R` such that `R^2 \geq\max(4,\mathit{ord})`, we have + + .. math :: + + \sum_{n\in C\mathbb{Z}^g + v,\ \lVert n\rVert^2 \geq R^2} \lVert n\rVert^{\mathit{ord}} e^{-\lVert n\rVert^2} + \leq 2^{2g+2} R^{g-1+p} e^{-R^2} \prod_{j=0}^{g-1} (1 + \gamma_j^{-1}) + +where `\gamma_0,\ldots, \gamma_{g-1}` are the diagonal coefficients of `C`. We +use this to bound the contribution from the tail of the theta series in naive +algorithms, and thus to find out which ellipsoid to consider at a given +precision. When several vectors `z` are present, we first reduce them to a +common compact domain and use only one ellipsoid, following [DHBHS2004]_. + +.. function:: void acb_theta_naive_radius(arf_t R2, arf_t eps, const arb_mat_t C, slong ord, slong prec) + + Sets *R2* and *eps* such that the above upper bound for *R2* + and the given *ord* is at most *eps*. We choose *eps* so that + the relative error on the output of the naive algorithm should be roughly + `2^{-\mathit{prec}}` if no cancellations occur in the sum, i.e. + `\mathit{eps} \simeq 2^{-\mathit{prec}} \prod_{j=0}^{g-1} (1 + \gamma_j^{-1})`. + +.. function:: void acb_theta_naive_reduce(arb_ptr v, acb_ptr new_zs, arb_ptr as, acb_ptr cs, arb_ptr us, acb_srcptr zs, slong nb, const acb_mat_t tau, slong prec) + + Given *zs*, a concatenation of *nb* vectors of length `g`, performs the + simultaneous reduction of these vectors with respect to the matrix + `\tau`. This means the following. Let `0\leq k< \mathit{nb}`, let `z` + denote the `k^{\mathrm{th}}` vector stored in *zs*, and let `X,Y` + (resp. `x,y`) be the real and imaginary parts of `\tau` (resp. `z`). Write + `Y^{-1}y = r + a` where `a` is an even integral vector and `r` is + bounded. (We set `a=0` instead if the entries of this vector have an + unreasonably large magnitude.) Then + + .. math :: + + \begin{aligned} + \theta_{0,b}(z,\tau) &= e^{\pi y^T Y^{-1} y} \sum_{n\in \mathbb{Z}^g} + e^{\pi i ((n - a)^T X (n - a) + 2(n - a)^T (x + \tfrac b2))} + e^{-\pi (n + r)^T Y (n + r)}\\ + &= e^{\pi y^T Y^{-1} y} e^{\pi i (a^T X a - 2a^T x + i r^T Y r)} + \theta_{0,b}((x - Xa) + iYr, \tau). + \end{aligned} + + The reduction of `z` is defined as `(x - Xa) + i Y r`, which has a bounded + imaginary part, and this vector is stored as the `k^{\mathrm{th}}` vector + of *new_zs*. The vector `a` is stored as the `k^{\mathrm{th}}` vector of + *as*. The quantity `u = \exp(\pi y^T Y^{-1} y)` is a multiplicative factor + for the error bound, and is stored as the `k^{\mathrm{th}}` entry of + *us*. The quantity + + .. math :: + + c = u \exp(\pi i (a^T X a - 2a^T x + i r^T Y r)) + + is a multiplicative factor for the theta values, and is stored as the + `k^{\mathrm{th}}` entry of *cs*. The offset for the corresponding ellipsoid + is `v^{(k)} = C r` which is also bounded independently of `k`, and *v* is + set to the :func:`acb_union` of the `v^{(k)}` for `0\leq k< \mathit{nb}`. + +.. function:: void acb_theta_naive_term(acb_t res, acb_srcptr z, const acb_mat_t tau, slong * tup, slong * n, slong prec) + + Sets *res* to `n_0^{k_0} \cdots n_{g-1}^{k_{g-1}}\exp(\pi i(n^T\tau n + 2 + n^Tz))`, where the `k_j` and `n_j` denotes the `j^{\mathrm{th}}` entry in + *tup* and *n* respectively. The vector *tup* may be *NULL*, which is + understood to mean the zero tuple. This is only used for testing. + +Naive algorithms: main functions +------------------------------------------------------------------------------- + +The main worker inside each version of the naive algorithm will process one +line inside the computed ellipsoid. Before calling this worker, for fixed +`\tau` and `z` and fixed coordinates `n_1,\ldots n_{g-1}` defining a line +inside the ellipsoid, if `n_{\mathrm{min}}` are `n_{\mathrm{max}}` are the +endpoints of the interval of allowed values for `n_0`, we (efficiently) +compute: + +- the vector `v_1` with entries `\exp(\pi i j^2 \tau_{0,0})` for + `n_{\mathrm{min}}\leq j\leq n_{\mathrm{max}}`, +- the vector `v_2` with entries `x^j` for `n_{\mathrm{min}}\leq j\leq + n_{\mathrm{max}}`, where + + .. math :: + + x = \exp(2 \pi i z_0) \prod_{k = 1}^{g-1} \exp(2 \pi i n_k \tau_{0,k}), + +- the cofactor `c\in \mathbb{C}` given by + + .. math :: + + c = \prod_{k = 1}^{g-1} \exp(2 \pi i n_k z_k) \cdot + \prod_{1\leq j\leq k < g} \exp(\pi i (2 - \delta_{j,k}) n_j n_k \tau_{j,k}). + +This allow us to use :func:`acb_dot` in the workers while maintaining +reasonable memory costs, and to use an average of strictly less than two +complex multiplications per lattice point as `R\to \infty`. Moreover, these +multiplications are performed at only a fraction of the full precision for +lattice points far from the ellipsoid center. Different versions of the naive +algorithm will rely on slightly different workers, so introducing a function +pointer type is helpful to avoid code duplication. + +The methods in this section are only used when `g\geq 2`: when `g=1`, the naive +algorithms will call functions from :ref:`acb_modular.h ` +directly. + +.. type:: acb_theta_naive_worker_t + + A function pointer type. A function *worker* of this type has the + following signature: + + .. function:: void worker(acb_ptr th, acb_srcptr v1, acb_srcptr v2, const slong * precs, slong len, const acb_t c, const slong * coords, slong ord, slong g, slong prec, slong fullprec) + + where: + + - *th* denotes the output vector of theta values to which terms will be added, + - *v1*, *v2* and *c* are precomputed as above, + - *precs* contains working precisions for each term `n_{\mathrm{min}}\leq + j\leq n_{\mathrm{max}}`, + - *len* `= n_{\mathrm{max}} - n_{\mathrm{min}} + 1` is the common length of + *v1*, *v2* and *precs*, + - *coords* is `(n_{\mathrm{min}}, n_1, \ldots, n_{g-1})`, + - *ord* is the maximal derivation order, + - *prec* is the working precision for this line inside the ellipsoid, and + finally + - *fullprec* is the working precision for summing into *th*. + +.. function:: void acb_theta_naive_worker(acb_ptr th, slong len, acb_srcptr zs, slong nb, const acb_mat_t tau, const acb_theta_eld_t E, slong ord, slong prec, acb_theta_naive_worker_t worker) + + Runs the naive algorithm by calling *worker* on each line in the ellipsoid + *E*. The argument *zs* is a concatenation of *nb* vectors `z\in + \mathbb{C}^g`, *len* is the number of theta values computed by *worker* for + each `z`, and *ord* is passed as an argument to *worker*. No error bound + coming from the tail is added. Considering several vectors `z` at the same + time allows for a faster computation of `\theta_{a,b}(z,\tau)` for many + values of `z` and a fixed `\tau`, since exponentials of the entries of + `\tau` can be computed only once. + +.. function:: void acb_theta_naive_00(acb_ptr th, acb_srcptr zs, slong nb, const acb_mat_t tau, slong prec) + +.. function:: void acb_theta_naive_0b(acb_ptr th, acb_srcptr zs, slong nb, const acb_mat_t tau, slong prec) + + Evaluates either `\theta_{0,0}(z^{(k)}, \tau)`, or alternatively + `\theta_{0,b}(z^{(k)}, \tau)` for each `b\in \{0,1\}^g`, for each `0\leq k + < \mathit{nb}`. The result *th* will be a concatenation of *nb* + vectors of length `1` or `2^g` respectively. + + The associated worker performs one :func:`acb_dot` operation. + +.. function:: void acb_theta_naive_fixed_a(acb_ptr th, ulong a, acb_srcptr zs, slong nb, const acb_mat_t tau, slong prec) + + Evaluates `\theta_{a,b}(z^{(k)}, \tau)` for all `(a,b)` where `b\in + \{0,1\}^g` and `a` is fixed, for each `0\leq k< \mathit{nb}`. The + result *th* will be a concatenation of *nb* vectors of length `2^g`. + + We reduce to calling :func:`acb_theta_naive_0b` + by writing + + .. math :: + + \theta_{a,b}(z,\tau) = \exp(\pi i \tfrac{a^T}{2} \tau \tfrac a2) + \exp(\pi i a^T(z + \tfrac b 2)) \theta_{0,b}(z + \tau \tfrac{a}{2}, \tau). + + We proceed similarly in :func:`acb_theta_naive_fixed_ab` and + :func:`acb_theta_naive_all`, using :func:`acb_theta_naive_00` for the + former. + +Naive algorithms for derivatives +------------------------------------------------------------------------------- + +This section contains methods to evaluate the successive partial derivatives of +`\theta_{a,b}(z,\tau)` with respect to the `g` coordinates of `z`. Derivatives +with respect to `\tau` are accounted for by the heat equation + + .. math :: + + \frac{\partial\theta_{a,b}}{\partial \tau_{j,k}} = \frac{1}{2\pi i(1 +\delta_{j,k})} + \frac{\partial^2\theta_{a,b}}{\partial z_j \partial z_k}. + +We encode tuples of derivation orders, henceforth called "derivation tuples", +as vectors of type :type:`slong` and length `g`. In agreement with +:ref:`acb_modular.h `, we also normalize derivatives in the same way +as in the Taylor expansion, so that the tuple `(k_0,\ldots,k_{g-1})` +corresponds to the differential operator + + .. math :: + + \frac{1}{k_0!}\cdots\frac{1}{k_{g-1}!} \cdot \frac{\partial^{|k|}}{\partial z_0^{k_0}\cdots \partial z_{g-1}^{k_{g-1}}}, + +where `|k|:=\sum k_i`. We always consider all derivation tuples up to a total +order *ord*, and order them first by their total order, then +reverse-lexicographically. For example, in the case `g=2`, the sequence of +orders is `(0,0)`, `(1,0)`, `(0,1)`, `(2,0)`, `(1,1)`, etc. + +The naive algorithms for derivatives will evaluate a partial sum of the +differentiated series: + + .. math :: + + \frac{\partial^{|k|}\theta_{a,b}}{\partial z_0^{k_0}\cdots \partial z_{g-1}^{k_{g-1}}}(z,\tau) = (2\pi i)^{|k|} \sum_{n\in \mathbb{Z}^g + \tfrac a2} n_0^{k_0} \cdots n_{g-1}^{k_{g-1}} + e^{\pi i n^T \tau n + 2\pi i n^T (z + \tfrac b2)}. + +.. function:: slong acb_theta_jet_nb(slong ord, slong g) + + Returns the number of derivation tuples with total order at most *ord*. The + result will be zero if *ord* is negative. + +.. function:: slong acb_theta_jet_total_order(const slong * tup, slong g) + + Returns the total derivation order for the given tuple *tup* of length *g*. + +.. function:: void acb_theta_jet_tuples(slong * tups, slong ord, slong g) + + Sets *tups* to the concatenation of all derivation tuples up to total order + *ord*. + +.. function:: slong acb_theta_jet_index(const slong * tup, slong g) + + Returns *n* such that *tup* is the `n^{\mathrm{th}}` derivation tuple of + length *g*. + +.. function:: void acb_theta_jet_mul(acb_ptr res, acb_srcptr v1, acb_srcptr v2, slong ord, slong g, slong prec) + + Sets *res* to the vector of derivatives of the product `fg`, assuming that + *v1* and *v2* contains the derivatives of `f` and `g` respectively. + +.. function:: void acb_theta_jet_compose(acb_ptr res, acb_srcptr v, const acb_mat_t N, slong ord, slong prec) + + Sets *res* to the vector of derivatives of the composition `f(Nz)`, + assuming that *v* contains the derivatives of *f* at the point `Nz`. + +.. function:: void acb_theta_jet_exp_pi_i(acb_ptr res, arb_srcptr a, slong ord, slong g, slong prec) + + Sets *res* to the vector of derivatives of the function `\exp(\pi i (a_0 + z_1 + \cdots + a_{g-1} z_{g-1}))` at `z = 0`, where `a_0,\ldots a_{g-1}` are + the entries of *a*. + +.. function:: void acb_theta_jet_naive_radius(arf_t R2, arf_t eps, arb_srcptr v, const arb_mat_t C, slong ord, slong prec) + + Assuming that *C* is the upper-triangular Cholesky matrix for `\pi Y` and + `v = C Y^{-1} y` where `y, Y` are the imaginary parts of `z` and `\tau` + respectively, returns *R2* and *eps* so that, when summing the above series + on terms `n\in \mathbb{Z}^g` such that `(v + C n)^T(v + C n)\leq R^2`, the + absolute value of the tail of the series (before multiplying by the leading + factor `(2\pi i)^{|k|} e^{\pi y^T Y^{-1} y}`, see below) will be bounded + above by *eps*, for any derivation tuple `k` with `|k|\leq \mathit{ord}`. + + We can rewrite the above sum as + + .. math :: + + (2\pi i)^{|k|} e^{\pi y^T Y^{-1} y} \sum_{n\in \mathbb{Z}^g + \tfrac a2} n_0^{k_0} \cdots n_{g-1}^{k_{g-1}} e^{\pi i(\cdots)} e^{-\pi (n + Y^{-1}y)^T Y (n + Y^{-1}y)}. + + We ignore the leading multiplicative factor. Writing `m = C n + v`, we have + + .. math :: + + n_0^{k_0}\cdots n_{g-1}^{k_{g-1}}\leq + (\lVert C^{-1}\rVert_\infty \lVert n\rVert_2 + \lVert Y^{-1}y\rVert_\infty)^{|k|}. + + Using the upper bound from :func:`acb_theta_naive_radius`, we see that the + absolute value of the tail of the series is bounded above by + + .. math :: + + (\lVert C^{-1} \rVert_\infty R + \lVert Y^{-1}y \rVert_\infty)^{|k|} + 2^{2g+2} R^{g-1} e^{-R^2} \prod_{j=0}^{g-1} (1 + \gamma_j^{-1}). + + Thus, we proceed as follows. We first compute *R2* and *eps* using + :func:`acb_theta_naive_radius` with *ord* = 0. If `R\leq \lVert + Y^{-1}y\rVert_\infty/\lVert C^{-1}\rVert_\infty`, we simply multiply *eps* + by `\max\{1, 2 \lVert Y^{-1}y \rVert\}^{\mathit{ord}}`. Otherwise, we + compute *R2* and *eps* using :func:`acb_theta_naive_radius` with the given + value of *ord*. We can then set *R2* to the maximum of *R2* and `\lVert + Y^{-1}y \rVert_\infty /\lVert C^{-1} \rVert_\infty`, and multiply *eps* by + `\max\{1, 2\lVert C^{-1}\rVert\}^{\mathit{ord}}`. + +.. function:: void acb_theta_jet_naive_00(acb_ptr dth, acb_srcptr z, const acb_mat_t tau, slong ord, slong prec) + + Sets *dth* to the vector of derivatives of `\theta_{0,0}` at the given + point `(z,\tau)` up to total order *ord*. + + In :func:`acb_theta_jet_naive_fixed_ab`, we reduce to this function using + the same formula as in :func:`acb_theta_naive_fixed_ab`, making suitable + linear combinations of the derivatives. + + In :func:`acb_theta_jet_naive_all`, we instead use an ellipsoid to encode + points in `\tfrac 12 \mathbb{Z}^g`, and divide `\tau` by 4 and `z` by 2 to + sum the correct terms. The bounds output by + :func:`acb_theta_jet_naive_radius` are still valid, since this just has the + effect of multiplying `\lVert C^{-1} \rVert` and each `\gamma_j^{-1}` by + `2`. + +.. function:: void acb_theta_jet_error_bounds(arb_ptr err, acb_srcptr z, const acb_mat_t tau, acb_srcptr dth, slong ord, slong prec) + + Assuming that *dth* contains the derivatives of a function `\theta_{a,b}` + up to total order `\mathit{ord} + 2`, sets *err* to a vector with the + following property. Let `(z_0,\tau_0)` be the midpoint of `(z,\tau)`, and + let `(z_1,\tau_1)` be any point inside the ball specified by the given *z* + and *tau*. Then the vectors of derivatives of `\theta_{a,b}` at + `(z_0,\tau_0)` and `(z_1,\tau_1)` up to total order *ord* differ by at most + *err* elementwise. + +Quasi-linear algorithms: presentation +------------------------------------------------------------------------------- + +We refer to [EK2023]_ for a detailed description of the quasi-linear algorithm +implemented here. In a nutshell, the algorithm relies on the following +duplication formula: for all `z,z'\in \mathbb{C}^g` and `\tau\in \mathbb{H}_g`, + + .. math :: + + \theta_{a,0}(z,\tau) \theta_{a,0}(z',\tau) = \sum_{a'\in(\mathbb{Z}/2\mathbb{Z})^g} + \theta_{a',0}(z+z',2\tau) \theta_{a+a',0}(z-z',2\tau). + +In particular, + + .. math :: + + \begin{aligned} + \theta_{a,0}(z,\tau)^2 &= \sum_{a'\in (\mathbb{Z}/2\mathbb{Z})^g} + \theta_{a',0}(2z,2\tau) \theta_{a+a',0}(0,2\tau),\\ + \theta_{a,0}(0,\tau)\theta_{a,0}(z,\tau) &= \sum_{a'\in(\mathbb{Z}/2\mathbb{Z})^g} + \theta_{a',0}(z,2\tau) \theta_{a+a',0}(z,2\tau), \\ + \theta_{a,0}(0,\tau)^2 &= \sum_{a'\in (\mathbb{Z}/2\mathbb{Z})^g} + \theta_{a',0}(0,2\tau) \theta_{a+a',0}(0,2\tau). + \end{aligned} + +Applying one of these duplication formulas amounts to taking a step in a +(generalized) AGM sequence. These formulas also have analogues for all theta +values, not just `\theta_{a,0}`: for instance, we have + + .. math :: + + \theta_{a,b}(0,\tau)^2 = \sum_{a'\in (\mathbb{Z}/2\mathbb{Z})^g} (-1)^{a'^Tb} + \theta_{a',0}(0,2\tau)\theta_{a+a',0}(0,2\tau). + +Suppose that we wish to compute `\theta_{a,0}(0,\tau)` for all `a\in \{0,1\}^g` +and a reduced matrix `\tau\in \mathbb{H}_g`. Applying the last formula `n` +times, we reduce to evaluating `\theta_{a,0}(0,2^n\tau)`. We expect that the +absolute value of this complex number is roughly `\exp(-d^2)` for `d = +2^n\mathrm{Dist}_\tau(0, \mathbb{Z}^g + \tfrac a2))`, where +`\mathrm{Dist}_\tau` denotes the distance in `\mathbb{R}^g` attached to the +quadratic form `\mathrm{Im}(\tau)`. Provided that `n \simeq +\log_2(\mathit{prec})`, we have to sum only `O_g(1)` terms in the naive +algorithm to evaluate `\theta_{a,0}(0,2^n\tau)` at "shifted absolute precision" +*prec*, i.e. absolute precision `\mathit{prec} + d^2/\log(2)`. + +In order to recover `\theta_{a,0}(0,\tau)`, we then perform `n` AGM +steps. Assuming that each `|\theta_{a,0}(0, 2^k\tau)|` is indeed of the +expected order of magnitude, we can ensure that the precision loss is `O_g(1)` +bits at each step in terms of shifted absolute precision, and we can calculate +the correct sign choices of square roots at each step with the naive +algorithm. However, depending on the choice of `\tau`, this assumption may not +always hold. + +We make the following adjustments to make the algorithm work for all `\tau`, as +well as for theta values at `z\neq 0`: + +- If we discover (after applying the naive algorithm) that some value + `\theta_{a,0}(0,2^k\tau)` is too small, we introduce an auxiliary real vector + `t`. At each step, starting from `\theta_{a,0}(0,2^{k+1}\tau)`, + `\theta_{a,0}(2^{k+1}t, 2^{k+1}\tau)` and `\theta_{a,0}(2^{k+2}t, + 2^{k+1}\tau)`, we compute `\theta_{a,0}(2^{k}t, 2^k\tau)` and + `\theta_{a,0}(2^{k+1}t, 2^k\tau)` using square roots (second formula above), + then `\theta_{a,0}(0, 2^k\tau)` using divisions (third formula). For a huge + majority of such `t`, none of the values `\theta_{a,0}(2^kt, 2^k\tau)` and + `\theta_{a,0}(2^{k+1}t, 2^k\tau)` will be too small [EK2023]_. In practice, + we choose `t` at random and obtain a probabilistic algorithm with a + negligible failure probability. + +- When computing `\theta_{a,0}(z,\tau)` for a nonzero `z`, we compute + `\theta_{a,0}(0, 2^k\tau)` and `\theta_{a,0}(2^k z, 2^k\tau)` using the + second and fourth formulas at each step. We actually replace each occurrence + of `\theta_{a,0}(z,\tau)` by `e^{-\pi y^T Y^{-1} y}\theta_{a,0}(z,\tau)`, as + the absolute values of the latter quantities do not increase as `y` gets + farther from zero, and they still satisfy the duplication formulas. + +- These two techniques can be combined by evaluating theta values at the six + vectors `2^k v` for `v = 0, t, 2t, z, z + t, z + 2t`. Note that we only have + to compute `\theta_{a,0}(2^kz, 2^k\tau)` at the last step `k=0`. + +- Finally, if the eigenvalues of `\mathrm{Im}(\tau)` have different orders of + magnitude, then the ellipsoid we have to sum on for the naive algorithm will + become very thin in one direction while still being thick in other + directions. In such a case, we can split the total sum and compute `O(1)` + theta values in a lower dimension. This increases the efficiency of the + algorithm while ensuring that the absolute precisions we consider are always + in `O(\mathit{prec})`. + +Quasi-linear algorithms: distances +------------------------------------------------------------------------------- + +.. function:: void acb_theta_dist_pt(arb_t d, arb_srcptr v, const arb_mat_t C, slong * n, slong prec) + + Sets *d* to `\lVert v + Cn\rVert^2` for the usual Euclidean norm. + +.. function:: void acb_theta_dist_lat(arb_t d, arb_srcptr v, const arb_mat_t C, slong prec) + + Sets *d* to `\mathrm{Dist}(v, C \mathbb{Z}^g)^2` for the usual Euclidean norm. We + first compute an upper bound on the result by considering the `2^g` vectors + obtained by rounding the entries of `C^{-1}v` to integers up or down, then + compute an ellipsoid to find the minimum distance. + +.. function:: void acb_theta_dist_a0(arb_ptr d, acb_srcptr z, const acb_mat_t tau, slong prec) + + Sets *d* to the vector containing `\mathrm{Dist}(C \cdot(Y^{-1}y + \tfrac + a2), C\cdot \mathbb{Z}^g)^2` for `a\in \{0,1\}^g`, where `y, Y` are the + imaginary parts of `z, \tau` respectively and `C` is the upper-triangular + Cholesky matrix for `\pi Y`. The `a^{\mathrm{th}}` entry of *d* is also + `\mathrm{Dist}_\tau(-Y^{-1}y, \mathbb{Z}^g + \tfrac a2)^2`, where + `\mathrm{Dist}_\tau` denotes the distance attached to the quadratic form + `\mathrm{Im}(\tau)`. + +.. function:: slong acb_theta_dist_addprec(const arb_t d) + + Returns an integer that is close to *d* divided by `\log(2)` if *d* is + finite and of reasonable size, and otherwise returns 0. + +Quasi-linear algorithms: AGM steps +------------------------------------------------------------------------------- + +.. function:: void acb_theta_agm_hadamard(acb_ptr res, acb_srcptr a, slong g, slong prec) + + Sets *res* to the product of the Hadamard matrix `\bigl(\begin{smallmatrix} + 1 & 1 \\ 1 & -1\end{smallmatrix}\bigr)^{\otimes g}` and the vector + `a`. Both *res* and `a` must be vectors of length `2^g`. In other words, + for each `k\in \{0,1\}^g`, this sets the `k^{\mathrm{th}}` entry of *res* + to `\sum_{j\in \{0,1\}^g} (-1)^{k^T j} a_j`. + +.. function:: void acb_theta_agm_sqrt(acb_ptr res, acb_srcptr a, acb_srcptr rts, slong nb, slong prec) + + Sets the `k^{\mathrm{th}}` entry `r_k` of *res* for `0\leq k < \mathit{nb}` + to a square root of the corresponding entry `a_k` of `a`. The choice of + sign is determined by *rts*: each `r_k` will overlap the corresponding + entry of *rts* but not its opposite. Exceptional cases are handled as + follows: if both square roots of `a_k` overlap *rts*, then `r_k` is set to + their :func:`acb_union`; if none ovelaps *rts*, then `r_k` is set to an + indeterminate value. + +.. function:: void acb_theta_agm_mul(acb_ptr res, acb_srcptr a1, acb_srcptr a2, slong g, slong prec) + + For each `0\leq k < 2^g`, sets the `k^{\mathrm{th}}` entry of *res* to + `2^{-g}\sum_{b\in \{0,1\}^g} a_{1,b}\, a_{2, b + k}`, where addition is + meant in `(\mathbb{Z}/2\mathbb{Z}^g)` (a bitwise xor). + + Following [LT2016]_, we apply the Hadamard matrix twice with + multiplications in-between. This causes precision losses when the absolute + values of the entries of *a1* and/or *a2* are of different orders of + magnitude. This function is faster when *a1* and *a2* are equal as + pointers, as we can use squarings instead of multiplications. + +.. function:: void acb_theta_agm_mul_tight(acb_ptr res, acb_srcptr a0, acb_srcptr a, arb_srcptr d0, arb_srcptr d, slong g, slong prec) + + Assuming that *d0* and *d* are obtained as the result of + :func:`acb_theta_dist_a0` on `(0,\tau)` and `(z,\tau)` respectively, + performs the same computation as :func:`acb_theta_agm_mul` on the vectors + *a0* and *a* with a different management of error bounds. The resulting + error bounds on *res* will be tighter when the absolute value of `a_k` is + roughly `e^{-d_k}` for each `0\leq k < 2^g`, and similarly for *a0* and + *d0*. + + When `g>1`, we manage the error bounds as follows. We compute `m, + \varepsilon` such that the following holds: for each `0\leq k < + \mathit{nb}`, if `d_k` (resp. `a_k`) denotes the `k^{\mathrm{th}}` entry of + *d* (resp. *a*), then the absolute value of `a_k` is at most `m \cdot + e^{-d_k}` and the radius of the complex ball `a_k` is at most + `\mathit{eps}\cdot e^{-d_k}`. We proceed similarly on *a0* and *d0* to + obtain `m_0, \varepsilon_0`. Then we call :func:`acb_theta_agm_mul` on the + midpoints of *a0* and *a* at a higher working precision, and finally add + `e^{-d_k} (m_0 \varepsilon + m \varepsilon_0 + \varepsilon\varepsilon_0)` + to the error bound on the `k^\mathrm{th}` entry of *res*. This is valid for + the following reason: keeping notation from :func:`acb_theta_dist_a0`, for + each `b\in \{0,1\}^g`, the sum + + .. math :: + + \mathrm{Dist}_\tau(-Y^{-1}y, \mathbb{Z}^g + \tfrac b2)^2 + + \mathrm{Dist}_\tau(-Y^{-1} y, \mathbb{Z}^g + \tfrac{b + k}{2})^2 + + is at most `\mathrm{Dist}_\tau(-Y^{-1}y, \mathbb{Z}^g + \tfrac{k}{2})^2` by + the parallelogram identity. + +Quasi-linear algorithms: main functions +------------------------------------------------------------------------------- + +The functions in this section will work best when `\tau` lies in the reduced +domain, however `\mathrm{Im}(\tau)` may have large eigenvalues. + +.. type:: acb_theta_ql_worker_t + + A function pointer type. A function *worker* of this type has the + following signature: + + .. function:: int worker(acb_ptr th, acb_srcptr t, acb_srcptr z, arb_scptr d0, arb_srcptr d, const acb_mat_t tau, slong guard, slong prec) + + Such a worker will attempt to set *th* to the values `\theta_{a,0}(v,\tau)` + for `v = 0,t,2t,z,z+t,z+2t` and `a\in \{0,1\}^g` at shifted absolute + precision *prec*, and return `1` on success and `0` on failure. The vectors + *d0* and *d* must contain the result of :func:`acb_theta_dist_a0` on + `(0,\tau)` and `(z,\tau)`. If `z = 0`, `t = 0`, or both, we only compute + `3`, `2`, or `1` vectors of `2^g` values respectively. + + Two functions of this type are available: :func:`acb_theta_ql_a0_naive` and + the main function :func:`acb_theta_ql_a0`. Using function pointers allows + us to write independent test code for the main workhorses + :func:`acb_theta_ql_a0_steps` and :func:`acb_theta_ql_a0_split` below. + +.. function:: int acb_theta_ql_a0_naive(acb_ptr th, acb_srcptr t, acb_srcptr z, arb_srcptr d0, arb_srcptr d, const acb_mat_t tau, slong guard, slong prec) + + Follows the specifications of a function of type + :type:`acb_theta_ql_worker_t` using the naive algorithm only. The return + value is `1` iff the output vector *th* contains finite values. + +.. function:: int acb_theta_ql_a0_split(acb_ptr th, acb_srcptr t, acb_srcptr z, arb_srcptr d, const acb_mat_t tau, slong s, slong guard, slong prec, acb_theta_ql_worker_t worker) + + Follows the specifications of a function of type + :type:`acb_theta_ql_worker_t`, except for the additional arguments *s* and + *worker*. We split the theta series according to the first `s` coordinates + of `n\in \mathbb{Z}^g`, writing `n = (n_0,n_1)` where `n_0\in \mathbb{Z}^s` + and `n_1\in \mathbb{Z}^{g - s}`. We must have `1\leq s\leq g -1`. Then + *worker* is called to evaluate the sum corresponding to each `n_1`. The + return value is 1 iff all the calls to *worker* succeed. + + For each `0\leq a < 2^g`, we compute *R2* and *eps* as in + :func:`acb_theta_naive_radius` at shifted absolte precision *prec*. Note + that `n^T \mathrm{Im}(\tau) n\geq \lVert C_1 n_1\rVert^2`, where `C_1` + denotes the lower-right block of `C` of dimensions + `(g-s)\times(g-s)`. Thus, in order to compute `\theta_{a,0}(z, 2^n\tau)` at + shifted absolute precision *prec*, it is enough to consider those `n_1\in + \mathbb{Z}^{g - s}` in an ellipsoid `E_1` of radius *R2* for the Cholesky + matrix `C_1`. This ellipsoid is meant to contain very few points, and we + list all of them. Then, for a given choice of `n_1`, the sum of the + corresponding terms in the theta series is + + .. math :: + + e^{\pi i \bigl((n_1 + \tfrac{a_1}{2})\tau_1 (n_1 + \tfrac{a_1}{2}) + 2 (n_1 + + \tfrac{a_1}{2}) z_1\bigr)} + \theta_{a_0,0}(z_0 + x (n_1 + \tfrac{a_1}{2}), \tau_0). + + where `\tau = \Bigl(\begin{smallmatrix} \tau_0 & x\\x^T & + \tau_1\end{smallmatrix}\Bigr)` and `z = (z_0,z_1)`. When calling *worker*, we + adjust the shifted absolute precision according to the distance between + `n_1` and the center of `E_1`. + +.. function:: int acb_theta_ql_a0_steps(acb_ptr th, acb_srcptr t, acb_srcptr z, arb_srcptr d0, arb_srcptr d, const acb_mat_t tau, slong nb_steps, slong s, slong guard, slong prec, acb_theta_ql_worker_t worker) + + Follows the specifications of a function of type + :type:`acb_theta_ql_worker_t`, except for the additional arguments + *nb_steps*, *s* and *worker*. We first compute low-precision approximations + (more precisely, at shifted absolute precision *guard*) of the square roots + we must take to perform *nb_steps* AGM steps; we hope that none of these + approximations contains zero. Then we call :func:`acb_theta_ql_a0_naive` or + :func:`acb_theta_ql_a0_split` (with the given *worker*) depending on + whether *s* is zero or not, and finally perform the AGM steps. The return + value is 1 iff each subprocedure succeeds. + + The user should ensure that the eigenvalues of + `2^{\mathit{nb\_steps}}\mathrm{Im}(\tau)` are not too large when calling + this function. + +.. function:: slong acb_theta_ql_a0_nb_steps(const arb_mat_t C, slong s, slong prec) + + Returns an integer `n` such that `2^n \gamma_s^2 \simeq \mathit{prec}` + where `\gamma_0,\ldots,\gamma_{g-1}` denote the diagonal coefficients of + `C`. This `n` is meant to be the number of AGM steps to use in + :func:`acb_theta_ql_a0_steps`, and its precise value is chosen to optimize + performance. We require `0\leq s < g`. + +.. function:: int acb_theta_ql_a0(acb_ptr th, acb_srcptr t, acb_srcptr z, arb_srcptr d0, arb_srcptr d, const acb_mat_t tau, slong guard, slong prec) + + Follows the specifications of a function of type + :type:`acb_theta_ql_worker_t`. + + We first decide how many AGM steps we should use and whether we should use + the splitting strategy. Then we run :func:`acb_theta_ql_a0_steps` on the + midpoints of `t,z` and `\tau` at a slightly higher precision to account for + precision losses in the duplication formulas, using a recursive call to + :func:`acb_theta_ql_a0` as *worker*. If the return value is 1, we finally + compute provable error bounds on the result using + :func:`acb_theta_jet_naive_fixed_ab` and + :func:`acb_theta_jet_error_bounds`. + +The function :func:`acb_theta_ql_a0` may fail for an unlucky choice of +auxiliary vector `t` or when *guard* is too small. Thus, we implement a +probabilistic algorithm where we gradually increase *guard* and first choose `t += 0`, then make a random choice of `t` at each step. + +.. function:: slong acb_theta_ql_reduce(acb_ptr new_z, acb_t c, arb_t u, slong * n1, acb_srcptr z, const acb_mat_t tau, slong prec) + + Sets *new_z*, *c*, *u*, *n1* and returns `-1\leq s\leq g` such that the + following holds. If `s\geq 0` is returned, then `z'` = *new_z* is a vector + of length `s` and `n_1` is a vector of length `g-s`, and for each + characteristic `(a,b)`, we have (borrowing notation from + :func:`acb_theta_ql_a0_split`): either + + .. math :: + + |\theta_{a,b}(z,\tau) - c i^{\,n_1^Tb_1} \theta_{a_0,b_0}(z', \tau_0)| \leq u + + when the last `g-s` coordinates of `a` equal `n_1` modulo 2, or + + .. math :: + + |\theta_{a,b}(z,\tau)|\leq u + + otherwise. If `s=-1` is returned, then *n1*, *c* and *new_z* are left + undefined and we have `\theta_{a,b}(z,\tau)\leq u` for all characteristics + `(a,b)`. This filters out very large eigenvalues of `\mathrm{Im}(\tau)` + that have a negligible impact on theta values but would give rise to + unreasonable choices of precisions in the final duplication formula for + computing all theta values `\theta_{a,b}`. + + This works as follows. We first compute *R2* and *eps* as in + :func:`acb_theta_naive_radius`, then set *c*, *u* and *new_z* as in + :func:`acb_theta_naive_reduce` in dimension `g`. We then set `s` such that + the ellipsoid `E` of radius `R^2` that we are interested in is either empty + or contains points whose `g-s` last coordinates are fixed. In the former + case, we return `s = -1`. Now assume that `E` is not empty, let `n_1` be + the vector of these fixed last `g-s` coordinates, and let `a_1\in + \{0,1\}^{g-s}` be the corresponding characteristic. We can then write the + sum defining `\theta_{a,b}` over `E` as + + .. math :: + + e^{\pi i (\tfrac{n_1^T}{2} \tau_1 \tfrac{n_1}{2} + n_1^T(z_1 + \tfrac{b_1}{2}))} + \sum_{n_0\in E_0 \cap (\mathbb{Z}^s + \tfrac{a_0}{2})} + e^{\pi i (n_0^T \tau_0 n_0 + 2n_0^T(z_0 + x \tfrac{n_1}{2} + \tfrac{b_0}{2}))} + + if the last `g-s` coordinates of `a` are equal to `n_1` modulo 2; the sum + is zero otherwise. Thus we can set `z'` to `z_0 + x\tfrac{n_1}{2}` and + multiply `c` by `\exp(\pi i (\tfrac{n_1^T}{2}\tau_1\tfrac{n_1}{2} + + n_1^Tz_1))`. + +.. function:: void acb_theta_ql_all(acb_ptr th, acb_srcptr z, const acb_mat_t tau, int sqr, slong prec) + + Sets *th* to the collection of `\theta_{a,b}(z,\tau)` or + `\theta_{a,b}(z,\tau)^2` for all `a,b\in \{0,1\}^g`, depending on whether + *sqr* is 0 (false) or nonzero (true). + + After calling :func:`acb_theta_ql_reduce`, we generally use the duplication + formula on the result of :func:`acb_theta_ql_a0` at `2\tau`. When *sqr* is + zero, we add a final square-root step. + +Quasi-linear algorithms: derivatives +------------------------------------------------------------------------------- + +We implement an algorithm for derivatives of theta functions on the reduced +domain based on finite differences. Consider the Taylor expansion: + + .. math :: + + \theta_{a,b}(z + h, \tau) + = \sum_{k\in \mathbb{Z}^g,\ k\geq 0} a_k\, h_0^{k_0}\cdots h_{g-1}^{k_{g-1}}. + +If one chooses `h = h_n = (\varepsilon \zeta^{n_0},\ldots, \varepsilon +\zeta^{n_{g-1}})` where `\varepsilon > 0` and `\zeta` is a primitive +`m^{\mathrm{th}}` root of unity and lets `n` run through all vectors in +`\{0,\ldots, m - 1\}^g`, then taking a discrete Fourier transform of the +resulting values will compute the individual Taylor coefficient for each +derivation tuple that is bounded by `m-1` elementwise. A constant proportion, +for fixed `g`, of this set consists of all tuples of total order at most +`m-1`. More precisely, fix `p\in \mathbb{Z}^g`. Then + + .. math :: + + \sum_{n\in \{0,\ldots,m-1\}^g} \zeta^{-p^T n} \theta_{a,b}(z + h_n, \tau) + = m^g \sum_{\substack{k\in \mathbb{Z}^g,\ k\geq 0,\\ k = p\ (\text{mod } m)}} + a_k\,\varepsilon^{|k|}. + +We obtain an upper bound on the tail of this series from the Cauchy integration +formula: if `|\theta_{a,b}(z,\tau)|\leq c` uniformly on a ball of radius `\rho` +centered in `z` for `\lVert\cdot\rVert_\infty`, then the sum is `m^g +(a_p\,\varepsilon^{|p|} + T)` with + + .. math :: + + |T|\leq 2c g\,\frac{\varepsilon^{|p|+m}}{\rho^m}. + +Since we divide by `\varepsilon^{|p|}` to get `a_p`, we will add an error of +`2c g \varepsilon^m/\rho^{m+|p|}` to the result of the discrete Fourier +transform. + +.. function:: void acb_theta_jet_ql_bounds(arb_t c, arb_t rho, acb_srcptr z, const acb_mat_t tau, slong ord) + + Sets *c* and *rho* such that on every ball centered at (a point contained + in) *z* of radius *rho*, the functions `|\theta_{a,b}|` for all + characteristics `(a,b)` are uniformly bounded by `c`. The choice of *rho* + is tuned to get interesting upper bounds on derivatives of `\theta_{a,b}` + up to order *ord*. + + We proceed as follows. First, we compute `c_0`, `c_1`, `c_2` such that for + any choice of `\rho`, one can take `c = c_0\exp((c_1 + c_2\rho)^2)` + above. We can take + + .. math :: + + c_0 = 2^g \prod_{j=0}^{g-1} (1 + 2\gamma_j^{-1}), + + .. math :: + + c_1 = \sqrt{\pi y^T Y^{-1} y}, + + .. math :: + + c_2 = \sup_{\lVert x \rVert_\infty\leq 1} + \sqrt{\pi x^T \mathrm{Im}(\tau)^{-1} x}. + + One can easily compute an upper bound on `c_2` from the Cholesky + decomposition of `\pi \mathrm{Im}(\tau)^{-1}`. We then look for a value of + `\rho` that minimizes `\exp((c_1 + c_2\rho)^2)/\rho^{2m-1}` where `m = + \mathit{ord}+1`, i.e. we set `\rho` to the positive root of `2c_2\rho + (c_1 + c_2\rho) = 2m-1`. + +.. function:: void acb_theta_jet_ql_radius(arf_t eps, arf_t err, const arb_t c, const arb_t rho, slong ord, slong g, slong prec) + + Sets *eps* and *err* to be a suitable radius and error bound for computing + derivatives up to total order *ord* at precision *prec*, given *c* and + *rho* as above. + + We set `varepsilon` such that `(2 g)^{1/m} \varepsilon \leq \rho` and `2 c + g \varepsilon^m/\rho^{m+|p|} \leq 2^{-\mathit{prec}}` where `m = + \mathit{ord} + 1`. We also set *err* to `2^{-\mathit{prec}}`. + +.. function:: void acb_theta_jet_ql_finite_diff(acb_ptr dth, const arf_t eps, const arf_t err, acb_srcptr val, slong ord, slong g, slong prec) + + Assuming that *val* contains the values `\theta_{a,b}(z + h_n,\tau)` where + `h_n = (\varepsilon \zeta^{n_0},\ldots, \varepsilon \zeta^{n_{g-1}})` for a + root of unity `\zeta` of order `\mathit{ord} + 1`, and assuming that *eps* + and *err* has been computed as in :func:`acb_theta_jet_ql_radius`, sets + *dth* to the vector of partial derivatives of `\theta_{a,b}` at `(z,\tau)` + up to total order *ord*. The vector *val* should be indexed in + lexicographic order as in :func:`acb_dft`, i.e. writing `j = + \overline{a_{g-1}\cdots a_0}` in basis `m`, the `j^{\mathrm{th}}` entry of + *val* corresponds to `n = (a_0,\ldots, a_{g-1})`. The output derivatives + are normalized as in the Taylor expansion. + +.. function:: void acb_theta_jet_ql_all(acb_ptr dth, acb_srcptr z, const acb_mat_t tau, slong ord, slong prec) + + Sets *dth* to the derivatives of all functions `\theta_{a,b}` for `a,b\in + \{0,1\}^g` at `(z,\tau)`, as a concatenation of `2^{2g}` vectors of length + `N`, the total number of derivation tuples of total order at most + *ord*. This algorithm runs in quasi-linear time in `\mathit{prec}\cdot + \mathit{ord}^{\,g}` for any fixed `g` provided that `(z,\tau)` is reduced. + + We first compute *c*, *rho*, *err* and *eps* as above, then compute theta + values `\theta_{a,b}(z + h_n,\tau)` at a higher precision at the midpoints + of `z` and `\tau` to account for division by + `\varepsilon^{\mathit{ord}}\cdot (\mathit{ord}+1)^g`. Finally, we adjust + the error bounds using :func:`acb_theta_jet_error_bounds` and the naive + algorithm for derivatives of order at most `\mathit{ord} + 2`. + +The transformation formula +------------------------------------------------------------------------------- + +The functions in this section implement the theta transformation formula of +[Igu1972]_, p. 176 and [Mum1983]_, p. 189: for any symplectic matrix `m`, any +`(z,\tau)\in \mathbb{C}^g\times \mathbb{H}_g`, and any characteristic `(a,b)`, +we have + + .. math :: + + \theta_{a,b}(m\cdot(z,\tau)) = \kappa(m) \zeta_8^{e(m, a, b)} \det(\gamma\tau + \delta)^{1/2} e^{\pi i z^T (\gamma\tau + \delta)^{-1} \gamma z} \theta_{a',b'}(z,\tau) + +where + +- `\gamma,\delta` are the lower `g\times g` blocks of `m`, +- `a',b'` is another characteristic depending on `m,a,b`, +- `\zeta_8=\exp(i\pi/4)`, +- `e(m,a,b)` is an integer given by an explicit formula in terms of + `m,a,b` (this is `\phi_m` in Igusa's notation), and +- `\kappa(m)` is an `8^{\mathrm{th}}` root of unity, only well-defined up to + sign unless we choose a particular branch of `\det(\gamma\tau + + \delta)^{1/2}` on `\mathbb{H}_g`. + +.. function:: ulong acb_theta_transform_char(slong * e, const fmpz_mat_t mat, ulong ab) + + Returns the theta characteristic `(a',b')` and sets *e* to `e(\mathit{mat},a,b)`. + +.. function:: void acb_theta_transform_sqrtdet(acb_t res, const acb_mat_t tau, slong prec) + + Sets *res* to `\det(\tau)^{1/2}`, where the branch of the square root is + chosen such that the result is `i^{g/2}\det(Y)` when `\tau = iY` is purely + imaginary. + + We pick a purely imaginary matrix *A* and consider the polynomial `P(t) = + \det(A + \tfrac{t+1}{2} (\tau - A))`. Up to choosing another `A`, we may assume that + it has degree `g` and that its roots (as complex balls) do not intersect + the segment `[-1,1]\subset \mathbb{C}`. We then find the correct branch of + `P(t)^{1/2}` between `t=-1` and `t=1` following [MN2019]_. + +.. function:: slong acb_theta_transform_kappa(acb_t sqrtdet, const fmpz_mat_t mat, const acb_mat_t tau, slong prec) + + Returns `0\leq r < 8` such that `\kappa(m) = \zeta_8^r` and sets *sqrtdet* + to the corresponding square root of `\det(\gamma\tau + \delta)`. + + After applying :func:`sp2gz_decompose`, we only have to consider four + special cases for *mat*. If *mat* is trigonal or block-diagonal, one can + compute its action on `\theta_{0,0}` directly. If *mat* is an embedded + matrix from `\mathrm{SL}_2(\mathbb{Z})`, we rely on + :func:`acb_modular_theta_transform`. Finally, if *mat* is an embedded `J` + matrix from dimension `0\leq r\leq g`, then `\kappa(m) \zeta_8^{e(m,0,0)} + i^{r/2} \det(\tau_0)^{1/2} = 1`, where `\tau_0` denotes the upper left `r\times + r` submatrix of `\tau` and the square root is computed as in + :func:`acb_theta_transform_sqrtdet`. + +.. function:: slong acb_theta_transform_kappa2(const fmpz_mat_t mat) + + Returns `0\leq r < 3` such that `\kappa(m)^2 = i^r`, which makes sense + without reference to a branch of `\det(\gamma\tau + \delta)^{1/2}`. + + We adopt a similar strategy to :func:`acb_theta_transform_kappa` but do not + call :func:`acb_theta_transform_sqrtdet`. + +.. function:: void acb_theta_transform_proj(acb_ptr res, const fmpz_mat_t mat, acb_srcptr th, int sqr, slong prec) + + Assuming that *sqr* is 0 (false) and that *th* contains + `\theta_{a,b}(z,\tau)` for some `(z,\tau)`, sets *res* to contain the + values `\theta_{a,b}(\mathit{mat}\cdot (z,\tau))` up to a common scalar + factor in `\mathbb{C}^\times`. This only permutes the theta values and + multiplies them by a suitable `8^{\mathrm{th}}` root of unity. If *sqr* is + nonzero (true), does the same computation for squared theta values + `\theta_{a,b}(z,\tau)^2` instead. + + In :func:`acb_theta_all` and :func:`acb_theta_jet_all`, we first reduce + `\tau` using :func:`acb_siegel_reduce`, then call :func:`acb_theta_ql_all`, + or :func:`acb_theta_jet_ql_all` on the reduced matrix, and finally apply + the transformation formula. If the reduction step is not successful, we set + the result to indeterminate values. + +Dimension 2 specifics +------------------------------------------------------------------------------- + +In the `g=2` case, one can use theta functions to evaluate many fundamental +Siegel modular forms. This section contains methods to do so, in analogy with +:func:`acb_modular_delta` or :func:`acb_modular_eisenstein` when `g=1`. + +We use the following notation. Fix `k,j\geq 0`. A Siegel modular form of weight +`\det^k\otimes \mathrm{Sym}^j` is by definition an analytic function +`f: \mathbb{H}_g\to \mathbb{C}_j[X]` (the vector space of polynomials of degree +at most `j`) such that for any `\tau\in \mathbb{H}_g` and +`m\in \mathrm{Sp}_4(\mathbb{Z})`, we have + + .. math :: + + f((\alpha\tau + \beta)(\gamma\tau + \delta)^{-1}) = \det(\gamma\tau + + \delta)^k\cdot \mathrm{Sym}^j(\gamma\tau + \delta)(f(\tau)). + +Here `\alpha,\beta,\gamma,\delta` are the `g\times g` blocks of `m`, and the +notation `\mathrm{Sym}^j(r)` where `r = \bigl(\begin{smallmatrix} a & b\\ c & +d\end{smallmatrix}\bigr)\in \mathrm{GL}_2(\mathbb{C})` stands for the map + + .. math :: + + P(X) \mapsto (b X + d)^j P\bigl(\tfrac{a X + c}{b X + d}\bigr). + +For a nonzero `f` to exist, `j` must be even. + +Siegel modular forms generate a bi-graded ring which is not finitely +generated. However, if we relax the definition of a Siegel modular form and +allow them to have a pole along the diagonal `\mathbb{H}_1^2 = +\bigl\{\bigl(\begin{smallmatrix} \tau_1 & 0 \\ 0 & +\tau_2\end{smallmatrix}\bigr)\bigr\}\subset \mathbb{H}_2` of a certain order +(depending on the weight), we indeed find a finitely generated ring +corresponding to classical "covariants" of a binary sextic. Historically, +covariants are classified in terms of their degree `k` and index `j`, +corresponding to Siegel modular functions of weight `\det^{k - j/2}\otimes +\mathrm{Sym}^j`. See [CFG2017]_ for more details on the correspondence between +modular forms and covariants. + +.. macro:: ACB_THETA_G2_COV_NB + + Macro giving the number of generators of the ring of covariants, equal to 26. + +.. function:: void acb_theta_g2_jet_naive_1(acb_ptr dth, const acb_mat_t tau, slong prec) + + Sets *dth* in the same way as :func:`acb_theta_jet_naive_all` at order 1 + for `z=0`. + + We take advantage of the fact that the value (resp. gradients) of + `\theta_{a,b}(z,\tau)` at `z = 0` vanish if `(a,b)` is an odd (resp. even) + characteristic. The attached worker of type + :type:`acb_theta_naive_worker_t` uses one of two available strategies + (doing multiplications and then summing, or calling :func:`acb_dot` twice) + depending on *prec*. + +.. function:: void acb_theta_g2_detk_symj(acb_poly_t res, const acb_mat_t m, const acb_poly_t f, slong k, slong j, slong prec) + + Sets *res* to `\det(m)^k \mathrm{Sym}^j(m)(f)`. The polynomial `f` should + be of degree at most `j` (any coefficients of larger degree are ignored). + +.. function:: void acb_theta_g2_transvectant(acb_poly_t res, const acb_poly_t g, const acb_poly_t h, slong m, slong n, slong k, slong prec) + + Sets *res* to the `k^{\mathrm{th}}` transvectant of the polynomials `g` and + `h` of degrees `m` and `n`: considering `g` and `h` as homogeneous + polynomials of degree `m` (resp. `n`) in `x_1,x_2`, this sets *res* to + + .. math :: + + (g,h)_k := \frac{(m-k)!(n-k)!}{m!n!} \sum_{j=0}^{k} (-1)^{k-j} \binom{k}{j} + \frac{\partial^k g}{\partial x_1^{k-j}\partial x_2^j} + \frac{\partial^k h}{\partial x_1^{j}\partial x_2^{k-j}}. + + Any coefficients of `g` or `h` of larger degree than `m` (resp. `n`) are + ignored. + +.. function:: void acb_theta_g2_transvectant_lead(acb_t res, const acb_poly_t g, const acb_poly_t h, slong m, slong n, slong k, slong prec) + + Sets *res* to the leading coefficient of `(g,h)_k` in `x_1`, with the same + conventions as in :func:`acb_theta_g2_transvectant`. + +.. function:: slong acb_theta_g2_character(const fmpz_mat_t mat) + + Returns the value in `\mathbb{Z}/2\mathbb{Z}` (0 or 1) of the unique + nontrivial character of `\mathrm{Sp}_4(\mathbb{Z})` at *mat*, following + [CFG2019]_, §12. + +.. function:: void acb_theta_g2_psi4(acb_t res, acb_srcptr th2, slong prec) + +.. function:: void acb_theta_g2_psi6(acb_t res, acb_srcptr th2, slong prec) + +.. function:: void acb_theta_g2_chi10(acb_t res, acb_srcptr th2, slong prec) + +.. function:: void acb_theta_g2_chi12(acb_t res, acb_srcptr th2, slong prec) + + Sets *res* to the value of the Eisenstein series `\psi_4`, `\psi_6` or the + cusp forms `\chi_{10}, \chi_{12}` corresponding to the given vector *th2* of + squared theta values (of length 16). + + We use the formulas from §7.1 in [Str2014]_, with the following normalizations: + + .. math :: + + \psi_4 = h_4/4, \quad \psi_6 = h_6/4,\quad \chi_{10} = -2^{-12} h_{10}, + \quad \chi_{12} = 2^{-15}h_{12}. + + We warn that `\chi_{10}` and `\chi_{12}` differ from the classical notation + of Igusa [Igu1979]_ by scalar factors. Writing `\tau = + \bigl(\begin{smallmatrix} \tau_1 & \tau_2 \\ \tau_2 & + \tau_3\end{smallmatrix}\bigr)` and `q_j = \exp(2\pi i \tau_j)`, the Fourier + expansions of these modular forms begin as follows: + + .. math :: + + \begin{aligned} \psi_4(\tau) &= 1 + 240(q_1 + q_3) + \cdots\\ + \psi_6(\tau) &= 1 - 504(q_1 + q_3) + \cdots\\ + \chi_{10}(\tau) &= (q_2 - 2 + q_2^{-1}) q_1 q_3 + \cdots\\ + \chi_{12}(\tau) &= (q_2 + 10 + q_2^{-1}) q_1 q_3 + \cdots. + \end{aligned} + +.. function:: void acb_theta_g2_chi5(acb_t res, acb_srcptr th, slong prec) + + Sets *res* to the value of `\chi_5 = - 2^{-6} \prod_{(a,b)\text{ even}} + \theta_{a,b}` corresponding to the given theta values *th*. The form + `\chi_5` is a Siegel cusp form with character: see [CFG2019]_ for more + details. + +.. function:: void acb_theta_g2_chi35(acb_t res, acb_srcptr th, slong prec) + + Sets *res* to the value of the cusp form `\chi_{35}` corresponding to the vector + of theta values *th*. The form `\chi_{35}` is the unique scalar-valued Siegel + modular form of weight `\det^{35}\otimes \mathrm{Sym}^0` up to scalars, and is + normalized as follows: + + .. math :: + + \chi_{35}(\tau) = q_1^2 q_3^2 (q_1 - q_3 )(q_2 - q_2^{-1}) + \cdots + + An explicit formula for `\chi_{35}` in terms of theta values is given in + [Bol1887]_. See also [Mum1984]_, Prop. 6.2 p. 98 for how to translate + Bolza's notation in terms of theta characteristics. + +.. function:: void acb_theta_g2_chi3_6(acb_poly_t res, acb_srcptr dth, slong prec) + + Sets *res* to the value of the vector-valued cusp form with character + `\chi_{6,3}` of weight `\det^3\otimes \mathrm{Sym}^6` corresponding to the + given values of *dth*, computed as in e.g. + :func:`acb_theta_g2_jet_naive_1`. We have by [CFG2017]_: + + .. math :: + + \chi_{3,6}(\tau) = \frac{1}{64\pi^6} \prod_{(a,b) \text{ odd}} + \left(\frac{\partial \theta_{a,b}}{\partial z_1}(0,\tau) x_1 + + \frac{\partial\theta_{a,b}}{\partial z_2}(0,\tau) x_2\right). + +.. function:: void acb_theta_g2_sextic(acb_poly_t res, const acb_mat_t tau, slong prec) + + Sets *res* to the value of `\chi_{-2,6}:=\chi_{3,6}/\chi_5` at `\tau`. We + reduce `\tau` to the Siegel fundamental domain and call either + :func:`acb_theta_g2_jet_naive_1` or :func:`acb_theta_jet_ql_all` to compute + theta gradients, depending on *prec*. Under the correspondence between + Siegel modular functions and covariants of binary sextics, `\chi_{-2,6}` + corresponds to the binary sextic itself, hence the name. + +.. function:: void acb_theta_g2_sextic_chi5(acb_poly_t res, acb_t chi5, const acb_mat_t tau, slong prec) + + Sets *res* and *chi5* to the values of `\chi_{-2,6}` and `\chi_5` at + `\tau`. Theta values are computed only once. + +.. function:: void acb_theta_g2_covariants(acb_poly_struct * res, const acb_poly_t f, slong prec) + + Sets *res* to the vector of 26 generators of the ring of covariants + evaluated at the sextic *f* (any terms of degree `>6` are ignored), in the + following order: + + 0. `C_{1,6}=f` + 1. `C_{2,0}= 60(f,f)_6` + 2. `C_{2,4}= 75(f,f)_4` + 3. `C_{2,8}= 90(f,f)_2` + 4. `C_{3,2}= 30(f,C_{2,4})_4` + 5. `C_{3,6}= 30(f,C_{2,4})_2` + 6. `C_{3,8}= 6(f,C_{2,4})_1` + 7. `C_{3,12}= 6 (f,C_{2,8})_1` + 8. `C_{4,0}= 2 (C_{2,4},C_{2,4})_4` + 9. `C_{4,4}= 30 (f,C_{3,2})_2` + 10. `C_{4,6}= 6(f,C_{3,2})_1` + 11. `C_{4,10}= 2(C_{2,8},C_{2,4})_1` + 12. `C_{5,2}=(C_{2,4},C_{3,2})_2` + 13. `C_{5,4}=\frac 25 (C_{2,4},C_{3,2})_1` + 14. `C_{5,8}= 2(C_{2,8},C_{3,2})_1` + 15. `C_{6,0}= 2(C_{3,2},C_{3,2})_2` + 16. `C_{6,6}^{(1)}= \frac 25(C_{3,6},C_{3,2})_1` + 17. `C_{6,6}^{(2)}= \frac 83(C_{3,8},C_{3,2})_2` + 18. `C_{7,2}= 30(f,C_{3,2}^2)_4` + 19. `C_{7,4}= 12(f,C_{3,2}^2)_3` + 20. `C_{8,2}= \frac 25(C_{2,4},C_{3,2}^2)_3` + 21. `C_{9,4}= 4(C_{3,8},C_{3,2}^2)_4` + 22. `C_{10,0}= 20(f,C_{3,2}^3)_6` + 23. `C_{10,2}= \frac 65(f,C_{3,2}^3)_5` + 24. `C_{12,2}= \frac 85(C_{3,8},C_{3,2}^3)_6` + 25. `C_{15,0}= \frac{1}{30000} (C_{3,8},C_{3,2}^4)_8`. + + The scalar factors are chosen so that when evaluated at a formal sextic `f + = \sum a_i x_1^{6-i}x_2^i`, the covariants are integral and primitive as + multivariate polynomials in `a_0,\ldots,a_6,x_1,x_2`. + +.. function:: void acb_theta_g2_covariants_lead(acb_ptr res, const acb_poly_t f, slong prec) + + Sets *res* to the vector of leading coefficients in `x_1` of the 26 + covariants evaluated at *f*. This is more efficient than taking leading + coefficients of :func:`acb_theta_g2_covariants`, since we can use + :func:`acb_theta_g2_transvectant_lead` instead of + :func:`acb_theta_g2_transvectant`. + +Tests +------------------------------------------------------------------------------- + +.. code-block:: bash + + ./build/acb_theta/test/main sp2gz_set_blocks + +Generates a random `2g\times 2g` matrix, calls :func:`sp2gz_set_blocks` on its +four `g\times g` windows, and checks that the result equals the original +matrix. + +.. code-block:: bash + + ./build/acb_theta/test/main sp2gz_is_correct + +Checks that the return value of :func:`sp2gz_is_correct` is 1 on matrices +generated by :func:`sp2gz_j`, :func:`sp2gz_block_diag`, :func:`sp2gz_trig` and +:func:`sp2gz_fundamental`, and 0 on the identity matrix if it is not square of +even size. + +.. code-block:: bash + + ./build/acb_theta/test/main sp2gz_inv + +Checks that the result of :func:`sp2gz_inv` agrees with :func:`fmpz_mat_inv` on +random input. + +.. code-block:: bash + + ./build/acb_theta/test/main sp2gz_decompose + +Checks that the result of :func:`sp2gz_decompose` on random input only consists +of symplectic matrices of the allowed types, and that their product equals the +original matrix. + +.. code-block:: bash + + ./build/acb_theta/test/main acb_siegel_cocycle + +Checks that the chain rule holds: if `m'' = m'm` is a product of two symplectic +matrices and `\tau\in \mathbb{H}_g`, then `\gamma''\tau + \delta'' = +(\gamma'\tau' + \delta')(\gamma\tau+\delta)` where `\tau' = m\tau`. These +quantities are computed using :func:`acb_siegel_cocycle` and +:func:`acb_siegel_transform`. + +.. code-block:: bash + + ./build/acb_theta/test/main acb_siegel_transform + +Checks that the chain rule holds, i.e. :func:`acb_siegel_transform` defines an +action of the group `\mathrm{Sp}_{2g}(\mathbb{Z})` on `\mathbb{H}_g`. + +.. code-block:: bash + + ./build/acb_theta/test/main acb_siegel_transform_z + +Checks that :func:`acb_siegel_transform` and :func:`acb_siegel_transform_z` +agree on random input, and that :func:`acb_siegel_transform_z` on the inverse +of any matrix yields the inverse transformation. + +.. code-block:: bash + + ./build/acb_theta/test/main acb_siegel_reduce + +Generates an input matrix `\tau` at a working precision that is not too low +compared to the size of its coefficients, and calls :func:`acb_siegel_reduce`. +Checks that the resulting matrix `m` is symplectic and that `m\tau` is reduced +with a tolerance of `2^{-10}` using :func:`acb_siegel_is_reduced`. + +.. code-block:: bash + + ./build/acb_theta/test/main acb_siegel_is_reduced + +Checks that :func:`acb_siegel_is_reduced` returns 1 on the matrix `i I_g`, but +0 on other matrices specially constructed to not be reduced. + +.. code-block:: bash + + ./build/acb_theta/test/main acb_theta_char_get_a + +Generates a random characteristic *a*, sets *n* to the result of +:func:`acb_theta_char_get_slong` on *a*, and checks that the result of +:func:`acb_theta_char_get_a` on *n* gives back *a*. + +.. code-block:: bash + + ./build/acb_theta/test/main acb_theta_char_dot + +Checks that dot products computed by :func:`acb_theta_char_dot`, +:func:`acb_theta_char_dot_slong` and :func:`acb_theta_char_dot_acb` agree on +random input. + +.. code-block:: bash + + ./build/acb_theta/test/main acb_theta_char_is_even + +Checks that the 10 even theta characteristics for `g=2` are 0, 1, 2, 3, 4, 6, +8, 9, 12, 15. + +.. code-block:: bash + + ./build/acb_theta/test/main acb_theta_char_is_goepel + +Checks that there are exactly 15 Göpel quadruples for `g=2`. + +.. code-block:: bash + + ./build/acb_theta/test/main acb_theta_char_is_syzygous + +Checks that there are exactly 60 syzygous triples for `g=2`. + +.. code-block:: bash + + ./build/acb_theta/test/main acb_theta_eld_points + +Generates a random ellipsoid *E* using :func:`acb_theta_eld_set`, computes its +points using :func:`acb_theta_eld_points`, and checks that each of these points +lies within the box specified by :macro:`acb_theta_eld_box`. Then, generates +random points *pt*: if *pt* is in *E* according to +:func:`acb_theta_eld_contains`, then *pt* must appear in the list of points, +otherwise the norm of *pt* according to the chosen Cholesky matrix must be at +least the radius of *E*. + +.. code-block:: bash + + ./build/acb_theta/test/main acb_theta_eld_border + +Generates a random ellipsoid *E*, computes its border using +:func:`acb_theta_eld_border`, and checks that none of these border points lie +in *E* nor any of its children. + +.. code-block:: bash + + ./build/acb_theta/test/main acb_theta_naive_radius + +Generates a reduced matrix `\tau` in `\mathbb{H}_g` and vector `z\in +\mathbb{C}^g`, calls :func:`acb_theta_naive_radius`, constructs the associated +ellipsoid *E*, and checks that the sums of absolute values of terms of the +theta series on the border of *E* is at most the specified bound. + +.. code-block:: bash + + ./build/acb_theta/test/main acb_theta_naive_reduce + +Checks that the results of :func:`acb_theta_naive_reduce` are sound on some +special values of the input, namely when *zs* has only real entries and when +`\mathrm{Im}(z) = -\mathrm{Im}(\tau) n + \varepsilon` where *n* is an even +integral vector and `\varepsilon` is small. + +.. code-block:: bash + + ./build/acb_theta/test/main acb_theta_naive_term + +Checks that the result of :func:`acb_theta_naive_term` is `n^k +\exp(i\pi(n^2\tau + 2nz))` in the `g=1` case. + +.. code-block:: bash + + ./build/acb_theta/test/main acb_theta_naive_00 + +Checks that the ouput of :func:`acb_theta_naive_00` overlaps the first entry of +the output of :func:`acb_theta_naive_0b`. + +.. code-block:: bash + + ./build/acb_theta/test/main acb_theta_naive_all + +Checks that the results of :func:`acb_theta_naive_all` agree with +:func:`acb_modular_theta` as follows: if the input matrix `\tau` is diagonal +with coefficients `\tau_0,\ldots, \tau_{g-1}`, then for all characteristics +`(a,b)` and vectors `z`, we have + + .. math:: + + \theta_{a,b}(z,\tau) = \prod_{j=0}^{g-1} \theta_{a_j,b_j}(z_j,\tau_j). + +.. code-block:: bash + + ./build/acb_theta/test/main acb_theta_naive_fixed_a + +Checks that the output of :func:`acb_theta_naive_fixed_a` overlaps the relevant +entries of :func:`acb_theta_naive_all` on random input. + +.. code-block:: bash + + ./build/acb_theta/test/main acb_theta_naive_fixed_ab + +Checks that the output of :func:`acb_theta_naive_fixed_ab` overlaps the relevant +entries of :func:`acb_theta_naive_all` on random input. + +.. code-block:: bash + + ./build/acb_theta/test/main acb_theta_jet_tuples + +For random *g* and *ord*, generates the list of derivation tuples using +:func:`acb_theta_jet_tuples`, picks an index `i` at random, and checks that the +result of :func:`acb_theta_jet_index` on the `i^{\mathrm{th}}` tuple is indeed +`i`. + +.. code-block:: bash + + ./build/acb_theta/test/main acb_theta_jet_mul + +Checks that the results of :func:`acb_theta_jet_mul` agrees with the result of +:func:`fmpz_mpoly_mul` on any input with integral entries. + +.. code-block:: bash + + ./build/acb_theta/test/main acb_theta_jet_compose + +Checks that the chain rule holds: if `N_3 = N_2 N_1`, then applying +:func:`acb_theta_jet_compose` with `N_2`, then `N_1` corresponds to applying +:func:`acb_theta_jet_compose` with `N_3` directly. + +.. code-block:: bash + + ./build/acb_theta/test/main acb_theta_jet_naive_radius + +Generates a reduced matrix `\tau` in `\mathbb{H}_g` and vector `z\in +\mathbb{C}^g`, chooses a random order of derivation, calls +:func:`acb_theta_jet_naive_radius`, constructs the associated ellipsoid *E*, +and checks that the sums of absolute values of terms of the differentiated +theta series on the border of *E* is at most the specified bound. + +.. code-block:: bash + + ./build/acb_theta/test/main acb_theta_jet_naive_all + +Checks that the results of :func:`acb_theta_jet_naive_all` agree with +:func:`acb_modular_theta_jet` as follows: if the input matrix `\tau` is +diagonal with coefficients `\tau_0,\ldots, \tau_{g-1}`, then for all +characteristics `(a,b)`, any vector `z`, and any derivation tuple +`(k_0,\ldots,k_{g-1})`, we have + + .. math:: + + \frac{\partial^{|k|} \theta_{a,b}} {\partial z_0^{k_0}\cdots \partial + z_{g-1}^{k-1}}(z,\tau) = \prod_{j=0}^{g-1} + \frac{\partial^{k_j}\theta_{a_j,b_j}}{\partial z^{k_j}}(z_j,\tau_j). + +.. code-block:: bash + + ./build/acb_theta/test/main acb_theta_jet_naive_00 + +Checks that the output of :func:`acb_theta_jet_naive_00` agrees with the +relevant entries of :func:`acb_theta_jet_naive_all` on random input. + +.. code-block:: bash + + ./build/acb_theta/test/main acb_theta_jet_naive_fixed_ab + +Checks that the output of :func:`acb_theta_jet_naive_fixed_ab` agrees with the +relevant entries of :func:`acb_theta_jet_naive_all` on random input. + +.. code-block:: bash + + ./build/acb_theta/test/main acb_theta_jet_error_bounds + +Generates two pairs `(z_1,\tau_1)` and `(z_2,\tau_2)` close to each other but +not overlapping, sets `(z,\tau)` to be their reunion (as complex balls on each +coefficient), and calls :func:`acb_theta_jet_error_bounds` on `(z,\tau)` for +some choice of derivation order. The difference between the results of +:func:`acb_theta_jet_naive_all` on `(z_1,\tau_1)` and `(z_2,\tau_2)` must then +be at most two times the computed error. + +.. code-block:: bash + + ./build/acb_theta/test/main acb_theta_dist_pt + +Checks that for a random Cholesky matrix `C` and integral vectors `n_1,n_2`, +the results of :func:`acb_theta_dist_pt` on `(v,n) = (Cn_1, n_2)` and `(Cn_2, +n_1)` agree. + +.. code-block:: bash + + ./build/acb_theta/test/main acb_theta_dist_lat + +Picks a random Cholesky matrix `C` and vector `v`, calls +:func:`acb_theta_dist_lat`, and computes the ellipsoid *E* whose radius is the +computed distance. Checks that *E* contains at least one point and that the +minimum distance is correct by looping over all the points in *E*. + +.. code-block:: bash + + ./build/acb_theta/test/main acb_theta_dist_a0 + +Checks that when `z = \mathrm{Im}(\tau) \tfrac{a}{2}` for some theta +characteristic `a`, the result of :func:`acb_theta_dist_a0` on `(z,\tau)` +contains zero in its `a^{\mathrm{th}}` entry. + +.. code-block:: bash + + ./build/acb_theta/test/main acb_theta_agm_hadamard + +Checks that calling :func:`acb_theta_agm_hadamard` twice on random input is +equivalent to multiplying by `2^g`. + +.. code-block:: bash + + ./build/acb_theta/test/main acb_theta_agm_sqrt + +Generates a random complex number *t*, sets *rts* to a low-precision rounding +of *t* (possibly containing zero), and sets *a* to the square of *t*. Checks +that the result of :func:`acb_theta_agm_sqrt` on this input is finite, contains +*t*, and that the precision loss is small when *rts* does not contain zero. + +.. code-block:: bash + + ./build/acb_theta/test/main acb_theta_agm_mul + +Checks that the duplication formula holds: the result of +:func:`acb_theta_agm_mul` on vectors containing `\theta_{0,b}(0,\tau)` and +`\theta_{0,b}(z,\tau)` for all `b\in\{0,1\}^g` and any choice of `(z,\tau)` +contains the squared theta values `\theta_{0,b}^2(2z,2\tau)`. + +.. code-block:: bash + + ./build/acb_theta/test/main acb_theta_agm_mul_tight + +Generates random `\tau` and `z` at working precision *prec*, computes the +associated vectors of distances *d0* and *d* using :func:`acb_theta_dist_a0`, +and constructs vectors *a0* and *a* with entries of the form `x e^{-t}` where +`x` is uniformly random with `|x|\leq 1` (generated by :func:`acb_urandom`) and +*t* is the corresponding entry of *d0* (resp. *d*). Calls +:func:`acb_theta_agm_mul_tight` at a lower precision *mprec*. For each `0\leq +k< 2^g`, checks that the absolute value of `k^{\mathrm{th}}` entry of the +result *res* is at most `e^{-d_k}`, and that the error bound on that entry is +at most `2^{-\mathit{mprec} + \delta} e^{-d_k}` for a reasonable value of +`\delta` (e.g. 25). + +.. code-block:: bash + + ./build/acb_theta/test/main acb_theta_ql_a0_split + +Checks that the result of :func:`acb_theta_ql_a0_split` (using +:func:`acb_theta_ql_a0_naive` as *worker*) agrees with that of +:func:`acb_theta_ql_a0_naive` in case of success. + +.. code-block:: bash + + ./build/acb_theta/test/main acb_theta_ql_a0_steps + +Checks that the result of :func:`acb_theta_ql_a0_steps` (using +:func:`acb_theta_ql_a0_naive` as *worker*) agrees with that of +:func:`acb_theta_ql_a0_naive` in case of success. + +.. code-block:: bash + + ./build/acb_theta/test/main acb_theta_ql_a0 + +Checks that :func:`acb_theta_ql_a0`, if successful, agrees with +:func:`acb_theta_ql_a0_naive` on random input. + +.. code-block:: bash + + ./build/acb_theta/test/main acb_theta_ql_reduce + +Generates random values `\tau` and `z` in such a way that +:func:`acb_theta_ql_reduce` is likely to output `s < g` and a nonzero *n1*, and +checks that the claimed inequalities in that function's documentation hold when +computing theta values using :func:`acb_theta_naive_all`. + +.. code-block:: bash + + ./build/acb_theta/test/main acb_theta_ql_all + +Checks that :func:`acb_theta_ql_all` agrees with :func:`acb_theta_naive_all` on +random input. + +.. code-block:: bash + + ./build/acb_theta/test/main acb_theta_jet_ql_bounds + +Generates random `(z,\tau)` at a working precision that is not too low and +calls :func:`acb_theta_jet_ql_bounds` to compute the bounds *c* and +*rho*. Checks that they are finite and that their definition is satisfied by +sampling theta values on the corresponding neighborhood of `z` at low +precisions with :func:`acb_theta_naive_all`. + +.. code-block:: bash + + ./build/acb_theta/test/main acb_theta_jet_ql_radius + +Checks that the result of :func:`acb_theta_jet_ql_radius` on random input +satisfies the required inequalities. + +.. code-block:: bash + + ./build/acb_theta/test/main acb_theta_jet_ql_finite_diff + +Checks that :func:`acb_theta_jet_ql_finite_diff` computes the correct Taylor +coefficients for the function `\exp(z_0+\cdots+z_{g-1})` at zero. Correct input +can be generated by :func:`acb_theta_jet_ql_radius`, as the bounds *c* and +*rho* can be computed directly for this function. + +.. code-block:: bash + + ./build/acb_theta/test/main acb_theta_jet_ql_all + +Checks that :func:`acb_theta_jet_ql_all` agrees with +:func:`acb_theta_jet_naive_all` on random input. + +.. code-block:: bash + + ./build/acb_theta/test/main acb_theta_transform_char + +Checks that the `a` component of any theta characteristic remains the same +after applying :func:`acb_theta_transform_char` when the symplectic matrix is +trigonal as in :func:`sp2gz_trig`. + +.. code-block:: bash + + ./build/acb_theta/test/main acb_theta_transform_sqrtdet + +Checks that the result of :func:`acb_theta_transform_sqrtdet` on any input +`\tau\in \mathbb{H}_g` squares to `\det(\tau)`. + +.. code-block:: bash + + ./build/acb_theta/test/main acb_theta_transform_kappa + +Checks that :func:`acb_theta_transform_kappa` and +:func:`acb_theta_transform_kappa2` agree on random input (i.e. they are +congruent modulo 4). + +.. code-block:: bash + + ./build/acb_theta/test/main acb_theta_transform_proj + +Checks that applying :func:`acb_theta_transform_proj` with a random symplectic +matrix, then its inverse gives back the initial vector up to scaling. + +.. code-block:: bash + + ./build/acb_theta/test/main acb_theta_all + +Checks that :func:`acb_theta_all` agrees with :func:`acb_theta_naive_all` on +random input. The matrix `\tau` is chosen to be a priori non-reduced but still +reasonably close to the reduced domain. + +.. code-block:: bash + + ./build/acb_theta/test/main acb_theta_jet_all + +Checks that :func:`acb_theta_jet_all` agrees with +:func:`acb_theta_jet_naive_all` on random input. The matrix `\tau` is chosen to +be a priori non-reduced but still reasonably close to the reduced domain. + +.. code-block:: bash + + ./build/acb_theta/test/main acb_theta_g2_jet_naive_1 + +Checks that :func:`acb_theta_g2_jet_naive_1` agrees with +:func:`acb_theta_jet_naive_all` with `g=2`, `z = 0` and `\mathit{ord} = 1` on a +random matrix `\tau`. + +.. code-block:: bash + + ./build/acb_theta/test/main acb_theta_g2_detk_symj + +Checks that the chain rule holds for the representation `\det^k \mathrm{Sym}^j` +of `\mathrm{GL}_2(\mathbb{C})` as computed by :func:`acb_theta_g2_detk_symj`. + +.. code-block:: bash + + ./build/acb_theta/test/main acb_theta_g2_transvectant + +Checks that on any sextic polynomial `f = \sum_{j=0}^6 a_j x^{6-j}`, the +transvectant `(f,f)_6` as computed by :func:`acb_theta_g2_transvectant` is +`-3a_2^3 + 8a_2 a_4 - 20a_1 a_5 + 120a_0 a_6`. + +.. code-block:: bash + + ./build/acb_theta/test/main acb_theta_g2_transvectant_lead + +Checks that the result of :func:`acb_theta_g2_transvectant_lead` is indeed the +leading term of the result of :func:`acb_theta_g2_transvectant` on random +input. + +.. code-block:: bash + + ./build/acb_theta/test/main acb_theta_g2_character + +Checks that the results of :func:`acb_theta_g2_character` and +:func:`acb_theta_transform_kappa2` for `g=2` are compatible, using the fact +that the product `\chi_5` of the ten even theta constants is a Siegel modular +form with character. + +.. code-block:: bash + + ./build/acb_theta/test/main acb_theta_g2_psi4 + +Checks that the result of :func:`acb_theta_g2_psi4` is invariant when applying +:func:`acb_theta_transform_proj` on any input vector. + +.. code-block:: bash + + ./build/acb_theta/test/main acb_theta_g2_psi6 + +Checks that the result of :func:`acb_theta_g2_psi6` is multiplied by `\pm 1` +when applying :func:`acb_theta_transform_proj` on any input vector. The correct +sign is given by :func:`acb_theta_transform_kappa2`. + +.. code-block:: bash + + ./build/acb_theta/test/main acb_theta_g2_chi10 + +Checks that the result of :func:`acb_theta_g2_chi10` is multiplied by `\pm 1` +when applying :func:`acb_theta_transform_proj` on any input vector. The correct +sign is given by :func:`acb_theta_transform_kappa2`. + +.. code-block:: bash + + ./build/acb_theta/test/main acb_theta_g2_chi12 + +Checks that the result of :func:`acb_theta_g2_chi12` is invariant when applying +:func:`acb_theta_transform_proj` on any input vector. + +.. code-block:: bash + + ./build/acb_theta/test/main acb_theta_g2_chi5 + +Checks that the result of :func:`acb_theta_g2_chi5` squares to the result of +:func:`acb_theta_g2_chi10` on any input vector. + +.. code-block:: bash + + ./build/acb_theta/test/main acb_theta_g2_chi35 + +Checks that the result of :func:`acb_theta_g2_chi35` is multiplied by `i^k` +when applying :func:`acb_theta_transform_proj` on an input vector of theta +values. The exponent `k` is given by :func:`acb_theta_transform_kappa2`. + +.. code-block:: bash + + ./build/acb_theta/test/main acb_theta_g2_chi3_6 + +Checks that the product `\chi_{8,6} = \chi_{5}\chi_{3,6}`, computed using +:func:`acb_theta_g2_chi5` and :func:`acb_theta_g2_chi3_6`, indeed defines a +modular form of weight `\det^8\mathrm{Sym}^6` by evaluating both sides of the +transformation law on random input. + +.. code-block:: bash + + ./build/acb_theta/test/main acb_theta_g2_sextic + +Checks that the discriminant of the result of :func:`acb_theta_g2_sextic` on a +random matrix `\tau` is `2^{12}\chi_{10}(\tau)`, as computed by +:func:`acb_theta_g2_chi10`. + +.. code-block:: bash + + ./build/acb_theta/test/main acb_theta_g2_sextic_chi5 + +Checks that the results of :func:`acb_theta_g2_sextic_chi5` agree with those of +:func:`acb_theta_g2_sextic` and :func:`acb_theta_g2_chi5` on random input. + +.. code-block:: bash + + ./build/acb_theta/test/main acb_theta_g2_covariants + +Checks that the output of :func:`acb_theta_g2_covariants` agrees with that of +:func:`acb_theta_g2_psi4` using the relation `20\psi_4 = - C_{2,0} + 3 +C_{4,0})`. Also checks that each covariant, when evaluated on the result of +:func:`acb_theta_g2_sextic`, defines a Siegel modular function of the correct +weight by evaluating the transformation law, and that covariants take integral +values when the input polynomial is integral. + +.. code-block:: bash + + ./build/acb_theta/test/main acb_theta_g2_covariants_lead + +Checks that the results of :func:`acb_theta_g2_covariants_lead` are indeed the +leading terms of the results of :func:`acb_theta_g2_covariants` on random +input. + +Profiling +------------------------------------------------------------------------------- + +.. code-block:: bash + + ./build/acb_theta/profile/p-siegel_reduce g pstep pmax dstep dmax + +Prints quick performance measurements for :func:`acb_siegel_reduce`: for the +given `g`, for *d* `\leq` *dmax* by steps of *dstep*, and *prec* `\leq` *pmax* +by steps of *pstep*, constructs an input matrix `w` as `\tau/d` where `\tau` is +generated by :func:`acb_siegel_randtest_reduced` and runs +:func:`acb_siegel_reduce` on `w` at working precision *prec*. + +This is meant to show that reduction is generally not a critical step when +evaluating theta functions. + +.. code-block:: bash + + ./build/acb_theta/profile/p-ql_a0_split g prec cstep cmax + +Prints quick performance measurements for :func:`acb_theta_ql_a0_split`: for +the given `g` and at the given working precision *prec*, generates an input +matrix `\tau` as in :func:`acb_siegel_randtest_reduced`, but whose lower right +`(g-s)\times (g-s)` submatrix is subsequently multiplied by `c`, where `s` runs +between `1` and `g-1` and `c\leq` *cmax* is increased by steps of *cstep*. The +running times of :func:`acb_theta_ql_a0_steps` with or without splitting at `s` +are then compared on each of these matrices, as well as the running time of +:func:`acb_theta_ql_a0`. + +This is meant to provide information on how the choice of splitting in +:func:`acb_theta_ql_a0` should be made. + +.. code-block:: bash + + ./build/acb_theta/profile/p-ql_a0_steps g pstep pmax + +Prints quick performance measurements for :func:`acb_theta_ql_a0_steps`: for +the given `g` and for a working precision *prec* `\leq` *pmax* increasing by +steps of *pstep*, generates a random matrix `\tau` in the reduced domain and +compares the running time of :func:`acb_theta_ql_a0_steps` with different +parameters *nb_steps*. + +This is meant to provide information on the correct value to return in +:func:`acb_theta_ql_a0_nb_steps`. + +.. code-block:: bash + + ./build/acb_theta/profile/p-all g nb_steps hasz + +Prints quick performance measurements for the functions :func:`acb_theta_all`, +:func:`acb_theta_ql_a0`, :func:`acb_theta_ql_all` and +:func:`acb_theta_naive_all` at different precisions on a specific input matrix +of the specified dimension *g*. We start at precision 32, then double it +*nb_steps* times. The parameter *hasz* should be either 0 (theta constants) or +1 (theta values at a nonzero point). + +This is meant to show whether the main user function is slower than naive +algorithms at low precisions. (This is currently the case.) + +.. code-block:: bash + + ./build/acb_theta/profile/p-jet_all + +Prints quick performance measurements for the functions +:func:`acb_theta_jet_all` and :func:`acb_theta_jet_naive_all` at different +precisions and order 1 on a specific input matrix for `g=2`. + +This is meant to show whether the main user function is slower than naive +algorithms at low precisions. (This is currently the case.) diff --git a/doc/source/arb_mat.rst b/doc/source/arb_mat.rst index a990b6a8bf..ae7cbf9dd2 100644 --- a/doc/source/arb_mat.rst +++ b/doc/source/arb_mat.rst @@ -403,12 +403,6 @@ Vector arithmetic The underscore methods do not allow aliasing between *res* and *v*. -.. function:: void arb_mat_bilinear_form(arb_t x, const arb_mat_t A, arb_srcptr v1, arb_srcptr v2, slong prec) - - Sets *res* to the product `v_1^T A v_2`, where `v_1` and `v_2` are seen as - column vectors. The lengths of the vectors must match the dimensions of - *A*. - Gaussian elimination and solving ------------------------------------------------------------------------------- @@ -804,18 +798,17 @@ LLL reduction .. function:: void arb_mat_spd_lll_reduce(fmpz_mat_t U, const arb_mat_t A, slong prec) - Given a symmetric positive definite matrix *A*, compute a unimodular - transformation *U* such that *U^T A U* is close to being LLL-reduced. If + Given a symmetric positive definite matrix *A*, sets *U* to an invertible + matrix such that `U^T A U` is close to being LLL-reduced. If :func:`arb_mat_spd_get_fmpz_mat` succeeds at the chosen precision, we call :func:`fmpz_lll`, and otherwise set *U* to the identity matrix. The warnings of :func:`arf_get_fmpz` apply. .. function:: int arb_mat_spd_is_lll_reduced(const arb_mat_t A, slong tol_exp, slong prec) - Returns nonzero iff *A* is LLL-reduced with a tolerance of `\varepsilon = - 2^{tol\_exp}`. This means the following. First, the error radius on - each entry of *A* must be at most `\varepsilon/16`. Then we consider the - matrix whose entries are `2^{\mathit{prec}}(1 + \varepsilon)^{i + j} - A_{i,j}` rounded to integers: it must be positive definite and pass - :func:`fmpz_mat_is_reduced` with default parameters. The warnings of - :func:`arf_get_fmpz` apply. + Given a symmetric positive definite matrix *A*, returns nonzero iff *A* is + certainly LLL-reduced with a tolerance of `\varepsilon = 2^{tol\_exp}`, + meaning that it satisfies the inequalities `|\mu_{j,k}|\leq \eta + + \varepsilon` and `(\delta - \varepsilon) \lVert b_{k-1}^*\rVert^2 \leq + \lVert b_k^*\rVert^2 + \mu_{k,k-1}^2 \lVert b_{k-1}^*\rVert^2` (with the + usual notation) for the default parameters `\eta = 0.51`, `\delta = 0.99`. diff --git a/doc/source/index.rst b/doc/source/index.rst index 44397e77cc..df3d8c420a 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -202,6 +202,7 @@ Real and complex numbers arb_hypgeom.rst acb_elliptic.rst acb_modular.rst + acb_theta.rst acb_dirichlet.rst bernoulli.rst hypgeom.rst diff --git a/doc/source/index_arb.rst b/doc/source/index_arb.rst index 47459a13a8..1505571700 100644 --- a/doc/source/index_arb.rst +++ b/doc/source/index_arb.rst @@ -102,6 +102,7 @@ arb_hypgeom.rst acb_elliptic.rst acb_modular.rst + acb_theta.rst dirichlet.rst acb_dirichlet.rst bernoulli.rst diff --git a/doc/source/references.rst b/doc/source/references.rst index c02aa85f54..acda064db7 100644 --- a/doc/source/references.rst +++ b/doc/source/references.rst @@ -49,6 +49,8 @@ References .. [Bog2012] \I. Bogaert, B. Michiels and J. Fostier, "O(1) computation of Legendre polynomials and Gauss-Legendre nodes and weights for parallel computing", SIAM Journal on Scientific Computing 34:3 (2012), C83-C101 +.. [Bol1887] \O. Bolza, "Darstellung der rationalen ganzen Invarianten der Binärform sechsten Grades durch die Nullwerthe der zugehörigen Theta-Functionen", Math. Ann. 30:4 (1887), 478--495. https://doi.org/10.1007/BF01444091 + .. [Bor1987] \P. Borwein, "Reduced complexity evaluation of hypergeometric functions", Journal of Approximation Theory 50:3 (1987) .. [Bor2000] \P. Borwein, "An Efficient Algorithm for the Riemann Zeta Function", Constructive experimental and nonlinear analysis, CMS Conference Proc. 27 (2000) 29-34, http://www.cecm.sfu.ca/personal/pborwein/PAPERS/P155.pdf @@ -63,6 +65,10 @@ References .. [BuhlerCrandallSompolski1992] \Buhler, J.P. and Crandall, R.E. and Sompolski, R.W. : Irregular primes to one million : Math. Comp. 59:2000 (1992) 717--722 +.. [CFG2017] \F. Cléry, C. Faber, and G. van der Geer. "Covariants of binary sextics and vector-valued Siegel modular forms of genus two", Math. Ann. 369 (2017), 1649--1669. https://doi.org/10.1007/s00208-016-1510-2 + +.. [CFG2019] \F. Cléry, C. Faber, and G. van der Geer. "Covariants of binary sextics and modular forms of degree 2 with character", Math. Comp. 88 (2019), 2423--2441. https://doi.org/10.1090/mcom/3412 + .. [CGHJK1996] \R. M. Corless, G. H. Gonnet, D. E. Hare, D. J. Jeffrey and D. E. Knuth, "On the Lambert W function", Advances in Computational Mathematics, 5(1) (1996), 329-359 .. [CP2005] \R. Crandall and C. Pomerance, *Prime Numbers: A Computational Perspective*, second edition, Springer (2005). @@ -83,6 +89,8 @@ References .. [CraPom2005] \Richard Crandall and Carl Pomerance: Prime numbers: a computational perspective. 2005. +.. [DHBHS2004] \B. Deconinck, M. Heil, A. Bobenko, M. van Hoeij, and M. Schmies, "Computing Riemann theta functions", Math. Comp. 73:247 (2004), 1417--1442. https://arxiv.org/abs/nlin/0206009 + .. [DYF1999] \A. Dzieciol, S. Yngve and P. O. Fröman, "Coulomb wave functions with complex values of the variable and the parameters", J. Math. Phys. 40, 6145 (1999), https://doi.org/10.1063/1.533083 .. [DelegliseNicolasZimmermann2009] \Deleglise, Marc and Niclas, Jean-Louis and Zimmermann, Paul : Landau's function for one million billions, J. Théor. Nombres Bordeaux 20:3 (2009) 625--671 @@ -97,6 +105,8 @@ References .. [EM2004] \O. Espinosa and V. Moll, "A generalized polygamma function", Integral Transforms and Special Functions (2004), 101-115. +.. [EK2023] \N. D. Elkies and J. Kieffer, "A uniform quasi-linear time algorithm for evaluating theta functions in any dimension", in preparation. + .. [Fie2007] \C. Fieker, "Sparse representation for cyclotomic fields". Experiment. Math. Volume 16, Issue 4 (2007), 493-500. https://doi.org/10.1080/10586458.2007.10129012 .. [FieHof2014] \Fieker C. and Hofmann T.: "Computing in quotients of rings of integers" LMS Journal of Computation and Mathematics, 17(A), 349-365 @@ -115,6 +125,8 @@ References .. [Gos1974] \R. W. Gosper, "Acceleration of series", MIT AI Memo no.304, (March-1974). https://dspace.mit.edu/handle/1721.1/6088 +.. [Got1959] \E. Gottschling, "Explizite Bestimmung der Randflächen es Fundamentalbereiches der Modulgruppe zweiten Grades'', Math. Annalen 138 (1959), 103--124. https://doi.org/10.1007/BF01342938 + .. [GowWag2008] \Jason Gower and Sam Wagstaff : "Square form factoring" Math. Comp. 77, 2008, pp 551-588, https://doi.org/10.1090/S0025-5718-07-02010-8 .. [GraMol2010] \Torbjorn Granlund and Niels Moller : Improved Division by Invariant Integers https://gmplib.org/~tege/division-paper.pdf @@ -147,6 +159,10 @@ References .. [Iliopoulos1989] \Iliopoulos, C. S., Worst-Case Complexity Bounds on Algorithms for Computing the Canonical Structure of Finite Abelian Groups and the Hermite and Smith Normal Forms of an Integer Matrix : SIAM J. Computation 18:4 (1989) 658 +.. [Igu1972] \J.-I. Igusa. *Theta functions*, Springer, 1972. https://doi.org/10.1007/978-3-642-65315-5 + +.. [Igu1979] \J.-I. Igusa, "On the ring of modular forms of degree two over Z", Amer. J. Math. 101:1 (1979), 149--183. https://doi.org/10.2307/2373943 + .. [JB2018] \F. Johansson and I. Blagouchine. "Computing Stieltjes constants using complex integration", preprint (2018), https://arxiv.org/abs/1804.01679 .. [JM2018] \F. Johansson and M. Mezzarobba, "Fast and rigorous arbitrary-precision computation of Gauss-Legendre quadrature nodes and weights", preprint (2018), https://arxiv.org/abs/1802.03948 @@ -193,10 +209,14 @@ References .. [Kri2013] \A. Krishnamoorthy and D. Menon, "Matrix Inversion Using Cholesky Decomposition" Proc. of the International Conference on Signal Processing Algorithms, Architectures, Arrangements, and Applications (SPA-2013), pp. 70-72, 2013. +.. [LT2016] \H. Labrande and E. Thomé, "Computing theta functions in quasi-linear time in genus 2 and above", ANTS XII, Kaiserslautern, LMS J. Comp. Math 19 (2016), 163--177. https://doi.org/10.1112/S1461157016000309 + .. [Leh1970] \R. S. Lehman, "On the Distribution of Zeros of the Riemann Zeta-Function", Proc. of the London Mathematical Society 20(3) (1970), 303-320, https://doi.org/10.1112/plms/s3-20.2.303 .. [LukPatWil1996] \R. F. Lukes and C. D. Patterson and H. C. Williams "Some results on pseudosquares" Math. Comp. 1996, no. 65, 361--372 +.. [MN2019] \P. Molin and C. Neurohr, "Computing period matrices and the Abel--Jacobi map of superelliptic curves", Math. Comp. 88:316 (2019), 847--888. + .. [MP2006] \M. Monagan and R. Pearce. "Rational simplification modulo a polynomial ideal". Proceedings of the 2006 international symposium on Symbolic and algebraic computation - ISSAC '06. https://doi.org/10.1145/1145768.1145809 .. [MPFR2012] The MPFR team, "MPFR Algorithms" (2012), https://www.mpfr.org/algo.html @@ -211,6 +231,10 @@ References .. [Mul2000] \Thom Mulders : On Short Multiplications and Divisions, AAECC vol. 11 (2000) 69--88 +.. [Mum1983] \D. Mumford, *Tata Lectures on Theta I*, Birkhäuser, 1983. https://doi.org/10.1007/978-1-4899-2843-6 + +.. [Mum1984] \D. Mumford, *Tata Lectures on Theta II*, Birkhäuser, 1984. https://doi.org/10.1007/978-0-8176-4578-6 + .. [NIST2012] National Institute of Standards and Technology, *Digital Library of Mathematical Functions* (2012), https://dlmf.nist.gov/ .. [NakTurWil1997] \Nakos, George and Turner, Peter and Williams, Robert : Fraction-free algorithms for linear and polynomial equations, ACM SIGSAM Bull. 31 (1997) 3 11--19 @@ -267,6 +291,8 @@ References .. [StoMul1998] \Storjohann, Arne and Mulders, Thom : Fast algorithms for linear algebra modulo :math:`N` : Algorithms---{ESA} '98 (Venice), Lecture Notes in Comput. Sci. 1461 139--150 +.. [Str2014] \M. Streng, "Computing Igusa class polynomials", Math. Comp. 83:285 (2014), 275--309. https://doi.org/10.1090/S0025-5718-2013-02712-3 + .. [Str1997] \A. Strzebonski. "Computing in the field of complex algebraic numbers". Journal of Symbolic Computation (1997) 24, 647-656. https://doi.org/10.1006/jsco.1997.0158 .. [Str2012] \A. Strzebonski. "Real root isolation for exp-log-arctan functions". Journal of Symbolic Computation 47 (2012) 282–314. https://doi.org/10.1016/j.jsc.2011.11.004 @@ -303,4 +329,4 @@ References .. [vdH2006] \J. van der Hoeven, "Computations with effective real numbers". Theoretical Computer Science, Volume 351, Issue 1, 14 February 2006, Pages 52-60. https://doi.org/10.1016/j.tcs.2005.09.060 -All referenced works: [AbbottBronsteinMulders1999]_, [Apostol1997]_, [Ari2011]_, [Ari2012]_, [Arn2010]_, [Arn2012]_, [ArnoldMonagan2011]_, [BBC1997]_, [BBC2000]_, [BBK2014]_, [BD1992]_, [BF2020]_, [BFSS2006]_, [BJ2013]_, [BM1980]_, [BZ1992]_, [BZ2011]_, [BaiWag1980]_, [BerTas2010]_, [Blo2009]_, [Bodrato2010]_, [Boe2020]_, [Bog2012]_, [Bor1987]_, [Bor2000]_, [Bre1978]_, [Bre1979]_, [Bre2010]_, [BrentKung1978]_, [BuhlerCrandallSompolski1992]_, [CGHJK1996]_, [CP2005]_, [Car1995]_, [Car2004]_, [Chen2003]_, [Cho1999]_, [Coh1996]_, [Coh2000]_, [Col1971]_, [CraPom2005]_, [DYF1999]_, [DelegliseNicolasZimmermann2009]_, [DomKanTro1987]_, [Dup2006]_, [Dus1999]_, [EHJ2016]_, [EM2004]_, [Fie2007]_, [FieHof2014]_, [Fil1992]_, [GCL1992]_, [GG2003]_, [GS2003]_, [GVL1996]_, [Gas2018]_, [Gos1974]_, [GowWag2008]_, [GraMol2010]_, [HM2017]_, [HS1967]_, [HZ2004]_, [HanZim2004]_, [Har2010]_, [Har2012]_, [Har2015]_, [Har2018]_, [Hart2010]_, [Hen1956]_, [Hoe2001]_, [Hoe2009]_, [Hor1972]_, [Iliopoulos1989]_, [JB2018]_, [JM2018]_, [JR1999]_, [Joh2012]_, [Joh2013]_, [Joh2014a]_, [Joh2014b]_, [Joh2014c]_, [Joh2015]_, [Joh2015b]_, [Joh2016]_, [Joh2017]_, [Joh2017a]_, [Joh2017b]_, [Joh2018a]_, [Joh2018b]_, [JvdP2002]_, [Kahan1991]_, [KanBac1979]_, [Kar1998]_, [Knu1997]_, [Kob2010]_, [Kri2013]_, [Leh1970]_, [LukPatWil1996]_, [MP2006]_, [MPFR2012]_, [MasRob1996]_, [Mic2007]_, [Miy2010]_, [Mos1971]_, [Mul2000]_, [NIST2012]_, [NakTurWil1997]_, [Olv1997]_, [PP2010]_, [PS1973]_, [PS1991]_, [Paterson1973]_, [PernetStein2010]_, [Pet1999]_, [Pla2011]_, [Pla2017]_, [RF1994]_, [Rad1973]_, [Rademacher1937]_, [Ric1992]_, [Ric1995]_, [Ric1997]_, [Ric2007]_, [Ric2009]_, [RosSch1962]_, [Rum2010]_, [Smi2001]_, [SorWeb2016]_, [Ste2002]_, [Ste2010]_, [Stehle2010]_, [Stein2007]_, [Sut2007]_, [StoMul1998]_, [Str1997]_, [Str2012]_, [Tak2000]_, [ThullYap1990]_, [Tre2008]_, [Tru2011]_, [Tru2014]_, [Tur1953]_, [Villard2007]_, [WaktinsZeitlin1993]_, [Wei2000]_, [Whiteman1956]_, [Zip1985]_, [Zun2023]_, [vHP2012]_, [vdH1995]_, [vdH2006]_ +All referenced works: [AbbottBronsteinMulders1999]_, [Apostol1997]_, [Ari2011]_, [Ari2012]_, [Arn2010]_, [Arn2012]_, [ArnoldMonagan2011]_, [BBC1997]_, [BBC2000]_, [BBK2014]_, [BD1992]_, [BF2020]_, [BFSS2006]_, [BJ2013]_, [BM1980]_, [BZ1992]_, [BZ2011]_, [BaiWag1980]_, [BerTas2010]_, [Blo2009]_, [Bodrato2010]_, [Boe2020]_, [Bog2012]_, [Bol1887]_, [Bor1987]_, [Bor2000]_, [Bre1978]_, [Bre1979]_, [Bre2010]_, [BrentKung1978]_, [BuhlerCrandallSompolski1992]_, [CFG2017]_, [CFG2019]_, [CGHJK1996]_, [CP2005]_, [Car1995]_, [Car2004]_, [Chen2003]_, [Cho1999]_, [Coh1996]_, [Coh2000]_, [Col1971]_, [CraPom2005]_, [DHBHS2004]_, [DYF1999]_, [DelegliseNicolasZimmermann2009]_, [DomKanTro1987]_, [Dup2006]_, [Dus1999]_, [EHJ2016]_, [EM2004]_, [EK2023]_, [Fie2007]_, [FieHof2014]_, [Fil1992]_, [GCL1992]_, [GG2003]_, [GS2003]_, [GVL1996]_, [Gas2018]_, [Gos1974]_, [GowWag2008]_, [GraMol2010]_, [HM2017]_, [HS1967]_, [HZ2004]_, [HanZim2004]_, [Har2010]_, [Har2012]_, [Har2015]_, [Har2018]_, [Hart2010]_, [Hen1956]_, [Hoe2001]_, [Hoe2009]_, [Hor1972]_, [Iliopoulos1989]_, [Igu1972]_, [Igu1979]_, [JB2018]_, [JM2018]_, [JR1999]_, [Joh2012]_, [Joh2013]_, [Joh2014a]_, [Joh2014b]_, [Joh2014c]_, [Joh2015]_, [Joh2015b]_, [Joh2016]_, [Joh2017]_, [Joh2017a]_, [Joh2017b]_, [Joh2018a]_, [Joh2018b]_, [JvdP2002]_, [Kahan1991]_, [KanBac1979]_, [Kar1998]_, [Knu1997]_, [Kob2010]_, [Kri2013]_, [LT2016]_, [Leh1970]_, [LukPatWil1996]_, [MN2019]_, [MP2006]_, [MPFR2012]_, [MasRob1996]_, [Mic2007]_, [Miy2010]_, [Mos1971]_, [Mul2000]_, [Mum1983]_, [Mum1984]_, [NIST2012]_, [NakTurWil1997]_, [Olv1997]_, [PP2010]_, [PS1973]_, [PS1991]_, [Paterson1973]_, [PernetStein2010]_, [Pet1999]_, [Pla2011]_, [Pla2017]_, [RF1994]_, [Rad1973]_, [Rademacher1937]_, [Ric1992]_, [Ric1995]_, [Ric1997]_, [Ric2007]_, [Ric2009]_, [RosSch1962]_, [Rum2010]_, [Smi2001]_, [SorWeb2016]_, [Ste2002]_, [Ste2010]_, [Stehle2010]_, [Stein2007]_, [Sut2007]_, [StoMul1998]_, [Str2014]_, [Str1997]_, [Str2012]_, [Tak2000]_, [ThullYap1990]_, [Tre2008]_, [Tru2011]_, [Tru2014]_, [Tur1953]_, [Villard2007]_, [WaktinsZeitlin1993]_, [Wei2000]_, [Whiteman1956]_, [Zip1985]_, [Zun2023]_, [vHP2012]_, [vdH1995]_, [vdH2006]_ diff --git a/src/acb_theta.h b/src/acb_theta.h new file mode 100644 index 0000000000..559ec4d48e --- /dev/null +++ b/src/acb_theta.h @@ -0,0 +1,262 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#ifndef ACB_THETA_H +#define ACB_THETA_H + +#include "fmpz_mat.h" +#include "acb_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ACB_THETA_LOW_PREC 32 + +/* The Siegel modular group */ + +static inline slong +sp2gz_dim(const fmpz_mat_t mat) +{ + return fmpz_mat_nrows(mat) / 2; +} + +void sp2gz_set_blocks(fmpz_mat_t mat, const fmpz_mat_t alpha, const fmpz_mat_t beta, + const fmpz_mat_t gamma, const fmpz_mat_t delta); +void sp2gz_j(fmpz_mat_t mat); +void sp2gz_block_diag(fmpz_mat_t mat, const fmpz_mat_t U); +void sp2gz_trig(fmpz_mat_t mat, const fmpz_mat_t S); +void sp2gz_embed(fmpz_mat_t res, const fmpz_mat_t mat); +void sp2gz_restrict(fmpz_mat_t res, const fmpz_mat_t mat); + +slong sp2gz_nb_fundamental(slong g); +void sp2gz_fundamental(fmpz_mat_t mat, slong j); + +int sp2gz_is_correct(const fmpz_mat_t mat); +int sp2gz_is_j(const fmpz_mat_t mat); +int sp2gz_is_block_diag(const fmpz_mat_t mat); +int sp2gz_is_trig(const fmpz_mat_t mat); +int sp2gz_is_embedded(fmpz_mat_t res, const fmpz_mat_t mat); + +void sp2gz_inv(fmpz_mat_t inv, const fmpz_mat_t mat); +fmpz_mat_struct * sp2gz_decompose(slong * nb, const fmpz_mat_t mat); + +void sp2gz_randtest(fmpz_mat_t mat, flint_rand_t state, slong bits); + +/* The Siegel half space */ + +void acb_siegel_cocycle(acb_mat_t c, const fmpz_mat_t mat, const acb_mat_t tau, slong prec); +void acb_siegel_transform_cocycle_inv(acb_mat_t w, acb_mat_t c, acb_mat_t cinv, + const fmpz_mat_t mat, const acb_mat_t tau, slong prec); +void acb_siegel_transform(acb_mat_t w, const fmpz_mat_t mat, const acb_mat_t tau, slong prec); +void acb_siegel_transform_z(acb_ptr r, acb_mat_t w, const fmpz_mat_t mat, + acb_srcptr z, const acb_mat_t tau, slong prec); + +void acb_siegel_cho(arb_mat_t C, const acb_mat_t tau, slong prec); +void acb_siegel_yinv(arb_mat_t Yinv, const acb_mat_t tau, slong prec); + +void acb_siegel_reduce(fmpz_mat_t mat, const acb_mat_t tau, slong prec); +int acb_siegel_is_reduced(const acb_mat_t tau, slong tol_exp, slong prec); + +void acb_siegel_randtest(acb_mat_t tau, flint_rand_t state, slong prec, slong mag_bits); +void acb_siegel_randtest_reduced(acb_mat_t tau, flint_rand_t state, slong prec, slong mag_bits); +void acb_siegel_randtest_vec(acb_ptr z, flint_rand_t state, slong g, slong prec); + +/* Theta characteristics */ + +void acb_theta_char_get_slong(slong * n, ulong a, slong g); +ulong acb_theta_char_get_a(const slong * n, slong g); +void acb_theta_char_get_arb(arb_ptr v, ulong a, slong g); +void acb_theta_char_get_acb(acb_ptr v, ulong a, slong g); + +slong acb_theta_char_dot(ulong a, ulong b, slong g); +slong acb_theta_char_dot_slong(ulong a, const slong * n, slong g); +void acb_theta_char_dot_acb(acb_t x, ulong a, acb_srcptr z, slong g, slong prec); + +int acb_theta_char_is_even(ulong ab, slong g); +int acb_theta_char_is_goepel(ulong ch1, ulong ch2, ulong ch3, ulong ch4, slong g); +int acb_theta_char_is_syzygous(ulong ch1, ulong ch2, ulong ch3, slong g); + +/* Ellipsoids in naive algorithms */ + +struct acb_theta_eld_struct +{ + slong dim, ambient_dim; + slong * last_coords; + slong min, mid, max, nr, nl; + struct acb_theta_eld_struct * rchildren; + struct acb_theta_eld_struct * lchildren; + slong nb_pts, nb_border; + slong * box; +}; + +typedef struct acb_theta_eld_struct acb_theta_eld_t[1]; + +#define acb_theta_eld_dim(E) ((E)->dim) +#define acb_theta_eld_ambient_dim(E) ((E)->ambient_dim) +#define acb_theta_eld_coord(E, k) ((E)->last_coords[(k) - acb_theta_eld_dim(E)]) +#define acb_theta_eld_min(E) ((E)->min) +#define acb_theta_eld_mid(E) ((E)->mid) +#define acb_theta_eld_max(E) ((E)->max) +#define acb_theta_eld_nr(E) ((E)->nr) +#define acb_theta_eld_nl(E) ((E)->nl) +#define acb_theta_eld_rchild(E, k) (&(E)->rchildren[(k)]) +#define acb_theta_eld_lchild(E, k) (&(E)->lchildren[(k)]) +#define acb_theta_eld_nb_pts(E) ((E)->nb_pts) +#define acb_theta_eld_nb_border(E) ((E)->nb_border) +#define acb_theta_eld_box(E, k) ((E)->box[(k)]) + +void acb_theta_eld_init(acb_theta_eld_t E, slong d, slong g); +void acb_theta_eld_clear(acb_theta_eld_t E); + +int acb_theta_eld_set(acb_theta_eld_t E, const arb_mat_t C, const arf_t R2, arb_srcptr v); +void acb_theta_eld_points(slong * pts, const acb_theta_eld_t E); +void acb_theta_eld_border(slong * pts, const acb_theta_eld_t E); +int acb_theta_eld_contains(const acb_theta_eld_t E, const slong * pt); +void acb_theta_eld_print(const acb_theta_eld_t E); + +/* Naive algorithms */ + +void acb_theta_naive_radius(arf_t R2, arf_t eps, const arb_mat_t C, slong ord, slong prec); +void acb_theta_naive_reduce(arb_ptr v, acb_ptr new_zs, arb_ptr as, acb_ptr cs, arb_ptr us, + acb_srcptr zs, slong nb, const acb_mat_t tau, slong prec); +void acb_theta_naive_term(acb_t res, acb_srcptr z, const acb_mat_t tau, const slong * tup, + const slong * n, slong prec); + +typedef void (*acb_theta_naive_worker_t)(acb_ptr, acb_srcptr, acb_srcptr, const slong *, + slong, const acb_t, const slong *, slong, slong, slong, slong); + +void acb_theta_naive_worker(acb_ptr th, slong len, acb_srcptr zs, slong nb, + const acb_mat_t tau, const acb_theta_eld_t E, slong ord, slong prec, + acb_theta_naive_worker_t worker); + +void acb_theta_naive_00(acb_ptr th, acb_srcptr zs, slong nb, const acb_mat_t tau, slong prec); +void acb_theta_naive_0b(acb_ptr th, acb_srcptr zs, slong nb, const acb_mat_t tau, slong prec); + +void acb_theta_naive_fixed_ab(acb_ptr th, ulong ab, acb_srcptr zs, slong nb, + const acb_mat_t tau, slong prec); +void acb_theta_naive_fixed_a(acb_ptr th, ulong a, acb_srcptr zs, slong nb, + const acb_mat_t tau, slong prec); +void acb_theta_naive_all(acb_ptr th, acb_srcptr zs, slong nb, const acb_mat_t tau, slong prec); + +/* Naive algorithms for derivatives */ + +slong acb_theta_jet_nb(slong ord, slong g); +slong acb_theta_jet_total_order(const slong * tup, slong g); +void acb_theta_jet_tuples(slong * tups, slong ord, slong g); +slong acb_theta_jet_index(const slong * tup, slong g); + +void acb_theta_jet_mul(acb_ptr res, acb_srcptr v1, acb_srcptr v2, slong ord, + slong g, slong prec); +void acb_theta_jet_compose(acb_ptr res, acb_srcptr v, const acb_mat_t N, + slong ord, slong prec); +void acb_theta_jet_exp_pi_i(acb_ptr res, arb_srcptr a, slong ord, slong g, slong prec); + +void acb_theta_jet_naive_radius(arf_t R2, arf_t eps, const arb_mat_t C, arb_srcptr v, + slong ord, slong prec); + +void acb_theta_jet_naive_00(acb_ptr dth, acb_srcptr z, const acb_mat_t tau, + slong ord, slong prec); +void acb_theta_jet_naive_fixed_ab(acb_ptr dth, ulong ab, acb_srcptr z, const acb_mat_t tau, + slong ord, slong prec); +void acb_theta_jet_naive_all(acb_ptr dth, acb_srcptr z, const acb_mat_t tau, + slong ord, slong prec); + +void acb_theta_jet_error_bounds(arb_ptr err, acb_srcptr z, const acb_mat_t tau, + acb_srcptr dth, slong ord, slong prec); + +/* Quasi-linear algorithms on the reduced domain */ + +void acb_theta_dist_pt(arb_t d, arb_srcptr v, const arb_mat_t C, const slong * n, slong prec); +void acb_theta_dist_lat(arb_t d, arb_srcptr v, const arb_mat_t C, slong prec); +void acb_theta_dist_a0(arb_ptr d, acb_srcptr z, const acb_mat_t tau, slong prec); +slong acb_theta_dist_addprec(const arb_t d); + +void acb_theta_agm_hadamard(acb_ptr res, acb_srcptr a, slong g, slong prec); +void acb_theta_agm_sqrt(acb_ptr res, acb_srcptr a, acb_srcptr roots, slong nb, slong prec); +void acb_theta_agm_mul(acb_ptr res, acb_srcptr a1, acb_srcptr a2, slong g, slong prec); +void acb_theta_agm_mul_tight(acb_ptr res, acb_srcptr a0, acb_srcptr a, + arb_srcptr d0, arb_srcptr d, slong g, slong prec); + +typedef int (*acb_theta_ql_worker_t)(acb_ptr, acb_srcptr, acb_srcptr, + arb_srcptr, arb_srcptr, const acb_mat_t, slong, slong); + +int acb_theta_ql_a0_naive(acb_ptr th, acb_srcptr t, acb_srcptr z, arb_srcptr d0, + arb_srcptr d, const acb_mat_t tau, slong guard, slong prec); +int acb_theta_ql_a0_split(acb_ptr th, acb_srcptr t, acb_srcptr z, arb_srcptr d, + const acb_mat_t tau, slong s, slong guard, slong prec, acb_theta_ql_worker_t worker); +int acb_theta_ql_a0_steps(acb_ptr th, acb_srcptr t, acb_srcptr z, arb_srcptr d0, + arb_srcptr d, const acb_mat_t tau, slong nb_steps, slong s, slong guard, + slong prec, acb_theta_ql_worker_t worker); +slong acb_theta_ql_a0_nb_steps(const arb_mat_t C, slong s, slong prec); +int acb_theta_ql_a0(acb_ptr th, acb_srcptr t, acb_srcptr z, arb_srcptr d0, + arb_srcptr d, const acb_mat_t tau, slong guard, slong prec); + +slong acb_theta_ql_reduce(acb_ptr x, acb_t c, arb_t u, slong * n1, acb_srcptr z, + const acb_mat_t tau, slong prec); + +void acb_theta_ql_all(acb_ptr th, acb_srcptr z, const acb_mat_t tau, int sqr, slong prec); + +/* Quasi-linear algorithms for derivatives */ + +void acb_theta_jet_ql_bounds(arb_t c, arb_t rho, acb_srcptr z, const acb_mat_t tau, slong ord); +void acb_theta_jet_ql_radius(arf_t eps, arf_t err, const arb_t c, const arb_t rho, + slong ord, slong g, slong prec); +void acb_theta_jet_ql_finite_diff(acb_ptr dth, const arf_t eps, const arf_t err, + const arb_t rho, acb_srcptr val, slong ord, slong g, slong prec); + +void acb_theta_jet_ql_all(acb_ptr dth, acb_srcptr z, const acb_mat_t tau, slong ord, slong prec); + +/* Transformation formulas */ + +ulong acb_theta_transform_char(slong * e, const fmpz_mat_t mat, ulong ab); +void acb_theta_transform_sqrtdet(acb_t res, const acb_mat_t tau, slong prec); +slong acb_theta_transform_kappa(acb_t sqrtdet, const fmpz_mat_t mat, + const acb_mat_t tau, slong prec); +slong acb_theta_transform_kappa2(const fmpz_mat_t mat); +void acb_theta_transform_proj(acb_ptr res, const fmpz_mat_t mat, acb_srcptr th, + int sqr, slong prec); + +void acb_theta_all(acb_ptr th, acb_srcptr z, const acb_mat_t tau, int sqr, slong prec); +void acb_theta_jet_all(acb_ptr dth, acb_srcptr z, const acb_mat_t tau, slong ord, slong prec); + +/* Genus 2 specifics */ + +#define ACB_THETA_G2_COV_NB 26 + +void acb_theta_g2_jet_naive_1(acb_ptr dth, const acb_mat_t tau, slong prec); +void acb_theta_g2_detk_symj(acb_poly_t res, const acb_mat_t m, const acb_poly_t f, + slong k, slong j, slong prec); +void acb_theta_g2_transvectant(acb_poly_t res, const acb_poly_t g, const acb_poly_t h, + slong m, slong n, slong k, slong prec); +void acb_theta_g2_transvectant_lead(acb_t r, const acb_poly_t g, const acb_poly_t h, + slong m, slong n, slong k, slong prec); + +slong acb_theta_g2_character(const fmpz_mat_t mat); + +void acb_theta_g2_psi4(acb_t res, acb_srcptr th2, slong prec); +void acb_theta_g2_psi6(acb_t res, acb_srcptr th2, slong prec); +void acb_theta_g2_chi10(acb_t res, acb_srcptr th2, slong prec); +void acb_theta_g2_chi12(acb_t res, acb_srcptr th2, slong prec); +void acb_theta_g2_chi5(acb_t res, acb_srcptr th, slong prec); +void acb_theta_g2_chi35(acb_t res, acb_srcptr th, slong prec); +void acb_theta_g2_chi3_6(acb_poly_t res, acb_srcptr dth, slong prec); + +void acb_theta_g2_sextic(acb_poly_t res, const acb_mat_t tau, slong prec); +void acb_theta_g2_sextic_chi5(acb_poly_t res, acb_t chi5, const acb_mat_t tau, slong prec); +void acb_theta_g2_covariants(acb_poly_struct * res, const acb_poly_t f, slong prec); +void acb_theta_g2_covariants_lead(acb_ptr res, const acb_poly_t f, slong prec); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/acb_theta/agm_hadamard.c b/src/acb_theta/agm_hadamard.c new file mode 100644 index 0000000000..0f10984aa3 --- /dev/null +++ b/src/acb_theta/agm_hadamard.c @@ -0,0 +1,37 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb.h" +#include "acb_theta.h" + +void +acb_theta_agm_hadamard(acb_ptr res, acb_srcptr a, slong g, slong prec) +{ + acb_ptr v; + slong half; + + if (g == 0) + { + acb_set(&res[0], &a[0]); + } + else + { + half = 1 << (g - 1); + v = _acb_vec_init(1 << g); + + acb_theta_agm_hadamard(v, a, g - 1, prec); + acb_theta_agm_hadamard(v + half, a + half, g - 1, prec); + _acb_vec_add(res, v, v + half, half, prec); + _acb_vec_sub(res + half, v, v + half, half, prec); + + _acb_vec_clear(v, 1 << g); + } +} diff --git a/src/acb_theta/agm_mul.c b/src/acb_theta/agm_mul.c new file mode 100644 index 0000000000..fb2fd620c9 --- /dev/null +++ b/src/acb_theta/agm_mul.c @@ -0,0 +1,46 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb.h" +#include "acb_theta.h" + +void +acb_theta_agm_mul(acb_ptr res, acb_srcptr a1, acb_srcptr a2, slong g, slong prec) +{ + acb_ptr v; + slong n = 1 << g; + slong k; + + v = _acb_vec_init(2 * n); + + acb_theta_agm_hadamard(v, a1, g, prec); + + if (a1 == a2) + { + for (k = 0; k < n; k++) + { + acb_sqr(&v[k], &v[k], prec); + } + } + else + { + acb_theta_agm_hadamard(v + n, a2, g, prec); + for (k = 0; k < n; k++) + { + acb_mul(&v[k], &v[k], &v[k + n], prec); + } + } + + acb_theta_agm_hadamard(res, v, g, prec); + _acb_vec_scalar_mul_2exp_si(res, res, n, -2 * g); + + _acb_vec_clear(v, 2 * n); +} diff --git a/src/acb_theta/agm_mul_tight.c b/src/acb_theta/agm_mul_tight.c new file mode 100644 index 0000000000..2479eaf092 --- /dev/null +++ b/src/acb_theta/agm_mul_tight.c @@ -0,0 +1,173 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb.h" +#include "acb_theta.h" + +static void +acb_theta_agm_rel_mag_err(arf_t m, arf_t eps, acb_srcptr a, arb_srcptr d, + slong nb, slong prec) +{ + acb_t x, err; + arb_t y; + arf_t abs; + slong k; + + acb_init(x); + acb_init(err); + arb_init(y); + arf_init(abs); + + arf_zero(m); + arf_zero(eps); + + for (k = 0; k < nb; k++) + { + arb_zero(y); + arb_get_ubound_arf(arb_midref(y), &d[k], prec); + arb_exp(y, y, prec); + acb_mul_arb(x, &a[k], y, prec); + + acb_abs(y, x, prec); + arb_get_ubound_arf(abs, y, prec); + arf_max(m, m, abs); + + acb_zero(err); + arf_set_mag(arb_midref(acb_realref(err)), arb_radref(acb_realref(x))); + arf_set_mag(arb_midref(acb_imagref(err)), arb_radref(acb_imagref(x))); + acb_abs(y, err, prec); + arb_get_ubound_arf(abs, y, prec); + arf_max(eps, eps, abs); + } + + acb_clear(x); + acb_clear(err); + arb_clear(y); + arf_clear(abs); +} + +/* This is assuming a0 corresponds to theta constants */ +static void +acb_theta_agm_mul_tight_gen(acb_ptr res, acb_srcptr a0, acb_srcptr a, + arb_srcptr d0, arb_srcptr d, slong g, slong prec) +{ + slong n = 1 << g; + slong lp = ACB_THETA_LOW_PREC; + slong hprec = prec; + acb_ptr v0, v; + arf_t m0, m, eps0, eps, e, t; + arb_t err; + slong k; + + v0 = _acb_vec_init(n); + v = _acb_vec_init(n); + arf_init(m0); + arf_init(m); + arf_init(eps0); + arf_init(eps); + arf_init(e); + arf_init(t); + arb_init(err); + + acb_theta_agm_rel_mag_err(m0, eps0, a0, d0, n, prec); + acb_theta_agm_rel_mag_err(m, eps, a, d, n, prec); + + for (k = 0; k < n; k++) + { + hprec = FLINT_MAX(hprec, prec + acb_theta_dist_addprec(&d[k])); + acb_get_mid(&v0[k], &a0[k]); + acb_get_mid(&v[k], &a[k]); + } + + /* Perform agm_mul or agm_sqr at high precision */ + if (a0 == a) + { + acb_theta_agm_mul(res, v0, v0, g, hprec); + } + else + { + acb_theta_agm_mul(res, v0, v, g, hprec); + } + + /* New relative error wrt distances is m0 eps + m eps0 + eps0 eps */ + arf_mul(e, m0, eps, lp, ARF_RND_CEIL); + arf_mul(t, m, eps0, lp, ARF_RND_CEIL); + arf_add(e, e, t, lp, ARF_RND_CEIL); + arf_mul(t, eps, eps0, lp, ARF_RND_CEIL); + arf_add(e, e, t, lp, ARF_RND_CEIL); + + for (k = 0; k < n; k++) + { + arb_neg(err, &d[k]); + arb_exp(err, err, prec); + arb_mul_arf(err, err, e, lp); + acb_add_error_arb(&res[k], err); + } + + _acb_vec_clear(v0, n); + _acb_vec_clear(v, n); + arf_clear(m0); + arf_clear(m); + arf_clear(eps0); + arf_clear(eps); + arf_clear(e); + arf_clear(t); + arb_clear(err); +} + +/* there might be a way of saving some multiplications here by writing in terms + of real & imaginary parts */ +static void +acb_theta_agm_mul_tight_g1(acb_ptr res, acb_srcptr a0, acb_srcptr a, + arb_srcptr d0, arb_srcptr d, slong g, slong prec) +{ + acb_t t; + acb_ptr aux; + + acb_init(t); + aux = _acb_vec_init(2); + + if (a == a0) + { + acb_sqr(t, &a[0], prec); + acb_sqr(&aux[0], &a[1], prec); + acb_add(&aux[0], &aux[0], t, prec + acb_theta_dist_addprec(&d[0])); + acb_mul(&aux[1], &a[0], &a[1], prec); + acb_mul_2exp_si(&aux[1], &aux[1], 1); + } + else + { + acb_mul(t, &a0[0], &a[0], prec); + acb_mul(&aux[0], &a0[1], &a[1], prec); + acb_add(&aux[0], &aux[0], t, prec + acb_theta_dist_addprec(&d[0])); + acb_mul(t, &a0[0], &a[1], prec); + acb_mul(&aux[1], &a0[1], &a[0], prec); + acb_add(&aux[1], &aux[1], t, prec + acb_theta_dist_addprec(&d[1])); + } + _acb_vec_scalar_mul_2exp_si(res, aux, 2, -1); + + acb_clear(t); + _acb_vec_clear(aux, 2); +} + +void +acb_theta_agm_mul_tight(acb_ptr res, acb_srcptr a0, acb_srcptr a, + arb_srcptr d0, arb_srcptr d, slong g, slong prec) +{ + if (g == 1) + { + acb_theta_agm_mul_tight_g1(res, a0, a, d0, d, g, prec); + } + else + { + acb_theta_agm_mul_tight_gen(res, a0, a, d0, d, g, prec); + } +} diff --git a/src/acb_theta/agm_sqrt.c b/src/acb_theta/agm_sqrt.c new file mode 100644 index 0000000000..67560c3b53 --- /dev/null +++ b/src/acb_theta/agm_sqrt.c @@ -0,0 +1,58 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb.h" +#include "acb_theta.h" + +static void +acb_theta_agm_sqrt_entry(acb_t res, const acb_t a, const acb_t rt, slong prec) +{ + acb_t y1, y2; + int t1, t2; + + acb_init(y1); + acb_init(y2); + + acb_sqrts(y1, y2, a, prec); + t1 = acb_overlaps(rt, y1); + t2 = acb_overlaps(rt, y2); + + if (t1 && t2) + { + acb_union(res, y1, y2, prec); + } + else if (t1) + { + acb_set(res, y1); + } + else if (t2) + { + acb_set(res, y2); + } + else + { + acb_indeterminate(res); + } + + acb_clear(y1); + acb_clear(y2); +} + +void +acb_theta_agm_sqrt(acb_ptr res, acb_srcptr a, acb_srcptr rts, slong nb, slong prec) +{ + slong k; + + for (k = 0; k < nb; k++) + { + acb_theta_agm_sqrt_entry(&res[k], &a[k], &rts[k], prec); + } +} diff --git a/src/acb_theta/all.c b/src/acb_theta/all.c new file mode 100644 index 0000000000..207bf4d549 --- /dev/null +++ b/src/acb_theta/all.c @@ -0,0 +1,95 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb.h" +#include "acb_mat.h" +#include "acb_theta.h" + +void +acb_theta_all(acb_ptr th, acb_srcptr z, const acb_mat_t tau, int sqr, slong prec) +{ + slong g = acb_mat_nrows(tau); + slong n2 = 1 << (2 * g); + fmpz_mat_t mat, gamma; + acb_mat_t w, c, N; + acb_ptr x, y, aux, units; + acb_t s, t; + ulong ab, image_ab; + slong kappa, e; + + fmpz_mat_init(mat, 2 * g, 2 * g); + acb_mat_init(w, g, g); + acb_mat_init(c, g, g); + acb_mat_init(N, g, g); + x = _acb_vec_init(g); + y = _acb_vec_init(g); + aux = _acb_vec_init(n2); + units = _acb_vec_init(8); + acb_init(s); + acb_init(t); + + acb_siegel_reduce(mat, tau, prec); + acb_siegel_transform_z(x, w, mat, z, tau, prec); + acb_siegel_cocycle(c, mat, tau, prec); + _acb_vec_unit_roots(units, 8, 8, prec); + + if (acb_siegel_is_reduced(w, -10, prec)) + { + sp2gz_inv(mat, mat); + + fmpz_mat_window_init(gamma, mat, g, 0, 2 * g, g); + acb_mat_set_fmpz_mat(N, gamma); + fmpz_mat_window_clear(gamma); + acb_mat_mul(N, c, N, prec); + acb_mat_vector_mul_col(y, N, x, prec); + acb_dot(t, NULL, 0, x, 1, y, 1, g, prec); + + acb_theta_ql_all(aux, x, w, sqr, prec); + + if (sqr) + { + kappa = acb_theta_transform_kappa2(mat); + acb_siegel_cocycle(c, mat, w, prec); + acb_mat_det(s, c, prec); + acb_mul_2exp_si(t, t, 1); + } + else + { + kappa = acb_theta_transform_kappa(s, mat, w, prec); + } + + acb_exp_pi_i(t, t, prec); + acb_mul(s, s, t, prec); + + for (ab = 0; ab < n2; ab++) + { + image_ab = acb_theta_transform_char(&e, mat, ab); + acb_mul(t, s, &units[((sqr ? 2 : 1) * (kappa + e)) % 8], prec); + acb_mul(&th[ab], &aux[image_ab], t, prec); + } + } + else + { + _acb_vec_indeterminate(th, n2); + } + + + fmpz_mat_clear(mat); + acb_mat_clear(w); + acb_mat_clear(c); + acb_mat_clear(N); + _acb_vec_clear(x, g); + _acb_vec_clear(y, g); + _acb_vec_clear(aux, n2); + _acb_vec_clear(units, 8); + acb_clear(s); + acb_clear(t); +} diff --git a/src/acb_theta/char_dot.c b/src/acb_theta/char_dot.c new file mode 100644 index 0000000000..75ad7e5ffa --- /dev/null +++ b/src/acb_theta/char_dot.c @@ -0,0 +1,30 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb_theta.h" + +slong +acb_theta_char_dot(ulong a, ulong b, slong g) +{ + int sgn = 0; + slong k; + ulong and = a & b; + + for (k = 0; k < g; k++) + { + if (and & 1) + { + sgn++; + } + and = and >> 1; + } + return sgn % 4; +} diff --git a/src/acb_theta/char_dot_acb.c b/src/acb_theta/char_dot_acb.c new file mode 100644 index 0000000000..a0e1c835b5 --- /dev/null +++ b/src/acb_theta/char_dot_acb.c @@ -0,0 +1,27 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb.h" +#include "acb_theta.h" + +void +acb_theta_char_dot_acb(acb_t x, ulong a, acb_srcptr z, slong g, slong prec) +{ + slong * v; + + v = flint_malloc(g * sizeof(slong)); + + acb_theta_char_get_slong(v, a, g); + acb_dot_si(x, NULL, 0, z, 1, v, 1, g, prec); + acb_mul_2exp_si(x, x, -1); + + flint_free(v); +} diff --git a/src/acb_theta/char_dot_slong.c b/src/acb_theta/char_dot_slong.c new file mode 100644 index 0000000000..44c995c205 --- /dev/null +++ b/src/acb_theta/char_dot_slong.c @@ -0,0 +1,31 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb_theta.h" + +slong +acb_theta_char_dot_slong(ulong a, const slong * n, slong g) +{ + ulong a_shift = a; + slong sgn = 0; + slong k; + + for (k = 0; k < g; k++) + { + if (a_shift & 1) + { + sgn += n[g - 1 - k] & 3; + } + a_shift = a_shift >> 1; + } + + return sgn % 4; +} diff --git a/src/acb_theta/char_get_a.c b/src/acb_theta/char_get_a.c new file mode 100644 index 0000000000..7f46b292fd --- /dev/null +++ b/src/acb_theta/char_get_a.c @@ -0,0 +1,27 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb_theta.h" + +ulong +acb_theta_char_get_a(const slong * n, slong g) +{ + slong k; + ulong a = 0; + + for (k = 0; k < g; k++) + { + a *= 2; + a += ((n[k] % 2) + 2) % 2; + } + + return a; +} diff --git a/src/acb_theta/char_get_acb.c b/src/acb_theta/char_get_acb.c new file mode 100644 index 0000000000..e31f02573a --- /dev/null +++ b/src/acb_theta/char_get_acb.c @@ -0,0 +1,26 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb.h" +#include "acb_theta.h" + +void +acb_theta_char_get_acb(acb_ptr v, ulong a, slong g) +{ + slong k; + + for (k = g - 1; k >= 0; k--) + { + acb_set_si(&v[k], a & 1); + a = a >> 1; + } + _acb_vec_scalar_mul_2exp_si(v, v, g, -1); +} diff --git a/src/acb_theta/char_get_arb.c b/src/acb_theta/char_get_arb.c new file mode 100644 index 0000000000..f42b5586f8 --- /dev/null +++ b/src/acb_theta/char_get_arb.c @@ -0,0 +1,26 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "arb.h" +#include "acb_theta.h" + +void +acb_theta_char_get_arb(arb_ptr v, ulong a, slong g) +{ + slong k; + + for (k = g - 1; k >= 0; k--) + { + arb_set_si(&v[k], a & 1); + a = a >> 1; + } + _arb_vec_scalar_mul_2exp_si(v, v, g, -1); +} diff --git a/src/acb_theta/char_get_slong.c b/src/acb_theta/char_get_slong.c new file mode 100644 index 0000000000..e0569952bf --- /dev/null +++ b/src/acb_theta/char_get_slong.c @@ -0,0 +1,24 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb_theta.h" + +void +acb_theta_char_get_slong(slong * n, ulong a, slong g) +{ + slong k; + + for (k = g - 1; k >= 0; k--) + { + n[k] = a & 1; + a = a >> 1; + } +} diff --git a/src/acb_theta/char_is_even.c b/src/acb_theta/char_is_even.c new file mode 100644 index 0000000000..d3553c9a2d --- /dev/null +++ b/src/acb_theta/char_is_even.c @@ -0,0 +1,19 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb_theta.h" + +int +acb_theta_char_is_even(ulong ab, slong g) +{ + ulong a = ab >> g; + return (acb_theta_char_dot(a, ab, g) % 2 == 0); +} diff --git a/src/acb_theta/char_is_goepel.c b/src/acb_theta/char_is_goepel.c new file mode 100644 index 0000000000..c864ed35b5 --- /dev/null +++ b/src/acb_theta/char_is_goepel.c @@ -0,0 +1,28 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb_theta.h" + +int +acb_theta_char_is_goepel(ulong ch1, ulong ch2, ulong ch3, ulong ch4, slong g) +{ + if (ch1 == ch2 || ch1 == ch3 || ch1 == ch4 + || ch2 == ch3 || ch2 == ch4 || ch3 == ch4) + { + return 0; + } + + return acb_theta_char_is_even(ch1, g) + && acb_theta_char_is_even(ch2, g) + && acb_theta_char_is_even(ch3, g) + && acb_theta_char_is_even(ch4, g) + && ((ch1 ^ ch2 ^ ch3 ^ ch4) == 0); +} diff --git a/src/acb_theta/char_is_syzygous.c b/src/acb_theta/char_is_syzygous.c new file mode 100644 index 0000000000..56115fe2d4 --- /dev/null +++ b/src/acb_theta/char_is_syzygous.c @@ -0,0 +1,18 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb_theta.h" + +int +acb_theta_char_is_syzygous(ulong ch1, ulong ch2, ulong ch3, slong g) +{ + return acb_theta_char_is_goepel(ch1, ch2, ch3, ch1 ^ ch2 ^ ch3, g); +} diff --git a/src/acb_theta/dist_a0.c b/src/acb_theta/dist_a0.c new file mode 100644 index 0000000000..183281a8c2 --- /dev/null +++ b/src/acb_theta/dist_a0.c @@ -0,0 +1,47 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb_mat.h" +#include "acb_theta.h" + +void +acb_theta_dist_a0(arb_ptr d, acb_srcptr z, const acb_mat_t tau, slong prec) +{ + slong g = acb_mat_nrows(tau); + slong n = 1 << g; + arb_mat_t Yinv, C; + arb_ptr v, w; + ulong a; + + arb_mat_init(Yinv, g, g); + arb_mat_init(C, g, g); + v = _arb_vec_init(g); + w = _arb_vec_init(g); + + acb_siegel_yinv(Yinv, tau, prec); + acb_siegel_cho(C, tau, prec); + + _acb_vec_get_imag(v, z, g); + arb_mat_vector_mul_col(v, Yinv, v, prec); + + for (a = 0; a < n; a++) + { + acb_theta_char_get_arb(w, a, g); + _arb_vec_add(w, v, w, g, prec); + arb_mat_vector_mul_col(w, C, w, prec); + acb_theta_dist_lat(&d[a], w, C, prec); + } + + arb_mat_clear(Yinv); + arb_mat_clear(C); + _arb_vec_clear(v, g); + _arb_vec_clear(w, g); +} diff --git a/src/acb_theta/dist_addprec.c b/src/acb_theta/dist_addprec.c new file mode 100644 index 0000000000..d23257147b --- /dev/null +++ b/src/acb_theta/dist_addprec.c @@ -0,0 +1,37 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "arb.h" +#include "acb_theta.h" + +slong +acb_theta_dist_addprec(const arb_t d2) +{ + arb_t x; + slong prec = ACB_THETA_LOW_PREC; + slong res; + + arb_init(x); + arb_const_log2(x, prec); + arb_div(x, d2, x, prec); + + if (arb_is_finite(x) && (arf_cmpabs_2exp_si(arb_midref(x), 30) <= 0)) + { + res = arf_get_si(arb_midref(x), prec); + } + else /* should never happen */ + { + res = 0; + } + + arb_clear(x); + return res; +} diff --git a/src/acb_theta/dist_lat.c b/src/acb_theta/dist_lat.c new file mode 100644 index 0000000000..a128afaee2 --- /dev/null +++ b/src/acb_theta/dist_lat.c @@ -0,0 +1,138 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb_mat.h" +#include "acb_theta.h" + +static void +acb_theta_dist_unif(arb_t d, const arb_mat_t C, slong prec) +{ + slong g = arb_mat_nrows(C); + arb_ptr v; + slong k; + + v = _arb_vec_init(g); + + for (k = 0; k < g; k++) + { + arb_zero_pm_one(&v[k]); + arb_mul_2exp_si(&v[k], &v[k], -1); + } + + arb_mat_vector_mul_col(v, C, v, prec); + arb_dot(d, NULL, 0, v, 1, v, 1, g, prec); + + _arb_vec_clear(v, g); +} + +static void +acb_theta_dist_ubound(arf_t u, arb_srcptr v, const arb_mat_t C, slong prec) +{ + slong g = acb_mat_nrows(C); + slong nb = 1 << g; + arb_mat_t Cinv; + arb_ptr x; + slong * approx; + slong * pt; + arb_t d; + arf_t b; + slong j, k; + int r = 1; + + arb_mat_init(Cinv, g, g); + x = _arb_vec_init(g); + approx = flint_malloc(2 * g * sizeof(slong)); + pt = flint_malloc(g * sizeof(slong)); + arb_init(d); + arf_init(b); + + arb_mat_one(Cinv); + arb_mat_solve_triu(Cinv, C, Cinv, 0, prec); + arb_mat_vector_mul_col(x, Cinv, v, prec); + r = _arb_vec_is_finite(x, g); + + for (k = 0; (k < g) && r; k++) + { + r = (arf_cmpabs_2exp_si(arb_midref(&x[k]), 30) <= 0); + if (r) + { + approx[2 * k] = - arf_get_si(arb_midref(&x[k]), ARF_RND_FLOOR); + approx[2 * k + 1] = - arf_get_si(arb_midref(&x[k]), ARF_RND_CEIL); + } + } + + arf_pos_inf(u); + if (r) + { + for (k = 0; k < nb; k++) + { + for (j = 0; j < g; j++) + { + pt[j] = approx[2 * j + (k & (1 << j) ? 0 : 1)]; + } + acb_theta_dist_pt(d, v, C, pt, prec); + arb_get_ubound_arf(b, d, prec); + arf_min(u, u, b); + } + } + + arb_mat_clear(Cinv); + _arb_vec_clear(x, g); + flint_free(approx); + flint_free(pt); + arb_clear(d); + arf_clear(b); +} + +void +acb_theta_dist_lat(arb_t d, arb_srcptr v, const arb_mat_t C, slong prec) +{ + slong g = arb_mat_nrows(C); + acb_theta_eld_t E; + slong nb; + slong * pts; + arf_t u; + arb_t x; + slong k; + int b; + + acb_theta_eld_init(E, g, g); + arf_init(u); + arb_init(x); + + acb_theta_dist_ubound(u, v, C, prec); + b = acb_theta_eld_set(E, C, u, v); + + if (b) + { + nb = acb_theta_eld_nb_pts(E); + pts = flint_malloc(nb * g * sizeof(slong)); + acb_theta_eld_points(pts, E); + + arb_pos_inf(d); + for (k = 0; k < nb; k++) + { + acb_theta_dist_pt(x, v, C, pts + k * g, prec); + arb_min(d, d, x, prec); + } + + flint_free(pts); + } + else + { + acb_theta_dist_unif(d, C, prec); + } + arb_nonnegative_part(d, d); + + acb_theta_eld_clear(E); + arf_clear(u); + arb_clear(x); +} diff --git a/src/acb_theta/dist_pt.c b/src/acb_theta/dist_pt.c new file mode 100644 index 0000000000..940831e049 --- /dev/null +++ b/src/acb_theta/dist_pt.c @@ -0,0 +1,33 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "arb_mat.h" +#include "acb_theta.h" + +void +acb_theta_dist_pt(arb_t d, arb_srcptr v, const arb_mat_t C, const slong * n, slong prec) +{ + slong g = arb_mat_nrows(C); + arb_ptr w; + slong k; + + w = _arb_vec_init(g); + + for (k = 0; k < g; k++) + { + arb_set_si(&w[k], n[k]); + } + arb_mat_vector_mul_col(w, C, w, prec); + _arb_vec_add(w, w, v, g, prec); + arb_dot(d, NULL, 0, w, 1, w, 1, g, prec); + + _arb_vec_clear(w, g); +} diff --git a/src/acb_theta/eld_border.c b/src/acb_theta/eld_border.c new file mode 100644 index 0000000000..9f3da69c53 --- /dev/null +++ b/src/acb_theta/eld_border.c @@ -0,0 +1,49 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb_theta.h" + +void +acb_theta_eld_border(slong * pts, const acb_theta_eld_t E) +{ + slong d = acb_theta_eld_dim(E); + slong g = acb_theta_eld_ambient_dim(E); + slong nr = acb_theta_eld_nr(E); + slong nl = acb_theta_eld_nl(E); + slong max = acb_theta_eld_max(E); + slong min = acb_theta_eld_min(E); + slong k, i; + + if (d == 1) + { + pts[0] = min - 1; + pts[g] = max + 1; + for (k = 1; k < g; k++) + { + pts[k] = acb_theta_eld_coord(E, k); + pts[k + g] = acb_theta_eld_coord(E, k); + } + } + else /* d > 1 */ + { + i = 0; + for (k = 0; k < nr; k++) + { + acb_theta_eld_border(&pts[i], acb_theta_eld_rchild(E, k)); + i += g * acb_theta_eld_nb_border(acb_theta_eld_rchild(E, k)); + } + for (k = 0; k < nl; k++) + { + acb_theta_eld_border(&pts[i], acb_theta_eld_lchild(E, k)); + i += g * acb_theta_eld_nb_border(acb_theta_eld_lchild(E, k)); + } + } +} diff --git a/src/acb_theta/eld_clear.c b/src/acb_theta/eld_clear.c new file mode 100644 index 0000000000..a529fb4cf1 --- /dev/null +++ b/src/acb_theta/eld_clear.c @@ -0,0 +1,40 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb_theta.h" + +void +acb_theta_eld_clear(acb_theta_eld_t E) +{ + slong k; + slong nr = acb_theta_eld_nr(E); + slong nl = acb_theta_eld_nl(E); + + if (nr > 0) + { + for (k = 0; k < nr; k++) + { + acb_theta_eld_clear(acb_theta_eld_rchild(E, k)); + } + flint_free(E->rchildren); + } + if (nl > 0) + { + for (k = 0; k < nl; k++) + { + acb_theta_eld_clear(acb_theta_eld_lchild(E, k)); + } + flint_free(E->lchildren); + } + + flint_free(E->last_coords); + flint_free(E->box); +} diff --git a/src/acb_theta/eld_contains.c b/src/acb_theta/eld_contains.c new file mode 100644 index 0000000000..829ab7a26e --- /dev/null +++ b/src/acb_theta/eld_contains.c @@ -0,0 +1,63 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb_theta.h" + +static int +acb_theta_eld_contains_rec(const acb_theta_eld_t E, const slong * pt) +{ + slong d = acb_theta_eld_dim(E); + slong c = pt[d - 1]; + slong k; + + if (c < acb_theta_eld_min(E) + || c > acb_theta_eld_max(E)) + { + return 0; + } + else if (d == 1) + { + return 1; + } + else if (c >= acb_theta_eld_mid(E)) + { + k = c - acb_theta_eld_mid(E); + return acb_theta_eld_contains_rec(acb_theta_eld_rchild(E, k), pt); + } + else + { + k = acb_theta_eld_mid(E) - 1 - c; + return acb_theta_eld_contains_rec(acb_theta_eld_lchild(E, k), pt); + } +} + +int +acb_theta_eld_contains(const acb_theta_eld_t E, const slong * pt) +{ + slong g = acb_theta_eld_ambient_dim(E); + slong d = acb_theta_eld_dim(E); + slong k; + + if (acb_theta_eld_nb_pts(E) == 0) + { + return 0; + } + + for (k = d; k < g; k++) + { + if (pt[k] != acb_theta_eld_coord(E, k)) + { + return 0; + } + } + + return acb_theta_eld_contains_rec(E, pt); +} diff --git a/src/acb_theta/eld_init.c b/src/acb_theta/eld_init.c new file mode 100644 index 0000000000..8c65874d3a --- /dev/null +++ b/src/acb_theta/eld_init.c @@ -0,0 +1,27 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb_theta.h" + +void +acb_theta_eld_init(acb_theta_eld_t E, slong d, slong g) +{ + FLINT_ASSERT(d >= 1 && d <= g); + + acb_theta_eld_dim(E) = d; + acb_theta_eld_ambient_dim(E) = g; + E->last_coords = flint_malloc((g - d) * sizeof(slong)); + E->rchildren = NULL; + acb_theta_eld_nr(E) = 0; + E->lchildren = NULL; + acb_theta_eld_nl(E) = 0; + E->box = flint_malloc(d * sizeof(slong)); +} diff --git a/src/acb_theta/eld_points.c b/src/acb_theta/eld_points.c new file mode 100644 index 0000000000..f48694b02c --- /dev/null +++ b/src/acb_theta/eld_points.c @@ -0,0 +1,50 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb_theta.h" + +void +acb_theta_eld_points(slong * pts, const acb_theta_eld_t E) +{ + slong d = acb_theta_eld_dim(E); + slong g = acb_theta_eld_ambient_dim(E); + slong nr = acb_theta_eld_nr(E); + slong nl = acb_theta_eld_nl(E); + slong k, j, i; + + if (d == 1) + { + i = 0; + for (k = acb_theta_eld_min(E); k <= acb_theta_eld_max(E); k++) + { + pts[i] = k; + for (j = 1; j < g; j++) + { + pts[i + j] = acb_theta_eld_coord(E, j); + } + i += g; + } + } + else /* d > 1 */ + { + i = 0; + for (k = 0; k < nr; k++) + { + acb_theta_eld_points(&pts[i], acb_theta_eld_rchild(E, k)); + i += g * acb_theta_eld_nb_pts(acb_theta_eld_rchild(E, k)); + } + for (k = 0; k < nl; k++) + { + acb_theta_eld_points(&pts[i], acb_theta_eld_lchild(E, k)); + i += g * acb_theta_eld_nb_pts(acb_theta_eld_lchild(E, k)); + } + } +} diff --git a/src/acb_theta/eld_print.c b/src/acb_theta/eld_print.c new file mode 100644 index 0000000000..8a7b3847aa --- /dev/null +++ b/src/acb_theta/eld_print.c @@ -0,0 +1,43 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb_theta.h" + +void +acb_theta_eld_print(const acb_theta_eld_t E) +{ + slong d = acb_theta_eld_dim(E); + slong g = acb_theta_eld_ambient_dim(E); + slong k; + + for (k = 0; k < g - d; k++) + { + flint_printf(" "); + } + flint_printf("Slice (..."); + for (k = 0; k < g - d; k++) + { + flint_printf(", %wd", acb_theta_eld_coord(E, k + d)); + } + flint_printf("): from %wd to %wd (mid: %wd)\n", + acb_theta_eld_min(E), acb_theta_eld_max(E), acb_theta_eld_mid(E)); + if (d > 1) + { + for (k = 0; k < acb_theta_eld_nr(E); k++) + { + acb_theta_eld_print(acb_theta_eld_rchild(E, k)); + } + for (k = 0; k < acb_theta_eld_nl(E); k++) + { + acb_theta_eld_print(acb_theta_eld_lchild(E, k)); + } + } +} diff --git a/src/acb_theta/eld_set.c b/src/acb_theta/eld_set.c new file mode 100644 index 0000000000..c4ebd081ef --- /dev/null +++ b/src/acb_theta/eld_set.c @@ -0,0 +1,330 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "arb_mat.h" +#include "acb_theta.h" + +#define ACB_THETA_ELD_MAX_PTS 1000000 +#define ACB_THETA_ELD_MAX_ERR 100 + +static void +slong_vec_max(slong * r, slong * v1, slong * v2, slong d) +{ + slong k; + for (k = 0; k < d; k++) + { + r[k] = FLINT_MAX(v1[k], v2[k]); + } +} + +static int +arf_get_si_safe(slong * m, const arf_t x, arf_rnd_t rnd) +{ + if (!arf_is_finite(x)) + { + return 0; + } + else if (arf_cmpabs_2exp_si(x, FLINT_BITS - 4) > 0) + { + return 0; + } + else + { + *m = arf_get_si(x, rnd); + return 1; + } +} + +static int +acb_theta_eld_interval(slong * min, slong * mid, slong * max, const arb_t ctr, const arf_t rad) +{ + slong lp = ACB_THETA_LOW_PREC; + slong e; + arb_t y; + arf_t b; + int res; + + arb_init(y); + arf_init(b); + + arf_set_mag(b, arb_radref(ctr)); + res = arf_get_si_safe(&e, b, ARF_RND_NEAR); + if (res) + { + res = (e <= ACB_THETA_ELD_MAX_ERR); + } + + res = res && arf_get_si_safe(mid, arb_midref(ctr), ARF_RND_NEAR); + + arb_set_arf(y, rad); + arb_add(y, ctr, y, lp); + arb_get_ubound_arf(b, y, lp); + res = res && arf_get_si_safe(max, b, ARF_RND_FLOOR); + + arb_set_arf(y, rad); + arb_sub(y, ctr, y, lp); + arb_get_lbound_arf(b, y, lp); + res = res && arf_get_si_safe(min, b, ARF_RND_CEIL); + + arb_clear(y); + arf_clear(b); + return res; +} + +static void +acb_theta_eld_next_R2(arf_t next_R2, const arf_t R2, const arb_t gamma, const arb_t v, slong k) +{ + slong lp = ACB_THETA_LOW_PREC; + arb_t x; + arf_t sub; + + arb_init(x); + arf_init(sub); + + /* Set next_R2 to R2 - (v + gamma*k)^2 */ + arb_mul_si(x, gamma, k, lp); + arb_add(x, x, v, lp); + arb_sqr(x, x, lp); + + arb_get_lbound_arf(sub, x, lp); + arf_sub(next_R2, R2, sub, lp, ARF_RND_CEIL); + + arb_clear(x); + arf_clear(sub); +} + +static void +acb_theta_eld_init_children(acb_theta_eld_t E, slong nr, slong nl) +{ + slong d = acb_theta_eld_dim(E); + slong g = acb_theta_eld_ambient_dim(E); + slong k; + + if (nr > 0) /* should always be the case */ + { + E->rchildren = flint_malloc(nr * sizeof(struct acb_theta_eld_struct)); + acb_theta_eld_nr(E) = nr; + for (k = 0; k < nr; k++) + { + acb_theta_eld_init(acb_theta_eld_rchild(E, k), d - 1, g); + } + } + if (nl > 0) + { + E->lchildren = flint_malloc(nl * sizeof(struct acb_theta_eld_struct)); + acb_theta_eld_nl(E) = nl; + for (k = 0; k < nl; k++) + { + acb_theta_eld_init(acb_theta_eld_lchild(E, k), d - 1, g); + } + } +} + +static int +acb_theta_eld_init_interval(acb_theta_eld_t E, const arb_mat_t C, + const arf_t R2, arb_srcptr v, slong * last_coords) +{ + slong min, mid, max; + slong d = acb_theta_eld_dim(E); + slong g = acb_theta_eld_ambient_dim(E); + slong lp = ACB_THETA_LOW_PREC; + slong k; + arb_t x, ctr; + arf_t rad; + int res; + + arb_init(x); + arb_init(ctr); + arf_init(rad); + + for (k = 0; k < g - d; k++) + { + E->last_coords[k] = last_coords[k]; + } + + if (arf_cmp_si(R2, 0) < 0) + { + arf_zero(rad); + } + else + { + arb_set_arf(x, R2); + arb_sqrt(x, x, lp); + arb_div(x, x, arb_mat_entry(C, d - 1, d - 1), lp); + arb_get_ubound_arf(rad, x, lp); + } + + arb_div(ctr, &v[d - 1], arb_mat_entry(C, d - 1, d - 1), lp); + arb_neg(ctr, ctr); + res = acb_theta_eld_interval(&min, &mid, &max, ctr, rad); + + if (res) + { + acb_theta_eld_min(E) = min; + acb_theta_eld_mid(E) = mid; + acb_theta_eld_max(E) = max; + } + + arb_clear(x); + arb_clear(ctr); + arf_clear(rad); + return res; +} + +/* Main recursive function in dimension d>1 */ + +static int +acb_theta_eld_set_rec(acb_theta_eld_t E, const arb_mat_t C, + const arf_t R2, arb_srcptr v, slong * last_coords) +{ + slong d = acb_theta_eld_dim(E); + slong g = acb_theta_eld_ambient_dim(E); + slong lp = ACB_THETA_LOW_PREC; + slong min, mid, max, k; + arf_t next_R2; + slong *next_coords; + arb_ptr v_diff; + arb_ptr v_mid; + arb_ptr next_v; + slong c; + slong nr, nl; + int res; + + res = acb_theta_eld_init_interval(E, C, R2, v, last_coords); + if (!res) + { + return 0; + } + min = acb_theta_eld_min(E); + mid = acb_theta_eld_mid(E); + max = acb_theta_eld_max(E); + + /* Induction only if d > 1 and min <= max */ + if (min > max) + { + acb_theta_eld_nb_pts(E) = 0; + if (d == 1) + { + acb_theta_eld_nb_border(E) = 2; + } + else + { + acb_theta_eld_nb_border(E) = 0; + } + for (k = 0; k < d; k++) + { + acb_theta_eld_box(E, k) = 0; + } + return 1; + } + else if (d == 1) + { + acb_theta_eld_nb_pts(E) = max - min + 1; + acb_theta_eld_nb_border(E) = 2; + acb_theta_eld_box(E, 0) = FLINT_MAX(max, -min); + return (acb_theta_eld_nb_pts(E) <= ACB_THETA_ELD_MAX_PTS); + } + + /* Begin main function */ + arf_init(next_R2); + next_coords = flint_malloc((g - d + 1) * sizeof(slong)); + v_diff = _arb_vec_init(d - 1); + v_mid = _arb_vec_init(d - 1); + next_v = _arb_vec_init(d - 1); + + /* Initialize children */ + nr = max - mid + 1; + nl = mid - min; + acb_theta_eld_init_children(E, nr, nl); + + /* Set v_mid, v_diff */ + for (k = 0; k < d - 1; k++) + { + arb_set(&v_diff[k], arb_mat_entry(C, k, d - 1)); + arb_mul_si(&v_mid[k], &v_diff[k], mid, lp); + } + _arb_vec_add(v_mid, v_mid, v, d - 1, lp); + for (k = 0; k < g - d; k++) + { + next_coords[k + 1] = last_coords[k]; + } + + /* Set children recursively */ + acb_theta_eld_nb_pts(E) = 0; + acb_theta_eld_nb_border(E) = 0; + acb_theta_eld_box(E, d - 1) = FLINT_MAX(max, -min); + for (k = 0; k < d - 1; k++) + { + acb_theta_eld_box(E, k) = 0; + } + + /* Right loop */ + _arb_vec_set(next_v, v_mid, d - 1); + for (k = 0; (k < nr) && res; k++) + { + if (k > 0) + { + _arb_vec_add(next_v, next_v, v_diff, d - 1, lp); + } + + c = mid + k; + acb_theta_eld_next_R2(next_R2, R2, arb_mat_entry(C, d - 1, d - 1), &v[d - 1], c); + next_coords[0] = c; + res = acb_theta_eld_set_rec(acb_theta_eld_rchild(E, k), C, next_R2, + next_v, next_coords); + if (res) + { + acb_theta_eld_nb_pts(E) += acb_theta_eld_nb_pts(acb_theta_eld_rchild(E, k)); + acb_theta_eld_nb_border(E) += acb_theta_eld_nb_border(acb_theta_eld_rchild(E, k)); + slong_vec_max(E->box, E->box, acb_theta_eld_rchild(E, k)->box, d - 1); + res = (acb_theta_eld_nb_pts(E) <= ACB_THETA_ELD_MAX_PTS); + } + } + + /* Left loop */ + _arb_vec_set(next_v, v_mid, d - 1); + for (k = 0; (k < nl) && res; k++) + { + _arb_vec_sub(next_v, next_v, v_diff, d - 1, lp); + + c = mid - (k + 1); + acb_theta_eld_next_R2(next_R2, R2, arb_mat_entry(C, d - 1, d - 1), &v[d - 1], c); + next_coords[0] = c; + res = acb_theta_eld_set_rec(acb_theta_eld_lchild(E, k), C, next_R2, + next_v, next_coords); + + if (res) /* we expect this always holds */ + { + acb_theta_eld_nb_pts(E) += acb_theta_eld_nb_pts(acb_theta_eld_lchild(E, k)); + acb_theta_eld_nb_border(E) += acb_theta_eld_nb_border(acb_theta_eld_lchild(E, k)); + slong_vec_max(E->box, E->box, acb_theta_eld_lchild(E, k)->box, d - 1); + res = (acb_theta_eld_nb_pts(E) <= ACB_THETA_ELD_MAX_PTS); + } + } + + arf_clear(next_R2); + flint_free(next_coords); + _arb_vec_clear(v_diff, d - 1); + _arb_vec_clear(v_mid, d - 1); + _arb_vec_clear(next_v, d - 1); + return res; +} + +int +acb_theta_eld_set(acb_theta_eld_t E, const arb_mat_t C, const arf_t R2, arb_srcptr v) +{ + slong d = acb_theta_eld_dim(E); + slong g = acb_theta_eld_ambient_dim(E); + + acb_theta_eld_clear(E); + acb_theta_eld_init(E, d, g); + return acb_theta_eld_set_rec(E, C, R2, v, NULL); +} diff --git a/src/acb_theta/g2_character.c b/src/acb_theta/g2_character.c new file mode 100644 index 0000000000..aa16412e31 --- /dev/null +++ b/src/acb_theta/g2_character.c @@ -0,0 +1,104 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "fmpz.h" +#include "acb_theta.h" + +/* See Cléry, Faber, van der Geer, "Covariants of binary sextics and modular + forms of degree 2 with character", §12 */ + +static void +g2_block_coeffs_mod_2(slong * coeffs, const fmpz_mat_t w) +{ + fmpz_t x; + + fmpz_init(x); + coeffs[0] = fmpz_mod_ui(x, fmpz_mat_entry(w, 0, 0), 2); + coeffs[1] = fmpz_mod_ui(x, fmpz_mat_entry(w, 0, 1), 2); + coeffs[2] = fmpz_mod_ui(x, fmpz_mat_entry(w, 1, 0), 2); + coeffs[3] = fmpz_mod_ui(x, fmpz_mat_entry(w, 1, 1), 2); + fmpz_clear(x); +} + +static slong +g2_block_det_mod_2(slong * coeffs) +{ + return (coeffs[0] * coeffs[3] + coeffs[1] * coeffs[2]) % 2; +} + +static slong +g2_character_formula(slong * a, slong * b, slong * c, slong * d) +{ + return (a[0] * c[0] + a[1] * c[0] + a[1] * c[1] + a[2] * c[2] + a[3] * c[2] + + a[3] * c[3] + c[0] * c[1] + c[1] * c[2] + c[2] * c[3] + c[0] * d[3] + + c[1] * d[2] + c[1] * d[3] + c[2] * d[1] + c[3] * d[0] + c[3] * d[1]) % 2; +} + +static slong +g2_character_switch(slong * a, slong * b, slong * c, slong * d, int twice) +{ + slong row[4]; + + if (g2_block_det_mod_2(c) == 1) + { + return g2_character_formula(a, b, c, d); + } + if (g2_block_det_mod_2(a) == 1) + { + return g2_character_formula(c, d, a, b); + } + if (g2_block_det_mod_2(d) == 1) + { + return g2_character_formula(b, a, d, c); + } + if (g2_block_det_mod_2(b) == 1) + { + return g2_character_formula(d, c, b, a); + } + + if (twice) + { + flint_printf("error: went through g2_character_switch twice\n"); + flint_abort(); + } + row[0] = a[0]; + row[1] = a[1]; + row[2] = b[0]; + row[3] = b[1]; + a[0] = c[0]; + a[1] = c[1]; + b[0] = d[0]; + b[1] = d[1]; + c[0] = row[0]; + c[1] = row[1]; + d[0] = row[2]; + d[1] = row[3]; + return 1 - g2_character_switch(a, b, c, d, 1); +} + +slong +acb_theta_g2_character(const fmpz_mat_t mat) +{ + fmpz_mat_t w; + slong coeffs[16]; + slong j, k; + + for (j = 0; j < 2; j++) + { + for (k = 0; k < 2; k++) + { + fmpz_mat_window_init(w, mat, 2 * j, 2 * k, 2 * j + 2, 2 * k + 2); + g2_block_coeffs_mod_2(coeffs + 4 * (2 * j + k), w); + fmpz_mat_window_clear(w); + } + } + return g2_character_switch(coeffs, coeffs + 4, coeffs + 8, coeffs + 12, 0); +} diff --git a/src/acb_theta/g2_chi10.c b/src/acb_theta/g2_chi10.c new file mode 100644 index 0000000000..fdead69ded --- /dev/null +++ b/src/acb_theta/g2_chi10.c @@ -0,0 +1,36 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb.h" +#include "acb_theta.h" + +void +acb_theta_g2_chi10(acb_t res, acb_srcptr th2, slong prec) +{ + slong g = 2; + slong n = 1 << (2 * g); + ulong ab; + acb_t t; + + acb_init(t); + acb_one(t); + + for (ab = 0; ab < n; ab++) + { + if (acb_theta_char_is_even(ab, g)) + { + acb_mul(t, t, &th2[ab], prec); + } + } + acb_mul_2exp_si(res, t, -12); + + acb_clear(t); +} diff --git a/src/acb_theta/g2_chi12.c b/src/acb_theta/g2_chi12.c new file mode 100644 index 0000000000..932d8b9dcd --- /dev/null +++ b/src/acb_theta/g2_chi12.c @@ -0,0 +1,57 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb.h" +#include "acb_theta.h" + +void +acb_theta_g2_chi12(acb_t res, acb_srcptr th2, slong prec) +{ + slong g = 2; + ulong ch1, ch2, ch3, ch4, ab; + ulong n = 1 << (2 * g); + acb_t s, t; + + acb_init(s); + acb_init(t); + + for (ch1 = 0; ch1 < n; ch1++) + { + for (ch2 = ch1 + 1; ch2 < n; ch2++) + { + for (ch3 = ch2 + 1; ch3 < n; ch3++) + { + ch4 = ch1 ^ ch2 ^ ch3; + if (acb_theta_char_is_goepel(ch1, ch2, ch3, ch4, g)) + { + acb_one(t); + for (ab = 0; ab < n; ab++) + { + if (acb_theta_char_is_even(ab, g) + && (ab != ch1) + && (ab != ch2) + && (ab != ch3) + && (ab != ch4)) + { + acb_mul(t, t, &th2[ab], prec); + } + } + acb_sqr(t, t, prec); + acb_add(s, s, t, prec); + } + } + } + } + acb_mul_2exp_si(res, s, -15); + + acb_clear(s); + acb_clear(t); +} diff --git a/src/acb_theta/g2_chi35.c b/src/acb_theta/g2_chi35.c new file mode 100644 index 0000000000..5bb9aedb70 --- /dev/null +++ b/src/acb_theta/g2_chi35.c @@ -0,0 +1,118 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb.h" +#include "acb_theta.h" + +/* Bolza to Mumford: + 0 -> {0,5} + 2 -> {2,5} + 4 -> {4,5} + 5 -> empty set + Then: {i,j} -> S = {i+1,j+1} */ + +/* Mumford to characteristics: + emptyset -> 0 + {1,2} -> 2 + {1,4} -> 9 + {1,6} -> 12 + {2,3} -> 8 + {2,5} -> 15 + {3,4} -> 3 + {3,6} -> 6 + {4,5} -> 4 + {5,6} -> 1 */ + +/* See Bolza, "Darstellung von Invarianten durch \theta-Functionen, p.493 */ +static void +bolza_E(acb_t E, acb_srcptr th, slong prec) +{ + acb_ptr R; + acb_ptr v; + acb_ptr cmp; + acb_t P; + slong k; + + R = _acb_vec_init(15); + v = _acb_vec_init(16); + cmp = _acb_vec_init(15); + acb_init(P); + + for (k = 0; k < 16; k++) + { + acb_pow_ui(&v[k], &th[k], 4, prec); + } + + acb_sub(&R[0], &v[2], &v[6], prec); + acb_sub(&cmp[0], &v[1], &v[9], prec); + acb_sub(&R[1], &v[8], &v[12], prec); + acb_sub(&cmp[1], &v[1], &v[3], prec); + acb_sub(&R[2], &v[0], &v[4], prec); + acb_add(&cmp[2], &v[9], &v[3], prec); + acb_sub(&R[3], &v[4], &v[12], prec); + acb_sub(&cmp[3], &v[2], &v[3], prec); + acb_sub(&R[4], &v[0], &v[8], prec); + acb_add(&cmp[4], &v[6], &v[3], prec); + acb_sub(&R[5], &v[4], &v[6], prec); + acb_sub(&cmp[5], &v[8], &v[9], prec); + acb_sub(&R[6], &v[0], &v[2], prec); + acb_add(&cmp[6], &v[12], &v[9], prec); + acb_add(&R[7], &v[12], &v[6], prec); + acb_sub(&cmp[7], &v[0], &v[1], prec); + acb_sub(&R[8], &v[4], &v[2], prec); + acb_sub(&cmp[8], &v[8], &v[1], prec); + acb_add(&R[9], &v[8], &v[2], prec); + acb_add(&cmp[9], &v[4], &v[1], prec); + acb_sub(&R[10], &v[0], &v[6], prec); + acb_add(&cmp[10], &v[12], &v[1], prec); + acb_add(&R[11], &v[12], &v[2], prec); + acb_sub(&cmp[11], &v[0], &v[9], prec); + acb_sub(&R[12], &v[4], &v[8], prec); + acb_sub(&cmp[12], &v[2], &v[1], prec); + acb_add(&R[13], &v[6], &v[8], prec); + acb_sub(&cmp[13], &v[0], &v[3], prec); + acb_sub(&R[14], &v[0], &v[12], prec); + acb_add(&cmp[14], &v[2], &v[9], prec); + + acb_one(P); + for (k = 0; k < 16; k++) + { + if (acb_theta_char_is_even(k, 2)) + { + acb_mul(P, P, &th[k], prec); + } + } + acb_one(E); + for (k = 0; k < 15; k++) + { + acb_mul(E, E, &R[k], prec); + } + acb_mul(E, E, P, prec); /* prod (theta) * prod(Ri) */ + + _acb_vec_clear(R, 15); + _acb_vec_clear(v, 16); + _acb_vec_clear(cmp, 15); + acb_clear(P); +} + +/* See Igusa, "Modular forms and projective invariants" p. 848 */ +void +acb_theta_g2_chi35(acb_t res, acb_srcptr th, slong prec) +{ + acb_t t; + acb_init(t); + + bolza_E(t, th, prec); + acb_neg(res, t); + acb_mul_2exp_si(res, res, -37); + + acb_clear(t); +} diff --git a/src/acb_theta/g2_chi3_6.c b/src/acb_theta/g2_chi3_6.c new file mode 100644 index 0000000000..98557b0e28 --- /dev/null +++ b/src/acb_theta/g2_chi3_6.c @@ -0,0 +1,65 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb_poly.h" +#include "acb_theta.h" + +void +acb_theta_g2_chi3_6(acb_poly_t res, acb_srcptr dth, slong prec) +{ + slong g = 2; + slong n = 1 << (2 * g); + slong orders[2] = {1, 0}; + slong i1 = acb_theta_jet_index(orders, g) - 1; /* 0 or 1 */ + slong nb = acb_theta_jet_nb(1, g); + acb_poly_struct * aux; + acb_poly_t s; + acb_t den; + ulong ab; + slong k; + + aux = flint_malloc(6 * sizeof(acb_poly_struct)); + acb_poly_init(s); + acb_init(den); + + for (k = 0; k < 6; k++) + { + acb_poly_init(&aux[k]); + } + + k = 0; + for (ab = 0; ab < n; ab++) + { + if (!acb_theta_char_is_even(ab, g)) + { + acb_poly_set_coeff_acb(&aux[k], 1, &dth[nb * ab + 1 + i1]); + acb_poly_set_coeff_acb(&aux[k], 0, &dth[nb * ab + 1 + (1 - i1)]); + k++; + } + } + acb_poly_mul(res, &aux[0], &aux[1], prec); + acb_poly_mul(res, res, &aux[2], prec); + acb_poly_mul(s, &aux[3], &aux[4], prec); + acb_poly_mul(s, s, &aux[5], prec); + acb_poly_mul(res, res, s, prec); + acb_const_pi(den, prec); + acb_pow_ui(den, den, 6, prec); + acb_poly_scalar_div(res, res, den, prec); + acb_poly_scalar_mul_2exp_si(res, res, -6); + + acb_poly_clear(s); + acb_clear(den); + for (k = 0; k < 6; k++) + { + acb_poly_clear(&aux[k]); + } + flint_free(aux); +} diff --git a/src/acb_theta/g2_chi5.c b/src/acb_theta/g2_chi5.c new file mode 100644 index 0000000000..ba7b3c808e --- /dev/null +++ b/src/acb_theta/g2_chi5.c @@ -0,0 +1,37 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb.h" +#include "acb_theta.h" + +void +acb_theta_g2_chi5(acb_t res, acb_srcptr th, slong prec) +{ + slong g = 2; + slong n = 1 << (2 * g); + ulong ab; + acb_t t; + + acb_init(t); + acb_one(t); + + for (ab = 0; ab < n; ab++) + { + if (acb_theta_char_is_even(ab, g)) + { + acb_mul(t, t, &th[ab], prec); + } + } + acb_neg(res, t); + acb_mul_2exp_si(res, res, -6); + + acb_clear(t); +} diff --git a/src/acb_theta/g2_covariants.c b/src/acb_theta/g2_covariants.c new file mode 100644 index 0000000000..724e89a2ae --- /dev/null +++ b/src/acb_theta/g2_covariants.c @@ -0,0 +1,79 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb_poly.h" +#include "acb_theta.h" + +static void +acb_theta_g2_transvectants(acb_poly_struct * res, const acb_poly_t f, slong prec) +{ + acb_poly_t s; + + acb_poly_init(s); + + acb_poly_set(&res[0], f); + acb_theta_g2_transvectant(&res[1], f, f, 6, 6, 6, prec); + acb_theta_g2_transvectant(&res[2], f, f, 6, 6, 4, prec); + acb_theta_g2_transvectant(&res[3], f, f, 6, 6, 2, prec); + acb_theta_g2_transvectant(&res[4], f, &res[2], 6, 4, 4, prec); + acb_theta_g2_transvectant(&res[5], f, &res[2], 6, 4, 2, prec); + acb_theta_g2_transvectant(&res[6], f, &res[2], 6, 4, 1, prec); + acb_theta_g2_transvectant(&res[7], f, &res[3], 6, 8, 1, prec); + acb_theta_g2_transvectant(&res[8], &res[2], &res[2], 4, 4, 4, prec); + acb_theta_g2_transvectant(&res[9], f, &res[4], 6, 2, 2, prec); + acb_theta_g2_transvectant(&res[10], f, &res[4], 6, 2, 1, prec); + acb_theta_g2_transvectant(&res[11], &res[3], &res[2], 8, 4, 1, prec); + acb_theta_g2_transvectant(&res[12], &res[2], &res[4], 4, 2, 2, prec); + acb_theta_g2_transvectant(&res[13], &res[2], &res[4], 4, 2, 1, prec); + acb_theta_g2_transvectant(&res[14], &res[3], &res[4], 8, 2, 1, prec); + acb_theta_g2_transvectant(&res[15], &res[4], &res[4], 2, 2, 2, prec); + acb_theta_g2_transvectant(&res[16], &res[5], &res[4], 6, 2, 1, prec); + acb_theta_g2_transvectant(&res[17], &res[6], &res[4], 8, 2, 2, prec); + acb_poly_mul(s, &res[4], &res[4], prec); /* now C_32^2 */ + acb_theta_g2_transvectant(&res[18], f, s, 6, 4, 4, prec); + acb_theta_g2_transvectant(&res[19], f, s, 6, 4, 3, prec); + acb_theta_g2_transvectant(&res[20], &res[2], s, 4, 4, 3, prec); + acb_theta_g2_transvectant(&res[21], &res[6], s, 8, 4, 4, prec); + acb_poly_mul(s, s, &res[4], prec); /* now C_32^3 */ + acb_theta_g2_transvectant(&res[22], f, s, 6, 6, 6, prec); + acb_theta_g2_transvectant(&res[23], f, s, 6, 6, 5, prec); + acb_theta_g2_transvectant(&res[24], &res[6], s, 8, 6, 6, prec); + acb_poly_mul(s, s, &res[4], prec); /* now C_32^4 */ + acb_theta_g2_transvectant(&res[25], &res[6], s, 8, 8, 8, prec); + + acb_poly_clear(s); +} + +void +acb_theta_g2_covariants(acb_poly_struct * res, const acb_poly_t f, slong prec) +{ + double cofactors[ACB_THETA_G2_COV_NB] = {1, 60, 75, 90, 2250, 2250, 450, + 540, 11250, 67500, 13500, 13500, 168750, 67500, 405000, 10125000, + 2025000, 2700000, 151875000, 60750000, 15187500, 9112500000, + 227812500000, 13668750000, 8201250000000, 384433593750}; + acb_t c; + fmpz_t m; + slong k; + + acb_init(c); + fmpz_init(m); + + acb_theta_g2_transvectants(res, f, prec); + for (k = 0; k < ACB_THETA_G2_COV_NB; k++) + { + fmpz_set_d(m, cofactors[k]); + acb_set_fmpz(c, m); + acb_poly_scalar_mul(&res[k], &res[k], c, prec); + } + + acb_clear(c); + fmpz_clear(m); +} diff --git a/src/acb_theta/g2_covariants_lead.c b/src/acb_theta/g2_covariants_lead.c new file mode 100644 index 0000000000..80004da643 --- /dev/null +++ b/src/acb_theta/g2_covariants_lead.c @@ -0,0 +1,95 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb_poly.h" +#include "acb_theta.h" + +static void +acb_theta_g2_transvectants(acb_ptr res, const acb_poly_t f, slong prec) +{ + acb_poly_t s, r2, r3, r4, r5, r6; + + acb_poly_init(s); + acb_poly_init(r2); + acb_poly_init(r3); + acb_poly_init(r4); + acb_poly_init(r5); + acb_poly_init(r6); + + /* Get polynomials */ + acb_theta_g2_transvectant(r2, f, f, 6, 6, 4, prec); + acb_theta_g2_transvectant(r3, f, f, 6, 6, 2, prec); + acb_theta_g2_transvectant(r4, f, r2, 6, 4, 4, prec); + acb_theta_g2_transvectant(r5, f, r2, 6, 4, 2, prec); + acb_theta_g2_transvectant(r6, f, r2, 6, 4, 1, prec); + + /* Get leading coefficients of f, r2, ..., r6 */ + acb_poly_get_coeff_acb(&res[0], f, 6); + acb_poly_get_coeff_acb(&res[2], r2, 4); + acb_poly_get_coeff_acb(&res[3], r3, 8); + acb_poly_get_coeff_acb(&res[4], r4, 2); + acb_poly_get_coeff_acb(&res[5], r5, 6); + acb_poly_get_coeff_acb(&res[6], r6, 8); + + /* Get other coefficients */ + acb_theta_g2_transvectant_lead(&res[1], f, f, 6, 6, 6, prec); + acb_theta_g2_transvectant_lead(&res[7], f, r3, 6, 8, 1, prec); + acb_theta_g2_transvectant_lead(&res[8], r2, r2, 4, 4, 4, prec); + acb_theta_g2_transvectant_lead(&res[9], f, r4, 6, 2, 2, prec); + acb_theta_g2_transvectant_lead(&res[10], f, r4, 6, 2, 1, prec); + acb_theta_g2_transvectant_lead(&res[11], r3, r2, 8, 4, 1, prec); + acb_theta_g2_transvectant_lead(&res[12], r2, r4, 4, 2, 2, prec); + acb_theta_g2_transvectant_lead(&res[13], r2, r4, 4, 2, 1, prec); + acb_theta_g2_transvectant_lead(&res[14], r3, r4, 8, 2, 1, prec); + acb_theta_g2_transvectant_lead(&res[15], r4, r4, 2, 2, 2, prec); + acb_theta_g2_transvectant_lead(&res[16], r5, r4, 6, 2, 1, prec); + acb_theta_g2_transvectant_lead(&res[17], r6, r4, 8, 2, 2, prec); + acb_poly_mul(s, r4, r4, prec); /* C_32^2 */ + acb_theta_g2_transvectant_lead(&res[18], f, s, 6, 4, 4, prec); + acb_theta_g2_transvectant_lead(&res[19], f, s, 6, 4, 3, prec); + acb_theta_g2_transvectant_lead(&res[20], r2, s, 4, 4, 3, prec); + acb_theta_g2_transvectant_lead(&res[21], r6, s, 8, 4, 4, prec); + acb_poly_mul(s, s, r4, prec); /* now C_32^3 */ + acb_theta_g2_transvectant_lead(&res[22], f, s, 6, 6, 6, prec); + acb_theta_g2_transvectant_lead(&res[23], f, s, 6, 6, 5, prec); + acb_theta_g2_transvectant_lead(&res[24], r6, s, 8, 6, 6, prec); + acb_poly_mul(s, s, r4, prec); /* now C_32^4 */ + acb_theta_g2_transvectant_lead(&res[25], r6, s, 8, 8, 8, prec); + + acb_poly_clear(s); + acb_poly_clear(r2); + acb_poly_clear(r3); + acb_poly_clear(r4); + acb_poly_clear(r5); + acb_poly_clear(r6); +} + +void +acb_theta_g2_covariants_lead(acb_ptr res, const acb_poly_t f, slong prec) +{ + double cofactors[ACB_THETA_G2_COV_NB] = {1, 60, 75, 90, 2250, 2250, 450, + 540, 11250, 67500, 13500, 13500, 168750, 67500, 405000, 10125000, + 2025000, 2700000, 151875000, 60750000, 15187500, 9112500000, + 227812500000, 13668750000, 8201250000000, 384433593750}; + fmpz_t m; + slong k; + + fmpz_init(m); + + acb_theta_g2_transvectants(res, f, prec); + for (k = 0; k < ACB_THETA_G2_COV_NB; k++) + { + fmpz_set_d(m, cofactors[k]); + acb_mul_fmpz(&res[k], &res[k], m, prec); + } + + fmpz_clear(m); +} diff --git a/src/acb_theta/g2_detk_symj.c b/src/acb_theta/g2_detk_symj.c new file mode 100644 index 0000000000..46ac317b72 --- /dev/null +++ b/src/acb_theta/g2_detk_symj.c @@ -0,0 +1,55 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb_poly.h" +#include "acb_mat.h" +#include "acb_theta.h" + +void +acb_theta_g2_detk_symj(acb_poly_t res, const acb_mat_t m, const acb_poly_t f, + slong k, slong j, slong prec) +{ + acb_poly_t x, y, t, u, aux; + acb_t a; + slong i; + + acb_poly_init(x); + acb_poly_init(y); + acb_poly_init(t); + acb_poly_init(u); + acb_poly_init(aux); + acb_init(a); + + acb_poly_set_coeff_acb(x, 0, acb_mat_entry(m, 1, 0)); + acb_poly_set_coeff_acb(x, 1, acb_mat_entry(m, 0, 0)); + acb_poly_set_coeff_acb(y, 0, acb_mat_entry(m, 1, 1)); + acb_poly_set_coeff_acb(y, 1, acb_mat_entry(m, 0, 1)); + + for (i = 0; i <= j; i++) + { + acb_poly_get_coeff_acb(a, f, i); + acb_poly_pow_ui(t, x, i, prec); + acb_poly_pow_ui(u, y, j - i, prec); + acb_poly_mul(t, t, u, prec); + acb_poly_scalar_mul(t, t, a, prec); + acb_poly_add(aux, aux, t, prec); + } + acb_mat_det(a, m, prec); + acb_pow_si(a, a, k, prec); + acb_poly_scalar_mul(res, aux, a, prec); + + acb_poly_clear(x); + acb_poly_clear(y); + acb_poly_clear(aux); + acb_poly_clear(t); + acb_poly_clear(u); + acb_clear(a); +} diff --git a/src/acb_theta/g2_jet_naive_1.c b/src/acb_theta/g2_jet_naive_1.c new file mode 100644 index 0000000000..6c1802ebf8 --- /dev/null +++ b/src/acb_theta/g2_jet_naive_1.c @@ -0,0 +1,251 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb_mat.h" +#include "acb_theta.h" + +#define ACB_THETA_G2_JET_NAIVE_1_THRESHOLD 100 + +static void +worker(acb_ptr dth, acb_srcptr v1, acb_srcptr v2, const slong * precs, slong len, + const acb_t cofactor, const slong * coords, slong ord, slong g, slong prec, slong fullprec) +{ + slong n = 1 << g; + acb_ptr v3, aux, sums_1, sums_2, diffs; + acb_t x; + slong a0, a1, b; + slong * dots; + slong i, ind0, ind1; + + v3 = _acb_vec_init(len); + aux = _acb_vec_init(2 * 3 * n); + sums_1 = _acb_vec_init(4); + sums_2 = _acb_vec_init(4); + diffs = _acb_vec_init(8); + dots = flint_malloc(n * sizeof(slong)); + acb_init(x); + + /* Precompute a0, a1, dots and multiplications */ + a0 = acb_theta_char_get_a(coords, g); + a1 = a0 ^ (1 << (g - 1)); + for (b = 0; b < n; b++) + { + dots[b] = acb_theta_char_dot_slong(b, coords, g); + } + + if (len > ACB_THETA_G2_JET_NAIVE_1_THRESHOLD) + { + /* Store multiplications in v3 */ + for (i = 0; i < len; i++) + { + acb_mul(&v3[i], &v1[i], &v2[i], precs[i]); + } + /* Main loop */ + for (i = 0; i < len; i++) + { + for (b = 0; b < n; b++) + { + acb_mul_i_pow_si(x, &v3[i], (dots[b] + i * (b >> (g - 1))) % 4); + ind0 = 3 * n * (i % 2) + 3 * b; + if ((dots[b] + i * (b >> (g - 1))) % 2 == 0) + { + acb_add(&aux[ind0], &aux[ind0], x, prec); + } + else + { + acb_addmul_si(&aux[ind0 + 1], x, coords[0] + i, prec); + acb_addmul_si(&aux[ind0 + 2], x, coords[1], prec); + } + } + } + } + else + { + /* Compute dot products and sum them appropriately */ + for (i = 0; i < len; i++) + { + acb_mul_si(&v3[i], &v2[i], coords[0] + i, precs[i]); + } + for (i = 0; i < 4; i++) + { + acb_dot(&sums_1[i], NULL, 0, v1 + i, 4, v2 + i, 4, (len + 3 - i) / 4, prec); + acb_dot(&sums_2[i], NULL, 0, v1 + i, 4, v3 + i, 4, (len + 3 - i) / 4, prec); + } + acb_add(&diffs[0], &sums_1[0], &sums_1[2], prec); + acb_add(&diffs[1], &sums_1[1], &sums_1[3], prec); + acb_sub(&diffs[2], &sums_1[0], &sums_1[2], prec); + acb_sub(&diffs[3], &sums_1[1], &sums_1[3], prec); + acb_add(&diffs[4], &sums_2[0], &sums_2[2], prec); + acb_add(&diffs[5], &sums_2[1], &sums_2[3], prec); + acb_sub(&diffs[6], &sums_2[0], &sums_2[2], prec); + acb_sub(&diffs[7], &sums_2[1], &sums_2[3], prec); + + /* Loop over b */ + for (b = 0; b < n; b++) + { + ind0 = 3 * b; + ind1 = 3 * (n + b); + if ((b >> (g - 1)) == 0) + { + if (dots[b] % 2 == 0) + { + /* All even */ + acb_mul_i_pow_si(x, &diffs[0], dots[b]); + acb_add(&aux[ind0], &aux[ind0], x, prec); + acb_mul_i_pow_si(x, &diffs[1], dots[b]); + acb_add(&aux[ind1], &aux[ind1], x, prec); + } + else + { + /* All odd; use v3 for derivative wrt z1 */ + acb_mul_i_pow_si(x, &diffs[4], dots[b]); + acb_add(&aux[ind0 + 1], &aux[ind0 + 1], x, prec); + acb_mul_i_pow_si(x, &diffs[0], dots[b]); + acb_add(&aux[ind0 + 2], &aux[ind0 + 2], x, prec); + acb_mul_i_pow_si(x, &diffs[5], dots[b]); + acb_add(&aux[ind1 + 1], &aux[ind1 + 1], x, prec); + acb_mul_i_pow_si(x, &diffs[1], dots[b]); + acb_add(&aux[ind1 + 2], &aux[ind1 + 2], x, prec); + } + } + else + { + /* Alternating, with different signs for a0 and a1 */ + if (dots[b] % 2 == 0) + { + /* a0 even, a1 odd */ + acb_mul_i_pow_si(x, &diffs[2], dots[b]); + acb_add(&aux[ind0], &aux[ind0], x, prec); + acb_mul_i_pow_si(x, &diffs[7], dots[b] + 1); + acb_add(&aux[ind1 + 1], &aux[ind1 + 1], x, prec); + acb_mul_i_pow_si(x, &diffs[3], dots[b] + 1); + acb_add(&aux[ind1 + 2], &aux[ind1 + 2], x, prec); + } + else + { + /* a0 odd, a1 even */ + acb_mul_i_pow_si(x, &diffs[3], dots[b] + 1); + acb_add(&aux[ind1], &aux[ind1], x, prec); + acb_mul_i_pow_si(x, &diffs[6], dots[b]); + acb_add(&aux[ind0 + 1], &aux[ind0 + 1], x, prec); + acb_mul_i_pow_si(x, &diffs[2], dots[b]); + acb_add(&aux[ind0 + 2], &aux[ind0 + 2], x, prec); + } + } + } + /* Multiply d/dz2 entries by coords[1] */ + for (i = 0; i < 2 * n; i++) + { + acb_mul_si(&aux[3 * i + 2], &aux[3 * i + 2], coords[1], prec); + } + } + + /* Multiply vector by cofactor and add to dth */ + _acb_vec_scalar_mul(aux, aux, 2 * 3 * n, cofactor, prec); + for (b = 0; b < n; b++) + { + _acb_vec_add(&dth[3 * (n * a0 + b)], &dth[3 * (n * a0 + b)], + &aux[3 * b], 3, fullprec); + _acb_vec_add(&dth[3 * (n * a1 + b)], &dth[3 * (n * a1 + b)], + &aux[3 * (n + b)], 3, fullprec); + } + + _acb_vec_clear(v3, len); + _acb_vec_clear(aux, 2 * 3 * n); + _acb_vec_clear(sums_1, 4); + _acb_vec_clear(sums_2, 4); + _acb_vec_clear(diffs, 8); + flint_free(dots); + acb_clear(x); +} + + +void +acb_theta_g2_jet_naive_1(acb_ptr dth, const acb_mat_t tau, slong prec) +{ + slong g = 2; + slong n2 = 1 << (2 * g); + slong ord = 1; + acb_theta_eld_t E; + acb_mat_t new_tau; + arb_mat_t C; + arf_t R2, eps; + acb_ptr z; + arb_ptr v, a; + acb_t c; + arb_t u; + slong k; + int b; + + acb_theta_eld_init(E, g, g); + acb_mat_init(new_tau, g, g); + arb_mat_init(C, g, g); + arf_init(R2); + arf_init(eps); + z = _acb_vec_init(g); + v = _arb_vec_init(g); + a = _arb_vec_init(g); + acb_init(c); + arb_init(u); + + acb_mat_scalar_mul_2exp_si(new_tau, tau, -2); + acb_siegel_cho(C, new_tau, prec); + + acb_theta_naive_reduce(v, z, a, c, u, z, 1, new_tau, prec); + acb_theta_jet_naive_radius(R2, eps, C, v, ord, prec); + b = acb_theta_eld_set(E, C, R2, v); + + if (b) + { + acb_theta_naive_worker(dth, 3 * n2, z, 1, new_tau, E, ord, prec, worker); + + arb_mul_arf(u, u, eps, prec); + for (k = 0; k < 3 * n2; k++) + { + acb_add_error_arb(&dth[k], u); + } + + _arb_vec_scalar_mul_2exp_si(a, a, 2, 1); + _arb_vec_neg(a, a, 2); + for (k = 0; k < n2; k++) + { + acb_addmul_arb(&dth[3 * k + 1], &dth[3 * k], &a[0], prec); + acb_addmul_arb(&dth[3 * k + 2], &dth[3 * k], &a[1], prec); + } + + acb_const_pi(c, prec); + acb_mul_onei(c, c); + for (k = 0; k < n2; k++) + { + acb_mul(&dth[3 * k + 1], &dth[3 * k + 1], c, prec); + acb_mul(&dth[3 * k + 2], &dth[3 * k + 2], c, prec); + } + + } + else + { + for (k = 0; k < 3 * n2; k++) + { + acb_indeterminate(&dth[k]); + } + } + + acb_theta_eld_clear(E); + acb_mat_clear(new_tau); + arb_mat_clear(C); + arf_clear(R2); + arf_clear(eps); + _acb_vec_clear(z, g); + _arb_vec_clear(v, g); + _arb_vec_clear(a, g); + acb_clear(c); + arb_clear(u); +} diff --git a/src/acb_theta/g2_psi4.c b/src/acb_theta/g2_psi4.c new file mode 100644 index 0000000000..363bb17dd8 --- /dev/null +++ b/src/acb_theta/g2_psi4.c @@ -0,0 +1,37 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb.h" +#include "acb_theta.h" + +void +acb_theta_g2_psi4(acb_t res, acb_srcptr th2, slong prec) +{ + slong g = 2; + ulong ab; + acb_t s, t; + + acb_init(s); + acb_init(t); + + for (ab = 0; ab < (1 << (2 * g)); ab++) + { + if (acb_theta_char_is_even(ab, g)) + { + acb_pow_ui(t, &th2[ab], 4, prec); + acb_add(s, s, t, prec); + } + } + acb_mul_2exp_si(res, s, -2); + + acb_clear(s); + acb_clear(t); +} diff --git a/src/acb_theta/g2_psi6.c b/src/acb_theta/g2_psi6.c new file mode 100644 index 0000000000..6025c7538e --- /dev/null +++ b/src/acb_theta/g2_psi6.c @@ -0,0 +1,82 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb.h" +#include "acb_theta.h" + +static void +g2_psi6_bits(int * b1, int * b2, int * b3, int * b4, ulong b) +{ + *b4 = b % 2; + b = b >> 1; + *b3 = b % 2; + b = b >> 1; + *b2 = b % 2; + b = b >> 1; + *b1 = b % 2; +} + +static slong +g2_psi6_sgn(ulong b, ulong c, ulong d) +{ + slong sgn; + int b1, b2, b3, b4, c1, c2, c3, c4, d1, d2, d3, d4; + + g2_psi6_bits(&b1, &b2, &b3, &b4, b); + g2_psi6_bits(&c1, &c2, &c3, &c4, c); + g2_psi6_bits(&d1, &d2, &d3, &d4, d); + + sgn = b1 + b2 + c1 + c2 + d1 + d2 + b1 * c1 + b2 * c2 + b4 * c2 + b1 * c3 + - b2 * c4 + b1 * d1 - b3 * d1 + c1 * d1 + b2 * d2 + c2 * d2 + c4 * d2 + + c1 * d3 - b2 * b3 * c1 - b2 * b4 * c2 - b1 * b2 * c3 - b2 * b3 * d1 + - b3 * c1 * d1 - b1 * c3 * d1 - b2 * c3 * d1 - b2 * b4 * d2 + - b4 * c2 * d2 - b1 * b2 * d3 - b1 * c1 * d3 - b2 * c1 * d3; + sgn = (sgn % 2 == 1 ? -1 : 1); + + return sgn; +} + + +void +acb_theta_g2_psi6(acb_t res, acb_srcptr th2, slong prec) +{ + slong g = 2; + ulong ch1, ch2, ch3; + ulong n = 1 << (2 * g); + slong sgn; + acb_t s, t; + + acb_init(s); + acb_init(t); + + for (ch1 = 0; ch1 < n; ch1++) + { + for (ch2 = ch1 + 1; ch2 < n; ch2++) + { + for (ch3 = ch2 + 1; ch3 < n; ch3++) + { + if (acb_theta_char_is_syzygous(ch1, ch2, ch3, g)) + { + sgn = g2_psi6_sgn(ch1, ch2, ch3); + acb_mul(t, &th2[ch1], &th2[ch2], prec); + acb_mul(t, t, &th2[ch3], prec); + acb_sqr(t, t, prec); + acb_mul_si(t, t, sgn, prec); + acb_add(s, s, t, prec); + } + } + } + } + acb_mul_2exp_si(res, s, -2); + + acb_clear(s); + acb_clear(t); +} diff --git a/src/acb_theta/g2_sextic.c b/src/acb_theta/g2_sextic.c new file mode 100644 index 0000000000..5a3f1736be --- /dev/null +++ b/src/acb_theta/g2_sextic.c @@ -0,0 +1,23 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb.h" +#include "acb_theta.h" + +void +acb_theta_g2_sextic(acb_poly_t res, const acb_mat_t tau, slong prec) +{ + acb_t chi5; + + acb_init(chi5); + acb_theta_g2_sextic_chi5(res, chi5, tau, prec); + acb_clear(chi5); +} diff --git a/src/acb_theta/g2_sextic_chi5.c b/src/acb_theta/g2_sextic_chi5.c new file mode 100644 index 0000000000..64391f4293 --- /dev/null +++ b/src/acb_theta/g2_sextic_chi5.c @@ -0,0 +1,77 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb_poly.h" +#include "acb_mat.h" +#include "acb_theta.h" + +#define ACB_THETA_G2_JET_NAIVE_THRESHOLD 10000 + +void +acb_theta_g2_sextic_chi5(acb_poly_t res, acb_t chi5, const acb_mat_t tau, slong prec) +{ + slong g = 2; + slong n2 = 1 << (2 * g); + slong nb = acb_theta_jet_nb(1, g); + fmpz_mat_t mat; + acb_mat_t w, c, cinv; + acb_ptr z, dth, th; + acb_t det; + slong k; + + fmpz_mat_init(mat, 2 * g, 2 * g); + acb_mat_init(w, g, g); + acb_mat_init(c, g, g); + acb_mat_init(cinv, g, g); + dth = _acb_vec_init(n2 * nb); + th = _acb_vec_init(n2); + z = _acb_vec_init(g); + acb_init(det); + + acb_siegel_reduce(mat, tau, prec); + acb_siegel_transform_cocycle_inv(w, c, cinv, mat, tau, prec); + + if (prec < ACB_THETA_G2_JET_NAIVE_THRESHOLD) + { + acb_theta_g2_jet_naive_1(dth, w, prec); + } + else + { + acb_theta_jet_ql_all(dth, z, w, 1, prec); + } + + for (k = 0; k < n2; k++) + { + acb_set(&th[k], &dth[k * nb]); + } + acb_theta_g2_chi3_6(res, dth, prec); + acb_theta_g2_chi5(chi5, th, prec); + acb_poly_scalar_div(res, res, chi5, prec); + + acb_theta_g2_detk_symj(res, cinv, res, -2, 6, prec); + acb_mat_det(det, cinv, prec); + acb_pow_ui(det, det, 5, prec); + + if (acb_theta_g2_character(mat) == 1) + { + acb_neg(det, det); + } + acb_mul(chi5, chi5, det, prec); + + fmpz_mat_clear(mat); + acb_mat_clear(w); + acb_mat_clear(c); + acb_mat_clear(cinv); + _acb_vec_clear(dth, n2 * nb); + _acb_vec_clear(th, n2); + _acb_vec_clear(z, g); + acb_clear(det); +} diff --git a/src/acb_theta/g2_transvectant.c b/src/acb_theta/g2_transvectant.c new file mode 100644 index 0000000000..a6584add50 --- /dev/null +++ b/src/acb_theta/g2_transvectant.c @@ -0,0 +1,88 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb_poly.h" +#include "acb_theta.h" + +void +acb_theta_g2_transvectant(acb_poly_t res, const acb_poly_t g, const acb_poly_t h, + slong m, slong n, slong k, slong prec) +{ + acb_poly_t aux, s, t; + acb_t x; + fmpz_t num, f; + slong i, j; + + acb_poly_init(aux); + acb_poly_init(s); + acb_poly_init(t); + acb_init(x); + fmpz_init(num); + fmpz_init(f); + + for (j = 0; j <= k; j++) + { + /* Set s to d^k g / dx^{k-j} dy^j; g was of degree m */ + acb_poly_zero(s); + for (i = 0; i <= m - k; i++) + { + fmpz_fac_ui(num, i + (k - j)); + fmpz_fac_ui(f, (m - k - i) + j); + fmpz_mul(num, num, f); + fmpz_bin_uiui(f, m - k, i); + fmpz_mul(num, num, f); + + acb_poly_get_coeff_acb(x, g, i + (k - j)); + acb_mul_fmpz(x, x, num, prec); + acb_poly_set_coeff_acb(s, i, x); + } + + /* Set t to d^k h / dx^j dy^{k-j}; h was of degree n */ + acb_poly_zero(t); + for (i = 0; i <= n - k; i++) + { + fmpz_fac_ui(num, i + j); + fmpz_fac_ui(f, (n - k - i) + (k - j)); + fmpz_mul(num, num, f); + fmpz_bin_uiui(f, n - k, i); + fmpz_mul(num, num, f); + + acb_poly_get_coeff_acb(x, h, i + j); + acb_mul_fmpz(x, x, num, prec); + acb_poly_set_coeff_acb(t, i, x); + } + + acb_poly_mul(s, s, t, prec); + fmpz_bin_uiui(f, k, j); + if ((k - j) % 2 == 1) + { + fmpz_neg(f, f); + } + acb_set_fmpz(x, f); + acb_poly_scalar_mul(s, s, x, prec); + acb_poly_add(aux, aux, s, prec); + } + + fmpz_fac_ui(num, m); + fmpz_fac_ui(f, n); + fmpz_mul(num, num, f); + + acb_one(x); + acb_div_fmpz(x, x, num, prec); + acb_poly_scalar_mul(res, aux, x, prec); + + acb_poly_clear(aux); + acb_poly_clear(s); + acb_poly_clear(t); + acb_clear(x); + fmpz_clear(num); + fmpz_clear(f); +} diff --git a/src/acb_theta/g2_transvectant_lead.c b/src/acb_theta/g2_transvectant_lead.c new file mode 100644 index 0000000000..8881ccde5f --- /dev/null +++ b/src/acb_theta/g2_transvectant_lead.c @@ -0,0 +1,57 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb_poly.h" +#include "acb_theta.h" + +void +acb_theta_g2_transvectant_lead(acb_t res, const acb_poly_t g, const acb_poly_t h, + slong m, slong n, slong k, slong prec) +{ + acb_ptr s, t; + fmpz_t num, f; + slong j; + + s = _acb_vec_init(k + 1); + t = _acb_vec_init(k + 1); + fmpz_init(num); + fmpz_init(f); + + /* Set i = m - k (resp. n - k) in g2_transvectant and use acb_dot */ + for (j = 0; j <= k; j++) + { + acb_poly_get_coeff_acb(&s[j], g, m - j); + acb_poly_get_coeff_acb(&t[j], h, n - k + j); + /* Put all factorials in s */ + fmpz_fac_ui(num, m - j); + fmpz_fac_ui(f, n - k + j); + fmpz_mul(num, num, f); + if ((k - j) % 2 == 1) + { + fmpz_neg(num, num); + } + acb_mul_fmpz(&s[j], &s[j], num, prec); + } + acb_dot(res, NULL, 0, s, 1, t, 1, k + 1, prec); + + fmpz_fac_ui(num, k); + acb_set_fmpz(t, num); + fmpz_fac_ui(num, m); + fmpz_fac_ui(f, n); + fmpz_mul(num, num, f); + acb_div_fmpz(t, t, num, prec); + acb_mul(res, res, t, prec); + + _acb_vec_clear(s, k + 1); + _acb_vec_clear(t, k + 1); + fmpz_clear(num); + fmpz_clear(f); +} diff --git a/src/acb_theta/jet_all.c b/src/acb_theta/jet_all.c new file mode 100644 index 0000000000..b6a9c3b5f0 --- /dev/null +++ b/src/acb_theta/jet_all.c @@ -0,0 +1,175 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb_poly.h" +#include "acb_mat.h" +#include "acb_theta.h" + +/* Compute jet of exp (z^T N z) */ +static void +acb_theta_jet_exp_qf(acb_ptr res, acb_srcptr z, const acb_mat_t N, slong ord, slong prec) +{ + slong g = acb_mat_nrows(N); + slong nb = acb_theta_jet_nb(ord, g); + acb_mat_t tp; + acb_poly_t pol; + acb_ptr aux; + acb_ptr y; + acb_t c; + slong * tup; + slong j, k, l, i; + + acb_mat_init(tp, g, g); + acb_poly_init(pol); + aux = _acb_vec_init(nb); + y = _acb_vec_init(g); + acb_init(c); + tup = flint_malloc(g * sizeof(slong)); + + /* exp((z+h)^T N (z+h)) = exp(z^T N z) exp(z^T (N+N^T) h) exp(h^T N h) */ + _acb_vec_zero(res, nb); + acb_mat_vector_mul_col(y, N, z, prec); + acb_dot(&res[0], NULL, 0, z, 1, y, 1, g, prec); + acb_exp(&res[0], &res[0], prec); + + acb_mat_transpose(tp, N); + acb_mat_add(tp, tp, N, prec); + acb_mat_vector_mul_row(y, z, tp, prec); + for (j = 0; j < g; j++) + { + _acb_vec_zero(aux, nb); + acb_poly_zero(pol); + acb_poly_set_coeff_acb(pol, 1, &y[j]); + acb_poly_exp_series(pol, pol, ord + 1, prec); + for (l = 0; l <= ord; l++) + { + for (i = 0; i < g; i++) + { + tup[i] = 0; + } + tup[j] = l; + acb_poly_get_coeff_acb(&aux[acb_theta_jet_index(tup, g)], pol, l); + } + acb_theta_jet_mul(res, res, aux, ord, g, prec); + } + + for (j = 0; j < g; j++) + { + for (k = j; k < g; k++) + { + _acb_vec_zero(aux, nb); + acb_poly_zero(pol); + acb_add(c, acb_mat_entry(N, k, j), acb_mat_entry(N, j, k), prec); + if (j == k) + { + acb_mul_2exp_si(c, c, -1); + } + acb_poly_set_coeff_acb(pol, 1, c); + acb_poly_exp_series(pol, pol, (ord / 2) + 1, prec); + for (l = 0; l <= (ord / 2); l++) + { + for (i = 0; i < g; i++) + { + tup[i] = 0; + } + tup[j] += l; + tup[k] += l; + acb_poly_get_coeff_acb(&aux[acb_theta_jet_index(tup, g)], pol, l); + } + acb_theta_jet_mul(res, res, aux, ord, g, prec); + } + } + + acb_mat_clear(tp); + acb_poly_clear(pol); + _acb_vec_clear(aux, nb); + _acb_vec_clear(y, g); + acb_clear(c); + flint_free(tup); +} + +void +acb_theta_jet_all(acb_ptr dth, acb_srcptr z, const acb_mat_t tau, slong ord, slong prec) +{ + slong g = acb_mat_nrows(tau); + slong n2 = 1 << (2 * g); + slong nb = acb_theta_jet_nb(ord, g); + fmpz_mat_t mat, gamma; + acb_mat_t w, c, cinv, N; + acb_ptr aux, x, units; + acb_t s, t; + ulong ab, image_ab; + slong kappa, e; + + fmpz_mat_init(mat, 2 * g, 2 * g); + acb_mat_init(w, g, g); + acb_mat_init(c, g, g); + acb_mat_init(cinv, g, g); + acb_mat_init(N, g, g); + x = _acb_vec_init(g); + aux = _acb_vec_init(n2 * nb); + units = _acb_vec_init(8); + acb_init(s); + acb_init(t); + + acb_siegel_reduce(mat, tau, prec); + acb_siegel_transform_cocycle_inv(w, c, cinv, mat, tau, prec); + _acb_vec_unit_roots(units, 8, 8, prec); + + if (acb_siegel_is_reduced(w, -10, prec)) + { + sp2gz_inv(mat, mat); + acb_mat_transpose(cinv, cinv); + acb_mat_vector_mul_col(x, cinv, z, prec); + + acb_theta_jet_ql_all(aux, x, w, ord, prec); + + kappa = acb_theta_transform_kappa(s, mat, w, prec); + for (ab = 0; ab < n2; ab++) + { + image_ab = acb_theta_transform_char(&e, mat, ab); + acb_mul(t, s, &units[(kappa + e) % 8], prec); + _acb_vec_scalar_mul(dth + ab * nb, aux + image_ab * nb, nb, t, prec); + acb_theta_jet_compose(dth + ab * nb, dth + ab * nb, cinv, ord, prec); + } + + fmpz_mat_window_init(gamma, mat, g, 0, 2 * g, g); + acb_mat_set_fmpz_mat(N, gamma); + acb_mat_mul(N, N, cinv, prec); + acb_const_pi(t, prec); + acb_mul_onei(t, t); + acb_mat_scalar_mul_acb(N, N, t, prec); + fmpz_mat_window_clear(gamma); + + acb_theta_jet_exp_qf(aux, z, N, ord, prec); + + for (ab = 0; ab < n2; ab++) + { + acb_theta_jet_mul(dth + ab * nb, dth + ab * nb, aux, ord, g, prec); + } + } + else + { + _acb_vec_indeterminate(dth, n2 * nb); + } + + + fmpz_mat_clear(mat); + acb_mat_clear(w); + acb_mat_clear(c); + acb_mat_clear(cinv); + acb_mat_clear(N); + _acb_vec_clear(x, g); + _acb_vec_clear(aux, n2 * nb); + _acb_vec_clear(units, 8); + acb_clear(s); + acb_clear(t); +} diff --git a/src/acb_theta/jet_compose.c b/src/acb_theta/jet_compose.c new file mode 100644 index 0000000000..975ad1ba59 --- /dev/null +++ b/src/acb_theta/jet_compose.c @@ -0,0 +1,91 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "ulong_extras.h" +#include "acb_mat.h" +#include "acb_theta.h" + +void +acb_theta_jet_compose(acb_ptr res, acb_srcptr v, const acb_mat_t N, + slong ord, slong prec) +{ + slong g = acb_mat_nrows(N); + slong nb = acb_theta_jet_nb(ord, g); + acb_ptr aux; + acb_t x; + fmpz_t m, p; + slong * tups; + slong * term; + slong n, k, j, i, l, t; + + tups = flint_malloc(nb * g * sizeof(slong)); + term = flint_malloc(g * sizeof(slong)); + aux = _acb_vec_init(nb); + acb_init(x); + fmpz_init(m); + fmpz_init(p); + + acb_theta_jet_tuples(tups, ord, g); + for (k = 0; k < nb; k++) + { + n = acb_theta_jet_total_order(tups + k * g, g); + for (j = 0; j < n_pow(g, n); j++) + { + for (i = 0; i < g; i++) + { + term[i] = 0; + } + for (i = 0; i < n; i++) + { + term[(j / n_pow(g, i)) % g]++; + } + + /* multiply by factorials */ + fmpz_one(p); + for (i = 0; i < g; i++) + { + fmpz_fac_ui(m, term[i]); + fmpz_mul(p, p, m); + } + acb_mul_fmpz(x, &v[acb_theta_jet_index(term, g)], p, prec); + + /* view tup as a collection of n indices, enumerate them */ + i = 0; + for (l = 0; l < g; l++) + { + for (t = 0; t < tups[k * g + l]; t++) + { + acb_mul(x, x, acb_mat_entry(N, (j / n_pow(g, i) % g), l), prec); + i++; + } + } + acb_add(&aux[k], &aux[k], x, prec); + } + + fmpz_one(p); + for (i = 0; i < g; i++) + { + fmpz_fac_ui(m, tups[k * g + i]); + fmpz_mul(p, p, m); + } + acb_div_fmpz(&aux[k], &aux[k], p, prec); + } + + _acb_vec_set(res, aux, nb); + + flint_free(tups); + flint_free(term); + _acb_vec_clear(aux, nb); + acb_clear(x); + fmpz_clear(m); + fmpz_clear(p); +} + diff --git a/src/acb_theta/jet_error_bounds.c b/src/acb_theta/jet_error_bounds.c new file mode 100644 index 0000000000..c974dc6754 --- /dev/null +++ b/src/acb_theta/jet_error_bounds.c @@ -0,0 +1,115 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "arb_mat.h" +#include "acb_mat.h" +#include "acb_theta.h" + +void +acb_theta_jet_error_bounds(arb_ptr err, acb_srcptr z, const acb_mat_t tau, + acb_srcptr dth, slong ord, slong prec) +{ + slong g = acb_mat_nrows(tau); + arb_ptr abs_der; + arb_mat_t tau_err; + arb_ptr z_err; + arb_t e, f; + slong nb = acb_theta_jet_nb(ord, g); + slong nb_dth = acb_theta_jet_nb(ord + 2, g); + slong * tups; + slong * new_tups; + slong j, l, m, i; + + abs_der = _arb_vec_init(nb_dth); + arb_mat_init(tau_err, g, g); + z_err = _arb_vec_init(g); + arb_init(e); + arb_init(f); + tups = flint_malloc(nb * g * sizeof(slong)); + new_tups = flint_malloc(g * sizeof(slong)); + + /* Get input errors on z, tau */ + for (l = 0; l < g; l++) + { + for (m = l; m < g; m++) + { + acb_get_rad_ubound_arf(arb_midref(e), acb_mat_entry(tau, l, m), prec); + arb_set(arb_mat_entry(tau_err, l, m), e); + } + acb_get_rad_ubound_arf(arb_midref(e), &z[l], prec); + arb_set(&z_err[l], e); + } + + /* We need order ord + 2 to use the heat equation. */ + for (j = 0; j < nb_dth; j++) + { + acb_get_abs_ubound_arf(arb_midref(&abs_der[j]), &dth[j], prec); + } + + /* Loop over tuples to compute the correct bounds */ + acb_theta_jet_tuples(tups, ord, g); + for (j = 0; j < nb; j++) + { + arb_zero(&err[j]); + /* Add error corresponding to entries of tau */ + for (l = 0; l < g; l++) + { + for (m = l; m < g; m++) + { + /* Heat equation: d/dzl d/dzm = 2pi i (1 + delta) d/dtaulm */ + for (i = 0; i < g; i++) + { + new_tups[i] = tups[j * g + i]; + } + new_tups[l] += 1; + new_tups[m] += 1; + i = acb_theta_jet_index(new_tups, g); + + arb_mul(e, arb_mat_entry(tau_err, l, m), &abs_der[i], prec); + arb_const_pi(f, prec); + if (l == m) + { + arb_mul_2exp_si(f, f, 2); + arb_mul_si(e, e, new_tups[l] * (new_tups[l] - 1), prec); + } + else + { + arb_mul_2exp_si(f, f, 1); + arb_mul_si(e, e, new_tups[l] * new_tups[m], prec); + } + arb_div(e, e, f, prec); + arb_add(&err[j], &err[j], e, prec); + } + } + /* Add error corresponding to entries of z */ + for (l = 0; l < g; l++) + { + for (i = 0; i < g; i++) + { + new_tups[i] = tups[j * g + i]; + } + new_tups[l] += 1; + i = acb_theta_jet_index(new_tups, g); + + arb_mul(e, &z_err[l], &abs_der[i], prec); + arb_mul_si(e, e, new_tups[l], prec); + arb_add(&err[j], &err[j], e, prec); + } + } + + _arb_vec_clear(abs_der, nb_dth); + arb_mat_clear(tau_err); + _arb_vec_clear(z_err, g); + arb_clear(e); + arb_clear(f); + flint_free(tups); + flint_free(new_tups); +} diff --git a/src/acb_theta/jet_exp_pi_i.c b/src/acb_theta/jet_exp_pi_i.c new file mode 100644 index 0000000000..d211328223 --- /dev/null +++ b/src/acb_theta/jet_exp_pi_i.c @@ -0,0 +1,59 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb.h" +#include "acb_theta.h" + +void +acb_theta_jet_exp_pi_i(acb_ptr res, arb_srcptr a, slong ord, slong g, slong prec) +{ + slong nb = acb_theta_jet_nb(ord, g); + slong * tups; + acb_t c; + arb_t t; + fmpz_t den, m; + slong k, l; + + tups = flint_malloc(nb * g * sizeof(slong)); + acb_init(c); + arb_init(t); + fmpz_init(den); + fmpz_init(m); + + acb_one(&res[0]); + acb_theta_jet_tuples(tups, ord, g); + + for (k = 1; k < nb; k++) + { + acb_one(&res[k]); + fmpz_one(den); + for (l = 0; l < g; l++) + { + arb_pow_ui(t, &a[l], tups[k * g + l], prec); + acb_mul_arb(&res[k], &res[k], t, prec); + + fmpz_fac_ui(m, tups[k * g + l]); + fmpz_mul(den, den, m); + } + + acb_const_pi(c, prec); + acb_mul_onei(c, c); + acb_pow_ui(c, c, acb_theta_jet_total_order(tups + k * g, g), prec); + acb_mul(&res[k], &res[k], c, prec); + acb_div_fmpz(&res[k], &res[k], den, prec); + } + + flint_free(tups); + acb_clear(c); + arb_clear(t); + fmpz_clear(den); + fmpz_clear(m); +} diff --git a/src/acb_theta/jet_index.c b/src/acb_theta/jet_index.c new file mode 100644 index 0000000000..669743fc92 --- /dev/null +++ b/src/acb_theta/jet_index.c @@ -0,0 +1,38 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb_theta.h" + +slong +acb_theta_jet_index(const slong * tup, slong g) +{ + slong ord, res, k; + slong j; + + /* Get total derivation order */ + ord = acb_theta_jet_total_order(tup, g); + if (ord == 0 || g == 1) + { + return ord; + } + + /* Count tuples with smaller total order */ + res = acb_theta_jet_nb(ord - 1, g); + + for (j = 0; j < g - 1; j++) + { + k = tup[j]; + res += acb_theta_jet_nb(ord - k - 1, g - 1 - j); + ord -= k; + } + + return res; +} diff --git a/src/acb_theta/jet_mul.c b/src/acb_theta/jet_mul.c new file mode 100644 index 0000000000..3e7d344251 --- /dev/null +++ b/src/acb_theta/jet_mul.c @@ -0,0 +1,65 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb.h" +#include "acb_theta.h" + +static int +acb_theta_jet_le(const slong * tup1, const slong * tup2, slong g) +{ + slong k; + + for (k = 0; k < g; k++) + { + if (tup1[k] > tup2[k]) + { + return 0; + } + } + return 1; +} + +void +acb_theta_jet_mul(acb_ptr res, acb_srcptr v1, acb_srcptr v2, slong ord, slong g, slong prec) +{ + slong nb = acb_theta_jet_nb(ord, g); + acb_ptr aux; + slong * tups; + slong * diff; + slong j, k, l; + + aux = _acb_vec_init(nb); + tups = flint_malloc(nb * g * sizeof(slong)); + diff = flint_malloc(g * sizeof(slong)); + + acb_theta_jet_tuples(tups, ord, g); + for (j = 0; j < nb; j++) + { + for (k = 0; k < nb; k++) + { + if (!acb_theta_jet_le(tups + k * g, tups + j * g, g)) + { + continue; + } + for (l = 0; l < g; l++) + { + diff[l] = tups[j * g + l] - tups[k * g + l]; + } + acb_addmul(&aux[j], &v1[k], &v2[acb_theta_jet_index(diff, g)], prec); + } + } + + _acb_vec_set(res, aux, nb); + + _acb_vec_clear(aux, nb); + flint_free(tups); + flint_free(diff); +} diff --git a/src/acb_theta/jet_naive_00.c b/src/acb_theta/jet_naive_00.c new file mode 100644 index 0000000000..4a75a3e20b --- /dev/null +++ b/src/acb_theta/jet_naive_00.c @@ -0,0 +1,189 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb_mat.h" +#include "acb_modular.h" +#include "acb_theta.h" + +static void +worker(acb_ptr dth, acb_srcptr v1, acb_srcptr v2, const slong * precs, slong len, + const acb_t cofactor, const slong * coords, slong ord, slong g, slong prec, slong fullprec) +{ + slong nb = acb_theta_jet_nb(ord, g); + slong * tups; + acb_ptr v3, aux; + acb_t x; + fmpz_t num, t; + slong j, i; + + tups = flint_malloc(g * nb * sizeof(slong)); + v3 = _acb_vec_init(len); + aux = _acb_vec_init(nb); + acb_init(x); + fmpz_init(num); + fmpz_init(t); + + /* Compute products in v3 */ + for (i = 0; i < len; i++) + { + acb_mul(&v3[i], &v1[i], &v2[i], precs[i]); + } + + acb_theta_jet_tuples(tups, ord, g); + for (j = 0; j < nb; j++) + { + fmpz_one(num); + for (i = 1; i < g; i++) + { + fmpz_set_si(t, coords[i]); + fmpz_pow_ui(t, t, tups[j * g + i]); + fmpz_mul(num, num, t); + } + + /* Loop over lattice points */ + for (i = 0; i < len; i++) + { + fmpz_set_si(t, coords[0] + i); + fmpz_pow_ui(t, t, tups[j * g]); + acb_mul_fmpz(x, &v3[i], t, precs[i]); + acb_add(&aux[j], &aux[j], x, prec); + } + + /* Multiply by cofactor * num */ + acb_mul_fmpz(x, cofactor, num, prec); + acb_mul(&aux[j], &aux[j], x, prec); + } + _acb_vec_add(dth, dth, aux, nb, fullprec); + + flint_free(tups); + _acb_vec_clear(v3, len); + _acb_vec_clear(aux, nb); + acb_clear(x); + fmpz_clear(num); + fmpz_clear(t); +} + +static void +acb_theta_jet_naive_00_gen(acb_ptr dth, acb_srcptr z, const acb_mat_t tau, + slong ord, slong prec) +{ + slong g = acb_mat_nrows(tau); + slong nb = acb_theta_jet_nb(ord, g); + slong * tups; + acb_theta_eld_t E; + arb_mat_t C; + arf_t R2, eps; + acb_ptr new_z, aux; + arb_ptr v, a; + acb_t c; + arb_t u; + fmpz_t m, t; + slong j, k; + int b; + + tups = flint_malloc(g * nb * sizeof(slong)); + acb_theta_eld_init(E, g, g); + arb_mat_init(C, g, g); + arf_init(R2); + arf_init(eps); + new_z = _acb_vec_init(g); + aux = _acb_vec_init(nb); + a = _arb_vec_init(g); + v = _arb_vec_init(g); + acb_init(c); + arb_init(u); + fmpz_init(m); + fmpz_init(t); + + acb_siegel_cho(C, tau, prec); + acb_theta_naive_reduce(v, new_z, a, c, u, z, 1, tau, prec); + acb_theta_jet_naive_radius(R2, eps, C, v, ord, prec); + b = acb_theta_eld_set(E, C, R2, v); + + if (b) + { + acb_theta_naive_worker(dth, nb, new_z, 1, tau, E, ord, prec, worker); + + arb_mul_arf(u, u, eps, prec); + for (k = 0; k < nb; k++) + { + acb_mul(&dth[k], &dth[k], c, prec); + acb_add_error_arb(&dth[k], u); + } + + acb_theta_jet_tuples(tups, ord, g); + for (k = 0; k < nb; k++) + { + acb_const_pi(c, prec); + acb_mul_2exp_si(c, c, 1); + acb_mul_onei(c, c); + acb_pow_ui(c, c, acb_theta_jet_total_order(tups + k * g, g), prec); + fmpz_one(m); + for (j = 0; j < g; j++) + { + fmpz_fac_ui(t, tups[k * g + j]); + fmpz_mul(m, m, t); + } + acb_div_fmpz(c, c, m, prec); + acb_mul(&dth[k], &dth[k], c, prec); + } + + _arb_vec_neg(a, a, g); + _arb_vec_scalar_mul_2exp_si(a, a, g, 1); + acb_theta_jet_exp_pi_i(aux, a, ord, g, prec); + acb_theta_jet_mul(dth, dth, aux, ord, g, prec); + } + else + { + for (k = 0; k < nb; k++) + { + acb_indeterminate(&dth[k]); + } + } + + flint_free(tups); + acb_theta_eld_clear(E); + arb_mat_clear(C); + arf_clear(R2); + arf_clear(eps); + _acb_vec_clear(new_z, g); + _acb_vec_clear(aux, nb); + _arb_vec_clear(v, g); + _arb_vec_clear(a, g); + acb_clear(c); + arb_clear(u); + fmpz_clear(m); + fmpz_clear(t); +} + +void +acb_theta_jet_naive_00(acb_ptr dth, acb_srcptr z, const acb_mat_t tau, + slong ord, slong prec) +{ + slong g = acb_mat_nrows(tau); + slong nb = acb_theta_jet_nb(ord, g); + acb_ptr res; + + if (g == 1) + { + res = _acb_vec_init(4 * nb); + + acb_modular_theta_jet(res, res + nb, res + 2 * nb, res + 3 * nb, + z, acb_mat_entry(tau, 0, 0), nb, prec); + _acb_vec_set(dth, res + 2 * nb, nb); + + _acb_vec_clear(res, 4 * nb); + } + else + { + acb_theta_jet_naive_00_gen(dth, z, tau, ord, prec); + } +} diff --git a/src/acb_theta/jet_naive_all.c b/src/acb_theta/jet_naive_all.c new file mode 100644 index 0000000000..895afcfb3a --- /dev/null +++ b/src/acb_theta/jet_naive_all.c @@ -0,0 +1,243 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb_mat.h" +#include "acb_modular.h" +#include "acb_theta.h" + +/* Use a big ellipsoid to avoid complicated formulas for derivatives; this + introduces powers of i in worker */ + +static void +worker(acb_ptr dth, acb_srcptr v1, acb_srcptr v2, const slong * precs, slong len, + const acb_t cofactor, const slong * coords, slong ord, slong g, slong prec, slong fullprec) +{ + slong n = 1 << g; + slong nb = acb_theta_jet_nb(ord, g); + slong * tups; + slong a0, a1; + slong * dots; + acb_ptr v3, aux; + acb_t x, y; + fmpz_t num, t; + slong j, i; + ulong b; + + tups = flint_malloc(g * nb * sizeof(slong)); + dots = flint_malloc(n * sizeof(slong)); + v3 = _acb_vec_init(len); + aux = _acb_vec_init(nb * n * n); + acb_init(x); + acb_init(y); + fmpz_init(num); + fmpz_init(t); + + /* Precompute a0, a1, dots */ + a0 = acb_theta_char_get_a(coords, g); + a1 = a0 ^ (1 << (g - 1)); + for (b = 0; b < n; b++) + { + dots[b] = acb_theta_char_dot_slong(b, coords, g); + } + + /* Compute products in v3 */ + for (i = 0; i < len; i++) + { + acb_mul(&v3[i], &v1[i], &v2[i], precs[i]); + } + + acb_theta_jet_tuples(tups, ord, g); + for (j = 0; j < nb; j++) + { + fmpz_one(num); + for (i = 1; i < g; i++) + { + fmpz_set_si(t, coords[i]); + fmpz_pow_ui(t, t, tups[j * g + i]); + fmpz_mul(num, num, t); + } + + /* Loop over lattice points */ + for (i = 0; i < len; i++) + { + fmpz_set_si(t, coords[0] + i); + fmpz_pow_ui(t, t, tups[j * g]); + acb_mul_fmpz(x, &v3[i], t, precs[i]); + /* Loop over b, adding coefficients in both a0b and a1b */ + for (b = 0; b < n; b++) + { + acb_mul_i_pow_si(y, x, (dots[b] + i * (b >> (g - 1))) % 4); + if (i % 2 == 0) + { + acb_add(&aux[(n * a0 + b) * nb + j], + &aux[(n * a0 + b) * nb + j], y, prec); + } + else + { + acb_add(&aux[(n * a1 + b) * nb + j], + &aux[(n * a1 + b) * nb + j], y, prec); + } + } + } + + /* Multiply by cofactor * num */ + acb_mul_fmpz(x, cofactor, num, prec); + for (b = 0; b < n; b++) + { + acb_mul(&aux[(n * a0 + b) * nb + j], &aux[(n * a0 + b) * nb + j], x, prec); + acb_mul(&aux[(n * a1 + b) * nb + j], &aux[(n * a1 + b) * nb + j], x, prec); + } + } + + _acb_vec_add(dth, dth, aux, nb * n * n, fullprec); + + flint_free(tups); + flint_free(dots); + _acb_vec_clear(v3, len); + _acb_vec_clear(aux, nb * n * n); + acb_clear(x); + acb_clear(y); + fmpz_clear(num); + fmpz_clear(t); +} + +static void +acb_theta_jet_naive_all_gen(acb_ptr dth, acb_srcptr z, const acb_mat_t tau, + slong ord, slong prec) +{ + slong g = acb_mat_nrows(tau); + slong n2 = 1 << (2 * g); + slong nb = acb_theta_jet_nb(ord, g); + slong * tups; + acb_theta_eld_t E; + arb_mat_t C; + arf_t R2, eps; + acb_ptr aux, new_z; + acb_mat_t new_tau; + arb_ptr v, a; + acb_t c; + arb_t u; + fmpz_t m, t; + slong k, j; + int b; + + tups = flint_malloc(g * nb * sizeof(slong)); + acb_theta_eld_init(E, g, g); + arb_mat_init(C, g, g); + arf_init(R2); + arf_init(eps); + aux = _acb_vec_init(n2 * nb); + new_z = _acb_vec_init(g); + acb_mat_init(new_tau, g, g); + v = _arb_vec_init(g); + a = _arb_vec_init(g); + acb_init(c); + arb_init(u); + fmpz_init(m); + fmpz_init(t); + + _acb_vec_scalar_mul_2exp_si(new_z, z, g, -1); + acb_mat_scalar_mul_2exp_si(new_tau, tau, -2); + acb_siegel_cho(C, new_tau, prec); + + acb_theta_naive_reduce(v, new_z, a, c, u, new_z, 1, new_tau, prec); + acb_theta_jet_naive_radius(R2, eps, C, v, ord, prec); + b = acb_theta_eld_set(E, C, R2, v); + + if (b) + { + acb_theta_naive_worker(dth, nb * n2, new_z, 1, new_tau, E, ord, prec, worker); + arb_mul_arf(u, u, eps, prec); + for (k = 0; k < nb * n2; k++) + { + acb_mul(&dth[k], &dth[k], c, prec); + acb_add_error_arb(&dth[k], u); + } + + acb_theta_jet_tuples(tups, ord, g); + for (k = 0; k < nb; k++) + { + acb_const_pi(c, prec); /* not 2 pi because of rescaling */ + acb_mul_onei(c, c); + acb_pow_ui(c, c, acb_theta_jet_total_order(tups + k * g, g), prec); + fmpz_one(m); + for (j = 0; j < g; j++) + { + fmpz_fac_ui(t, tups[k * g + j]); + fmpz_mul(m, m, t); + } + acb_div_fmpz(c, c, m, prec); + for (j = 0; j < n2; j++) + { + acb_mul(&dth[j * nb + k], &dth[j * nb + k], c, prec); + } + } + + _arb_vec_neg(a, a, g); + acb_theta_jet_exp_pi_i(aux, a, ord, g, prec); + for (k = 0; k < n2; k++) + { + acb_theta_jet_mul(dth + k * nb, dth + k * nb, aux, ord, g, prec); + arb_zero(u); + for (j = 0; j < g; j++) + { + if ((k >> (g - 1 - j)) % 2 == 1) + { + arb_add(u, u, &a[j], prec); + } + } + acb_onei(c); + acb_pow_arb(c, c, u, prec); + _acb_vec_scalar_mul(dth + k * nb, dth + k * nb, nb, c, prec); + } + } + else + { + for (k = 0; k < nb * n2; k++) + { + acb_indeterminate(&dth[k]); + } + } + + flint_free(tups); + acb_theta_eld_clear(E); + arb_mat_clear(C); + arf_clear(R2); + arf_clear(eps); + _acb_vec_clear(aux, n2 * nb); + _acb_vec_clear(new_z, g); + acb_mat_clear(new_tau); + _arb_vec_clear(v, g); + _arb_vec_clear(a, g); + acb_clear(c); + arb_clear(u); + fmpz_clear(m); + fmpz_clear(t); +} + +void +acb_theta_jet_naive_all(acb_ptr dth, acb_srcptr z, const acb_mat_t tau, + slong ord, slong prec) +{ + slong g = acb_mat_nrows(tau); + slong nb = acb_theta_jet_nb(ord, g); + + if (g == 1) + { + acb_modular_theta_jet(dth + 3 * nb, dth + 2 * nb, dth, dth + nb, + z, acb_mat_entry(tau, 0, 0), nb, prec); + _acb_vec_neg(dth + 3 * nb, dth + 3 * nb, nb); + } + else + { + acb_theta_jet_naive_all_gen(dth, z, tau, ord, prec); + } +} diff --git a/src/acb_theta/jet_naive_fixed_ab.c b/src/acb_theta/jet_naive_fixed_ab.c new file mode 100644 index 0000000000..f15b87f83d --- /dev/null +++ b/src/acb_theta/jet_naive_fixed_ab.c @@ -0,0 +1,67 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb_mat.h" +#include "acb_theta.h" + +void +acb_theta_jet_naive_fixed_ab(acb_ptr dth, ulong ab, acb_srcptr z, const acb_mat_t tau, + slong ord, slong prec) +{ + slong g = acb_mat_nrows(tau); + slong nb = acb_theta_jet_nb(ord, g); + ulong a = ab >> g; + ulong b = ab; + acb_ptr v, w, new_z, aux; + arb_ptr u; + acb_t c, x; + + v = _acb_vec_init(g); + w = _acb_vec_init(g); + new_z = _acb_vec_init(g); + aux = _acb_vec_init(nb); + u = _arb_vec_init(g); + acb_init(c); + acb_init(x); + + acb_theta_char_get_acb(v, a, g); + acb_theta_char_get_acb(w, b, g); + acb_theta_char_get_arb(u, a, g); + _arb_vec_scalar_mul_2exp_si(u, u, g, 1); + + /* Get jet at new_z */ + acb_mat_vector_mul_col(new_z, tau, v, prec); + _acb_vec_add(new_z, new_z, w, g, prec); + _acb_vec_add(new_z, new_z, z, g, prec); + acb_theta_jet_naive_00(dth, new_z, tau, ord, prec); + + /* Get exponential factor */ + acb_mat_vector_mul_col(v, tau, v, prec); + acb_theta_char_dot_acb(c, a, v, g, prec); + _acb_vec_add(w, w, z, g, prec); + acb_theta_char_dot_acb(x, a, w, g, prec); + acb_mul_2exp_si(x, x, 1); + acb_add(x, x, c, prec); + acb_exp_pi_i(x, x, prec); + + /* Get other coefficients */ + acb_theta_jet_exp_pi_i(aux, u, ord, g, prec); + _acb_vec_scalar_mul(aux, aux, nb, x, prec); + acb_theta_jet_mul(dth, dth, aux, ord, g, prec); + + _acb_vec_clear(new_z, g); + _acb_vec_clear(v, g); + _acb_vec_clear(w, g); + _acb_vec_clear(aux, nb); + _arb_vec_clear(u, g); + acb_clear(c); + acb_clear(x); +} diff --git a/src/acb_theta/jet_naive_radius.c b/src/acb_theta/jet_naive_radius.c new file mode 100644 index 0000000000..fa940488a8 --- /dev/null +++ b/src/acb_theta/jet_naive_radius.c @@ -0,0 +1,81 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "arb_mat.h" +#include "acb_theta.h" + +void +acb_theta_jet_naive_radius(arf_t R2, arf_t eps, const arb_mat_t C, arb_srcptr v, + slong ord, slong prec) +{ + slong g = arb_mat_nrows(C); + slong lp = ACB_THETA_LOW_PREC; + arb_mat_t mat; + arb_ptr vec; + arb_t na, nx, t, u; + arf_t cmp; + mag_t norm; + + arb_mat_init(mat, g, g); + vec = _arb_vec_init(g); + arb_init(na); + arb_init(nx); + arb_init(t); + arb_init(u); + arf_init(cmp); + mag_init(norm); + + /* Get norms of C^{-1} and v */ + arb_mat_one(mat); + arb_mat_solve_triu(mat, C, mat, 0, lp); + arb_mat_bound_inf_norm(norm, mat); + arf_set_mag(arb_midref(na), norm); + + arb_mat_vector_mul_col(vec, mat, v, prec); + _arb_vec_get_mag(norm, vec, g); + arf_set_mag(arb_midref(nx), norm); + + /* Get R2, eps assuming R <= nx/na */ + acb_theta_naive_radius(R2, eps, C, 0, prec); + arb_mul_2exp_si(t, nx, 1); + arb_one(u); + arb_max(t, t, u, lp); + arb_pow_ui(t, t, ord, lp); + arb_mul_arf(t, t, eps, lp); + arb_get_ubound_arf(eps, t, lp); + + /* If R too large, assume R >= nx/na instead */ + arb_div(t, nx, na, lp); + arb_sqr(t, t, lp); + arb_get_lbound_arf(cmp, t, lp); + if (arf_cmp(cmp, R2) <= 0) + { + acb_theta_naive_radius(R2, eps, C, ord, prec); + arb_div(t, nx, na, lp); + arb_get_ubound_arf(cmp, t, lp); + arf_max(R2, R2, cmp); + arb_mul_2exp_si(t, na, 1); + arb_one(u); + arb_max(t, t, u, lp); + arb_pow_ui(t, t, ord, lp); + arb_mul_arf(t, t, eps, lp); + arb_get_ubound_arf(eps, t, lp); + } + + arb_mat_clear(mat); + _arb_vec_clear(vec, g); + arb_clear(na); + arb_clear(nx); + arb_clear(t); + arb_clear(u); + arf_clear(cmp); + mag_clear(norm); +} diff --git a/src/acb_theta/jet_nb.c b/src/acb_theta/jet_nb.c new file mode 100644 index 0000000000..621967375a --- /dev/null +++ b/src/acb_theta/jet_nb.c @@ -0,0 +1,34 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "fmpz.h" +#include "acb_theta.h" + +slong +acb_theta_jet_nb(slong ord, slong g) +{ + fmpz_t x; + slong res; + + FLINT_ASSERT(g >= 0); + + if (ord < 0) + { + return 0; + } + + fmpz_init(x); + fmpz_bin_uiui(x, g + ord, g); + res = fmpz_get_si(x); + + fmpz_clear(x); + return res; +} diff --git a/src/acb_theta/jet_ql_all.c b/src/acb_theta/jet_ql_all.c new file mode 100644 index 0000000000..a2384f4383 --- /dev/null +++ b/src/acb_theta/jet_ql_all.c @@ -0,0 +1,183 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "ulong_extras.h" +#include "acb_mat.h" +#include "acb_theta.h" + +static void +acb_theta_jet_ql_all_red(acb_ptr dth, acb_srcptr z, const acb_mat_t tau, slong ord, slong prec) +{ + slong g = acb_mat_nrows(tau); + slong n2 = 1 << (2 * g); + slong b = ord + 1; + slong hprec; + slong lp = ACB_THETA_LOW_PREC; + slong nb = acb_theta_jet_nb(ord, g); + slong nb_low = acb_theta_jet_nb(ord + 2, g); + int hasz = !_acb_vec_is_zero(z, g); + arb_t c, rho, t; + arf_t eps, err, e; + acb_mat_t tau_mid; + acb_ptr z_mid, zetas, new_z, all_val, val, jet, dth_low; + arb_ptr err_vec; + slong k, kmod, j; + + arb_init(c); + arb_init(rho); + arb_init(t); + arf_init(eps); + arf_init(err); + + /* Get bounds and high precision, fail if too large */ + acb_theta_jet_ql_bounds(c, rho, z, tau, ord); + acb_theta_jet_ql_radius(eps, err, c, rho, ord, g, prec); + arb_set_arf(t, eps); + arb_log_base_ui(t, t, 2, lp); + arb_neg(t, t); + + /* we expect that the second bound holds if finite, but still check */ + if (!arb_is_finite(t) || (arf_cmpabs_2exp_si(arb_midref(t), 20) > 0)) + { + _acb_vec_indeterminate(dth, n2 * nb); + arb_clear(c); + arb_clear(rho); + arb_clear(t); + arf_clear(eps); + arf_clear(err); + return; + } + + arf_init(e); + acb_mat_init(tau_mid, g, g); + z_mid = _acb_vec_init(g); + zetas = _acb_vec_init(b); + new_z = _acb_vec_init(g); + all_val = _acb_vec_init(n2 * n_pow(b, g)); + val = _acb_vec_init(n_pow(b, g)); + jet = _acb_vec_init(nb); + dth_low = _acb_vec_init(n2 * nb_low); + err_vec = _arb_vec_init(nb); + + hprec = prec + ord * (arf_get_si(arb_midref(t), ARF_RND_CEIL) + g); + arf_one(e); + arf_mul_2exp_si(e, e, -hprec); + + /* Get midpoint at precision hprec */ + for (j = 0; j < g; j++) + { + for (k = 0; k < g; k++) + { + acb_get_mid(acb_mat_entry(tau_mid, j, k), acb_mat_entry(tau, j, k)); + acb_add_error_arf(acb_mat_entry(tau_mid, j, k), e); + acb_set(acb_mat_entry(tau_mid, k, j), acb_mat_entry(tau_mid, j, k)); + } + acb_get_mid(&z_mid[j], &z[j]); + if (hasz) + { + acb_add_error_arf(&z_mid[j], e); + } + } + + /* Collect values around midpoint */ + _acb_vec_unit_roots(zetas, b, b, hprec); + for (k = 0; k < n_pow(b, g); k++) + { + kmod = k; + for (j = g - 1; j >= 0; j--) + { + acb_set(&new_z[j], &zetas[kmod % b]); + kmod = kmod / b; + } + arb_set_arf(t, eps); + _acb_vec_scalar_mul_arb(new_z, new_z, g, t, hprec); + _acb_vec_add(new_z, new_z, z_mid, g, hprec); + + acb_theta_ql_all(all_val + k * n2, new_z, tau_mid, 0, hprec); + } + + /* Make finite differences */ + for (k = 0; k < n2; k++) + { + for (j = 0; j < n_pow(b, g); j++) + { + acb_set(&val[j], &all_val[j * n2 + k]); + } + acb_theta_jet_ql_finite_diff(jet, eps, err, rho, val, ord, g, hprec); + _acb_vec_set(dth + k * nb, jet, nb); + } + + /* Add error */ + acb_theta_jet_naive_all(dth_low, z, tau, ord + 2, lp); + for (k = 0; k < n2; k++) + { + acb_theta_jet_error_bounds(err_vec, z, tau, dth_low + k * nb_low, ord, lp); + for (j = 0; j < nb; j++) + { + acb_add_error_arb(&dth[k * nb + j], &err_vec[j]); + } + } + + arb_clear(c); + arb_clear(rho); + arb_clear(t); + arf_clear(eps); + arf_clear(err); + arf_clear(e); + acb_mat_clear(tau_mid); + _acb_vec_clear(z_mid, g); + _acb_vec_clear(zetas, b); + _acb_vec_clear(new_z, g); + _acb_vec_clear(all_val, n2 * n_pow(b, g)); + _acb_vec_clear(val, n_pow(b, g)); + _acb_vec_clear(jet, nb); + _acb_vec_clear(dth_low, n2 * nb_low); + _arb_vec_clear(err_vec, nb); +} + +void +acb_theta_jet_ql_all(acb_ptr dth, acb_srcptr z, const acb_mat_t tau, slong ord, slong prec) +{ + slong g = acb_mat_nrows(tau); + slong n2 = 1 << (2 * g); + slong nb = acb_theta_jet_nb(ord, g); + acb_ptr aux, new_z; + arb_ptr v, a; + acb_t c; + arb_t u; + slong k; + + aux = _acb_vec_init(nb); + new_z = _acb_vec_init(g); + v = _arb_vec_init(g); + a = _arb_vec_init(g); + acb_init(c); + arb_init(u); + + acb_theta_naive_reduce(v, new_z, a, c, u, z, 1, tau, prec); + acb_theta_jet_ql_all_red(dth, new_z, tau, ord, prec); + + _acb_vec_scalar_mul(dth, dth, n2 * nb, c, prec); + _arb_vec_neg(a, a, g); + _arb_vec_scalar_mul_2exp_si(a, a, g, 1); + acb_theta_jet_exp_pi_i(aux, a, ord, g, prec); + for (k = 0; k < n2; k++) + { + acb_theta_jet_mul(dth + k * nb, dth + k * nb, aux, ord, g, prec); + } + + _acb_vec_clear(aux, nb); + _acb_vec_clear(new_z, g); + _arb_vec_clear(v, g); + _arb_vec_clear(a, g); + acb_clear(c); + arb_clear(u); +} diff --git a/src/acb_theta/jet_ql_bounds.c b/src/acb_theta/jet_ql_bounds.c new file mode 100644 index 0000000000..ec850e9bd6 --- /dev/null +++ b/src/acb_theta/jet_ql_bounds.c @@ -0,0 +1,132 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "arb_mat.h" +#include "acb_mat.h" +#include "acb_theta.h" + +/* Compute c0, c1, c2 such that |theta_{a,b}(z,tau)| on a ball of radius rho + around z is bounded above by c0 exp((c1 + c2 rho)^2) */ + +static void +acb_theta_jet_ql_ci(arb_t c0, arb_t c1, arb_t c2, acb_srcptr z, const acb_mat_t tau) +{ + slong lp = ACB_THETA_LOW_PREC; + slong g = acb_mat_nrows(tau); + arb_mat_t Yinv; + arb_mat_t cho; + arb_ptr y, w; + arb_t t, s; + slong k, j; + + arb_mat_init(Yinv, g, g); + arb_mat_init(cho, g, g); + y = _arb_vec_init(g); + w = _arb_vec_init(g); + arb_init(t); + arb_init(s); + + _acb_vec_get_imag(y, z, g); + acb_siegel_yinv(Yinv, tau, lp); + acb_siegel_cho(cho, tau, lp); + + /* c0 is 2^g \prod_{i=1}^g (1 + 2/\sqrt{\gamma_i}) */ + arb_one(c0); + arb_mul_2exp_si(c0, c0, g); + for (k = 0; k < g; k++) + { + arb_mul_2exp_si(t, arb_mat_entry(cho, k, k), 1); + arb_add_si(t, t, 1, lp); + arb_mul(c0, c0, t, lp); + } + + /* c1 is sqrt(\pi y Y^{-1} y) */ + arb_const_pi(t, lp); + arb_mat_scalar_mul_arb(Yinv, Yinv, t, lp); + arb_mat_vector_mul_col(w, Yinv, y, lp); + arb_dot(c1, NULL, 0, y, 1, w, 1, g, lp); + arb_nonnegative_part(c1, c1); + arb_sqrt(c1, c1, lp); + + /* c2 is sqrt(max of \pi x Y^{-1} x where |x| \leq 1) */ + arb_zero(c2); + arb_mat_cho(cho, Yinv, lp); + arb_mat_transpose(cho, cho); + for (k = 0; k < g; k++) + { + arb_zero(s); + for (j = k; j < g; j++) + { + arb_abs(t, arb_mat_entry(cho, k, j)); + arb_add(s, s, t, lp); + } + arb_sqr(s, s, lp); + arb_add(c2, c2, s, lp); + } + arb_nonnegative_part(c2, c2); + arb_sqrt(c2, c2, lp); + + arb_mat_clear(Yinv); + arb_mat_clear(cho); + _arb_vec_clear(y, g); + _arb_vec_clear(w, g); + arb_clear(t); + arb_clear(s); +} + +/* Pick rho and c such that |theta_{a,b}(z,tau)| on a ball of radius rho around + z is bounded above by c, and is a good choice to bound derivatives up to + order ord */ + +void +acb_theta_jet_ql_bounds(arb_t c, arb_t rho, acb_srcptr z, const acb_mat_t tau, slong ord) +{ + slong lp = ACB_THETA_LOW_PREC; + slong b = ord + 1; + arb_t t, c0, c1, c2; + arf_t x; + + arb_init(t); + arb_init(c0); + arb_init(c1); + arb_init(c2); + arf_init(x); + + /* Get ci */ + acb_theta_jet_ql_ci(c0, c1, c2, z, tau); + + /* Set rho to positive root of 2 c_2 rho (c_1 + c_2 rho) = 2 b - 1 */ + arb_mul(t, c1, c2, lp); + arb_mul_2exp_si(t, t, 1); + arb_sqr(rho, t, lp); + arb_sqr(t, c2, lp); + arb_mul_si(t, t, 8 * (2 * b - 1), lp); + arb_add(rho, rho, t, lp); + arb_sqrt(rho, rho, lp); + arb_mul(t, c1, c2, lp); + arb_submul_si(rho, t, 2, lp); + arb_sqr(t, c2, lp); + arb_mul_2exp_si(t, t, 2); + arb_div(rho, rho, t, lp); + + /* Set c to corresponding bound */ + arb_mul(c, c2, rho, lp); + arb_add(c, c, c1, lp); + arb_sqr(c, c, lp); + arb_exp(c, c, lp); + arb_mul(c, c, c0, lp); + + arb_clear(t); + arb_clear(c0); + arb_clear(c1); + arb_clear(c2); + arf_clear(x); +} diff --git a/src/acb_theta/jet_ql_finite_diff.c b/src/acb_theta/jet_ql_finite_diff.c new file mode 100644 index 0000000000..067d1e4da0 --- /dev/null +++ b/src/acb_theta/jet_ql_finite_diff.c @@ -0,0 +1,78 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb_dft.h" +#include "acb_theta.h" + +/* Given values of f at (x_1 + eps zeta^{n_1}, ..., x_g + eps zeta^{n_g}), make + Fourier transforms to get Taylor coefficients and add error bound */ + +void +acb_theta_jet_ql_finite_diff(acb_ptr dth, const arf_t eps, const arf_t err, + const arb_t rho, acb_srcptr val, slong ord, slong g, slong prec) +{ + slong nb = acb_theta_jet_nb(ord, g); + slong lp = ACB_THETA_LOW_PREC; + acb_ptr aux; + arb_t t, e; + slong b = ord + 1; + slong * tups; + slong * cyc; + slong j, i, l; + slong k; + + aux = _acb_vec_init(n_pow(b, g)); + arb_init(t); + arb_init(e); + tups = flint_malloc(g * nb * sizeof(slong)); + cyc = flint_malloc(g * sizeof(slong)); + + for (j = 0; j < g; j++) + { + cyc[j] = b; + } + acb_dft_prod(aux, val, cyc, g, prec); + arb_set_si(t, n_pow(b, g)); + _acb_vec_scalar_div_arb(aux, aux, n_pow(b, g), t, prec); + + /* Get Taylor coefficients, divide by eps^k, add error */ + acb_theta_jet_tuples(tups, ord, g); + k = 0; + arb_one(t); + arb_pow_ui(e, rho, ord, lp); + arb_mul_arf(e, e, err, lp); + for (j = 0; j < nb; j++) + { + l = 0; + for (i = 0; i < g; i++) + { + l *= b; + l += tups[j * g + i]; + } + acb_set(&dth[j], &aux[l]); + + if (acb_theta_jet_total_order(tups + j * g, g) > k) + { + k++; + arb_mul_arf(t, t, eps, prec); + arb_pow_ui(e, rho, ord - k, lp); + arb_mul_arf(e, e, err, lp); + } + acb_div_arb(&dth[j], &dth[j], t, prec); + acb_add_error_arb(&dth[j], e); + } + + _acb_vec_clear(aux, n_pow(b, g)); + arb_clear(t); + arb_clear(e); + flint_free(tups); + flint_free(cyc); +} diff --git a/src/acb_theta/jet_ql_radius.c b/src/acb_theta/jet_ql_radius.c new file mode 100644 index 0000000000..457af1ef0c --- /dev/null +++ b/src/acb_theta/jet_ql_radius.c @@ -0,0 +1,46 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "arb.h" +#include "acb_theta.h" + +void +acb_theta_jet_ql_radius(arf_t eps, arf_t err, const arb_t c, const arb_t rho, + slong ord, slong g, slong prec) +{ + slong lp = ACB_THETA_LOW_PREC; + slong b = ord + 1; + arb_t x, y; + + arb_init(x); + arb_init(y); + + /* Set x to min of (1/2g)^(1/b)*rho, (2^(-prec)/2cg)^(1/b)*rho^(2b-1)/b */ + arb_set_si(x, 2 * g); + arb_inv(x, x, lp); + arb_root_ui(x, x, b, lp); + arb_mul(x, x, rho, lp); + + arb_pow_ui(y, rho, 2 * b - 1, prec); + arb_mul_2exp_si(y, y, -prec); + arb_div(y, y, c, lp); + arb_div_si(y, y, 2 * g, lp); + arb_root_ui(y, y, b, lp); + + arb_min(x, x, y, lp); + arb_get_lbound_arf(eps, x, lp); + + arf_one(err); + arf_mul_2exp_si(err, err, -prec); + + arb_clear(x); + arb_clear(y); +} diff --git a/src/acb_theta/jet_total_order.c b/src/acb_theta/jet_total_order.c new file mode 100644 index 0000000000..6f2a077dda --- /dev/null +++ b/src/acb_theta/jet_total_order.c @@ -0,0 +1,26 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb_theta.h" + +slong +acb_theta_jet_total_order(const slong * tup, slong g) +{ + slong k; + slong res = 0; + + for (k = 0; k < g; k++) + { + res += tup[k]; + } + + return res; +} diff --git a/src/acb_theta/jet_tuples.c b/src/acb_theta/jet_tuples.c new file mode 100644 index 0000000000..e8ee977c84 --- /dev/null +++ b/src/acb_theta/jet_tuples.c @@ -0,0 +1,49 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb_theta.h" + +void +acb_theta_jet_tuples(slong * tups, slong ord, slong g) +{ + slong k, j, l, nb_rec, ind; + slong * rec; + + if (g == 1) + { + for (k = 0; k <= ord; k++) + { + tups[k] = k; + } + return; + } + + /* Generate tuples in dimension g - 1 */ + nb_rec = acb_theta_jet_nb(ord, g - 1); + rec = flint_malloc((g - 1) * nb_rec * sizeof(slong)); + acb_theta_jet_tuples(rec, ord, g - 1); + + for (k = 0; k <= ord; k++) + { + /* Construct tuples of total order k from rec */ + ind = acb_theta_jet_nb(k - 1, g); + for (j = 0; j < acb_theta_jet_nb(k, g - 1); j++) + { + tups[(ind + j) * g] = k - acb_theta_jet_total_order(rec + j * (g - 1), g - 1); + for (l = 0; l < g - 1; l++) + { + tups[(ind + j) * g + l + 1] = rec[j * (g - 1) + l]; + } + } + } + + flint_free(rec); +} diff --git a/src/acb_theta/naive_00.c b/src/acb_theta/naive_00.c new file mode 100644 index 0000000000..16846643df --- /dev/null +++ b/src/acb_theta/naive_00.c @@ -0,0 +1,130 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "arb_mat.h" +#include "acb_mat.h" +#include "acb_modular.h" +#include "acb_theta.h" + +static void +worker(acb_ptr th, acb_srcptr v1, acb_srcptr v2, const slong * precs, slong len, + const acb_t cofactor, const slong * coords, slong ord, slong g, slong prec, slong fullprec) +{ + acb_t sum; + + acb_init(sum); + + acb_dot(sum, NULL, 0, v1, 1, v2, 1, len, prec); + acb_addmul(th, sum, cofactor, fullprec); + + acb_clear(sum); +} + +static void +acb_theta_naive_00_gen(acb_ptr th, acb_srcptr zs, slong nb, const acb_mat_t tau, slong prec) +{ + slong g = acb_mat_nrows(tau); + acb_theta_eld_t E; + arb_mat_t C; + arf_t R2, eps; + acb_ptr cs; + arb_ptr v, as, us; + acb_ptr new_zs; + slong k; + int b; + + acb_theta_eld_init(E, g, g); + arb_mat_init(C, g, g); + arf_init(R2); + arf_init(eps); + cs = _acb_vec_init(nb); + us = _arb_vec_init(nb); + as = _arb_vec_init(g * nb); + v = _arb_vec_init(g); + new_zs = _acb_vec_init(g * nb); + + acb_siegel_cho(C, tau, prec); + acb_theta_naive_radius(R2, eps, C, 0, prec); + acb_theta_naive_reduce(v, new_zs, as, cs, us, zs, nb, tau, prec); + b = acb_theta_eld_set(E, C, R2, v); + + if (b) + { + acb_theta_naive_worker(th, 1, new_zs, nb, tau, E, 0, prec, worker); + + for (k = 0; k < nb; k++) + { + acb_mul(&th[k], &th[k], &cs[k], prec); + arb_mul_arf(&us[k], &us[k], eps, prec); + acb_add_error_arb(&th[k], &us[k]); + } + } + else + { + for (k = 0; k < nb; k++) + { + acb_indeterminate(&th[k]); + } + } + + acb_theta_eld_clear(E); + arb_mat_clear(C); + arf_clear(R2); + arf_clear(eps); + _acb_vec_clear(cs, nb); + _arb_vec_clear(us, nb); + _arb_vec_clear(as, g * nb); + _arb_vec_clear(v, g); + _acb_vec_clear(new_zs, g * nb); +} + +static void +acb_theta_naive_00_g1(acb_ptr th, acb_srcptr zs, slong nb, const acb_mat_t tau, slong prec) +{ + acb_t q, w; + acb_ptr res; + int w_is_unit; + slong k; + + acb_init(q); + acb_init(w); + res = _acb_vec_init(4); + + acb_exp_pi_i(q, acb_mat_entry(tau, 0, 0), prec); + + for (k = 0; k < nb; k++) + { + acb_exp_pi_i(w, &zs[k], prec); + w_is_unit = arb_is_zero(acb_imagref(&zs[k])); + acb_modular_theta_sum(&res[0], &res[1], &res[2], &res[3], + w, w_is_unit, q, 1, prec); + acb_set(&th[k], &res[2]); + } + + acb_clear(q); + acb_clear(w); + _acb_vec_clear(res, 4); +} + +void +acb_theta_naive_00(acb_ptr th, acb_srcptr zs, slong nb, const acb_mat_t tau, slong prec) +{ + slong g = acb_mat_nrows(tau); + + if (g == 1) + { + acb_theta_naive_00_g1(th, zs, nb, tau, prec); + } + else + { + acb_theta_naive_00_gen(th, zs, nb, tau, prec); + } +} diff --git a/src/acb_theta/naive_0b.c b/src/acb_theta/naive_0b.c new file mode 100644 index 0000000000..b801b216c9 --- /dev/null +++ b/src/acb_theta/naive_0b.c @@ -0,0 +1,170 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "arb_mat.h" +#include "acb_mat.h" +#include "acb_modular.h" +#include "acb_theta.h" + +static void +worker(acb_ptr th, acb_srcptr v1, acb_srcptr v2, const slong * precs, slong len, + const acb_t cofactor, const slong * coords, slong ord, slong g, slong prec, slong fullprec) +{ + slong n = 1 << g; + acb_t s0, s1, add, sub; + ulong b; + slong dot; + + acb_init(s0); + acb_init(s1); + acb_init(add); + acb_init(sub); + + /* Compute alternate sums to adjust signs */ + acb_dot(s0, NULL, 0, v1, 2, v2, 2, (len + 1) / 2, prec); + acb_dot(s1, NULL, 0, v1 + 1, 2, v2 + 1, 2, len / 2, prec); + acb_add(add, s0, s1, prec); + acb_sub(sub, s0, s1, prec); + acb_mul(add, add, cofactor, prec); + acb_mul(sub, sub, cofactor, prec); + + for (b = 0; b < n; b++) + { + dot = acb_theta_char_dot_slong(b, coords, g) % 2; + if ((b >> (g - 1)) && dot) + { + acb_sub(&th[b], &th[b], sub, fullprec); + } + else if ((b >> (g - 1))) + { + acb_add(&th[b], &th[b], sub, fullprec); + } + else if (dot) + { + acb_sub(&th[b], &th[b], add, fullprec); + } + else + { + acb_add(&th[b], &th[b], add, fullprec); + } + } + + acb_clear(s0); + acb_clear(s1); + acb_clear(add); + acb_clear(sub); +} + +static void +acb_theta_naive_0b_gen(acb_ptr th, acb_srcptr zs, slong nb, const acb_mat_t tau, slong prec) +{ + slong g = acb_mat_nrows(tau); + acb_theta_eld_t E; + arb_mat_t C; + arf_t R2, eps; + acb_ptr cs; + arb_ptr v, as, us; + acb_ptr new_zs; + slong len = 1 << g; + slong k, l; + int b; + + acb_theta_eld_init(E, g, g); + arb_mat_init(C, g, g); + arf_init(R2); + arf_init(eps); + cs = _acb_vec_init(nb); + as = _arb_vec_init(g * nb); + us = _arb_vec_init(nb); + v = _arb_vec_init(g); + new_zs = _acb_vec_init(nb * g); + + acb_siegel_cho(C, tau, prec); + acb_theta_naive_radius(R2, eps, C, 0, prec); + acb_theta_naive_reduce(v, new_zs, as, cs, us, zs, nb, tau, prec); + b = acb_theta_eld_set(E, C, R2, v); + + if (b) + { + acb_theta_naive_worker(th, len, new_zs, nb, tau, E, 0, prec, worker); + + for (k = 0; k < nb; k++) + { + _acb_vec_scalar_mul(th + k * len, th + k * len, len, &cs[k], prec); + arb_mul_arf(&us[k], &us[k], eps, prec); + for (l = 0; l < len; l++) + { + acb_add_error_arb(&th[k * len + l], &us[k]); + } + } + } + else + { + for (k = 0; k < nb * len; k++) + { + acb_indeterminate(&th[k]); + } + } + + acb_theta_eld_clear(E); + arb_mat_clear(C); + arf_clear(R2); + arf_clear(eps); + _acb_vec_clear(cs, nb); + _arb_vec_clear(as, g * nb); + _arb_vec_clear(us, nb); + _arb_vec_clear(v, g); + _acb_vec_clear(new_zs, nb * g); +} + +static void +acb_theta_naive_0b_g1(acb_ptr th, acb_srcptr zs, slong nb, const acb_mat_t tau, slong prec) +{ + acb_t q, w; + acb_ptr res; + int w_is_unit; + slong k; + + acb_init(q); + acb_init(w); + res = _acb_vec_init(4); + + acb_exp_pi_i(q, acb_mat_entry(tau, 0, 0), prec); + + for (k = 0; k < nb; k++) + { + acb_exp_pi_i(w, &zs[k], prec); + w_is_unit = arb_is_zero(acb_imagref(&zs[k])); + acb_modular_theta_sum(&res[0], &res[1], &res[2], &res[3], + w, w_is_unit, q, 1, prec); + acb_set(&th[2 * k], &res[2]); + acb_set(&th[2 * k + 1], &res[3]); + } + + acb_clear(q); + acb_clear(w); + _acb_vec_clear(res, 4); +} + +void +acb_theta_naive_0b(acb_ptr th, acb_srcptr zs, slong nb, const acb_mat_t tau, slong prec) +{ + slong g = acb_mat_nrows(tau); + + if (g == 1) + { + acb_theta_naive_0b_g1(th, zs, nb, tau, prec); + } + else + { + acb_theta_naive_0b_gen(th, zs, nb, tau, prec); + } +} diff --git a/src/acb_theta/naive_all.c b/src/acb_theta/naive_all.c new file mode 100644 index 0000000000..50f5ce3ec3 --- /dev/null +++ b/src/acb_theta/naive_all.c @@ -0,0 +1,123 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb_mat.h" +#include "acb_modular.h" +#include "acb_theta.h" + +static void +acb_theta_naive_all_gen(acb_ptr th, acb_srcptr zs, slong nb, const acb_mat_t tau, slong prec) +{ + slong g = acb_mat_nrows(tau); + slong n = 1 << g; + acb_ptr all_zs, ata, v; + acb_t c; + slong a, b, d, k; + + all_zs = _acb_vec_init(g * n * nb); + ata = _acb_vec_init(n); + v = _acb_vec_init(g); + acb_init(c); + + for (a = 0; a < n; a++) + { + acb_theta_char_get_acb(v, a, g); + acb_mat_vector_mul_col(v, tau, v, prec); + for (k = 0; k < nb; k++) + { + _acb_vec_add(all_zs + k * g * n + a * g, zs + k * g, v, g, prec); + } + acb_theta_char_dot_acb(&ata[a], a, v, g, prec); + } + + acb_theta_naive_0b(th, all_zs, n * nb, tau, prec); + + for (a = 0; a < n; a++) + { + /* Factors depending on z, not on b */ + for (k = 0; k < nb; k++) + { + acb_theta_char_dot_acb(c, a, zs + k * g, g, prec); + acb_mul_2exp_si(c, c, 1); + acb_add(c, c, &ata[a], prec); + acb_exp_pi_i(c, c, prec); + _acb_vec_scalar_mul(th + k * n * n + a * n, + th + k * n * n + a * n, n, c, prec); + } + /* Factors depending on b, not on z */ + for (b = 0; b < n; b++) + { + d = acb_theta_char_dot(a, b, g); + for (k = 0; k < nb; k++) + { + acb_mul_i_pow_si(&th[k * n * n + a * n + b], + &th[k * n * n + a * n + b], d); + } + } + } + + _acb_vec_clear(all_zs, g * n * nb); + _acb_vec_clear(ata, n); + _acb_vec_clear(v, g); + acb_clear(c); +} + +static void +acb_theta_naive_all_g1(acb_ptr th, acb_srcptr zs, slong nb, const acb_mat_t tau, slong prec) +{ + acb_t q4, q, w; + acb_ptr res; + int w_is_unit; + slong k; + + acb_init(q4); + acb_init(q); + acb_init(w); + res = _acb_vec_init(4); + + acb_mul_2exp_si(q4, acb_mat_entry(tau, 0, 0), -2); + acb_exp_pi_i(q4, q4, prec); + acb_pow_ui(q, q4, 4, prec); + + for (k = 0; k < nb; k++) + { + acb_exp_pi_i(w, &zs[k], prec); + w_is_unit = arb_is_zero(acb_imagref(&zs[k])); + acb_modular_theta_sum(&res[0], &res[1], &res[2], &res[3], + w, w_is_unit, q, 1, prec); + acb_set(&th[4 * k], &res[2]); + acb_set(&th[4 * k + 1], &res[3]); + acb_mul(&th[4 * k + 2], &res[1], q4, prec); + acb_mul(&th[4 * k + 3], &res[0], q4, prec); + acb_neg(&th[4 * k + 3], &th[4 * k + 3]); + } + + acb_clear(q4); + acb_clear(q); + acb_clear(w); + _acb_vec_clear(res, 4); +} + +void +acb_theta_naive_all(acb_ptr th, acb_srcptr zs, slong nb, const acb_mat_t tau, slong prec) +{ + slong g = acb_mat_nrows(tau); + + if (g == 1) + { + acb_theta_naive_all_g1(th, zs, nb, tau, prec); + } + else + { + acb_theta_naive_all_gen(th, zs, nb, tau, prec); + } +} + diff --git a/src/acb_theta/naive_fixed_a.c b/src/acb_theta/naive_fixed_a.c new file mode 100644 index 0000000000..21802050d6 --- /dev/null +++ b/src/acb_theta/naive_fixed_a.c @@ -0,0 +1,61 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb_mat.h" +#include "acb_theta.h" + +void +acb_theta_naive_fixed_a(acb_ptr th, ulong a, acb_srcptr zs, slong nb, + const acb_mat_t tau, slong prec) +{ + slong g = acb_mat_nrows(tau); + slong n = 1 << g; + acb_ptr new_zs; + acb_ptr v, w; + acb_t c, x; + slong k, b; + + new_zs = _acb_vec_init(nb * g); + v = _acb_vec_init(g); + w = _acb_vec_init(g); + acb_init(c); + acb_init(x); + + acb_theta_char_get_acb(v, a, g); + acb_mat_vector_mul_col(v, tau, v, prec); /* tau.a/2 */ + for (k = 0; k < nb; k++) + { + _acb_vec_add(new_zs + k * g, zs + k * g, v, g, prec); + } + + acb_theta_naive_0b(th, new_zs, nb, tau, prec); + + acb_theta_char_dot_acb(c, a, v, g, prec); + for (k = 0; k < nb; k++) + { + for (b = 0; b < n; b++) + { + acb_theta_char_get_acb(w, b, g); + _acb_vec_add(w, w, zs + k * g, g, prec); + acb_theta_char_dot_acb(x, a, w, g, prec); + acb_mul_2exp_si(x, x, 1); + acb_add(x, x, c, prec); + acb_exp_pi_i(x, x, prec); + acb_mul(&th[k * n + b], &th[k * n + b], x, prec); + } + } + + _acb_vec_clear(new_zs, nb * g); + _acb_vec_clear(v, g); + _acb_vec_clear(w, g); + acb_clear(c); + acb_clear(x); +} diff --git a/src/acb_theta/naive_fixed_ab.c b/src/acb_theta/naive_fixed_ab.c new file mode 100644 index 0000000000..a65864f23a --- /dev/null +++ b/src/acb_theta/naive_fixed_ab.c @@ -0,0 +1,61 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb_mat.h" +#include "acb_theta.h" + +void +acb_theta_naive_fixed_ab(acb_ptr th, ulong ab, acb_srcptr zs, slong nb, + const acb_mat_t tau, slong prec) +{ + slong g = acb_mat_nrows(tau); + ulong a = ab >> g; + ulong b = ab; + acb_ptr new_zs; + acb_ptr v, w; + acb_t c, x; + slong k; + + new_zs = _acb_vec_init(nb * g); + v = _acb_vec_init(g); + w = _acb_vec_init(g); + acb_init(c); + acb_init(x); + + acb_theta_char_get_acb(v, a, g); + acb_mat_vector_mul_col(v, tau, v, prec); /* tau.a/2 */ + acb_theta_char_get_acb(w, b, g); + _acb_vec_add(w, v, w, g, prec); + for (k = 0; k < nb; k++) + { + _acb_vec_add(new_zs + k * g, zs + k * g, w, g, prec); + } + + acb_theta_naive_00(th, new_zs, nb, tau, prec); + + acb_theta_char_dot_acb(c, a, v, g, prec); + for (k = 0; k < nb; k++) + { + acb_theta_char_get_acb(w, b, g); + _acb_vec_add(w, w, zs + k * g, g, prec); + acb_theta_char_dot_acb(x, a, w, g, prec); + acb_mul_2exp_si(x, x, 1); + acb_add(x, x, c, prec); + acb_exp_pi_i(x, x, prec); + acb_mul(&th[k], &th[k], x, prec); + } + + _acb_vec_clear(new_zs, nb * g); + _acb_vec_clear(v, g); + _acb_vec_clear(w, g); + acb_clear(c); + acb_clear(x); +} diff --git a/src/acb_theta/naive_radius.c b/src/acb_theta/naive_radius.c new file mode 100644 index 0000000000..91b867f792 --- /dev/null +++ b/src/acb_theta/naive_radius.c @@ -0,0 +1,118 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "arb_mat.h" +#include "acb_theta.h" + +/* Assuming a >= 0, return R2 such that x - (a/2)*log(x)\geq b for all + x\geq R2, and R2 is close to the smallest possible */ + +static void +invert_lin_plus_log(arf_t R2, slong a, const arb_t b, slong prec) +{ + arb_t x, y, t; + arf_t z; + slong k; + + arb_init(x); + arb_init(y); + arb_init(t); + arf_init(z); + + if (a == 0) + { + arb_get_ubound_arf(R2, b, prec); + goto exit; + } + + /* minimum is at x=a/2 */ + arb_set_si(x, a); + arb_div_si(x, x, 2, prec); + arb_log(y, x, prec); + arb_mul(y, y, x, prec); + arb_sub(y, x, y, prec); + + /* x = max(a, 2*(b - min) + a log 2) is always large enough; then iterate + function a few times */ + arb_sub(y, b, y, prec); + arb_const_log2(t, prec); + arb_mul_2exp_si(t, t, -1); + arb_mul_si(t, t, a, prec); + arb_add(y, y, t, prec); + arb_max(y, y, x, prec); + arb_mul_si(x, y, 2, prec); + arb_get_ubound_arf(z, x, prec); + arb_set_arf(x, z); + + for (k = 0; k < 4; k++) + { + arb_log(y, x, prec); + arb_mul_si(y, y, a, prec); + arb_div_si(y, y, 2, prec); + arb_add(x, b, y, prec); + arb_get_ubound_arf(z, x, prec); + arb_set_arf(x, z); + } + + arb_get_ubound_arf(R2, x, prec); + goto exit; + + exit: + { + arb_clear(x); + arb_clear(y); + arb_clear(t); + arf_clear(z); + } +} + +void +acb_theta_naive_radius(arf_t R2, arf_t eps, const arb_mat_t C, slong ord, slong prec) +{ + slong g = arb_mat_nrows(C); + slong lp = ACB_THETA_LOW_PREC; + arb_t b, temp; + arf_t cmp; + slong k; + + arb_init(b); + arb_init(temp); + arf_init(cmp); + + arf_one(eps); + arf_mul_2exp_si(eps, eps, -prec); + arb_set_arf(b, eps); + arb_mul_2exp_si(b, b, -2 * g - 2); + + /* Solve R2^((g-1)/2+ord) exp(-R2) \leq b */ + arb_log(b, b, lp); + arb_neg(b, b); + invert_lin_plus_log(R2, g - 1 + 2 * ord, b, lp); + + /* Max with 4, 2*ord for formula to be valid */ + arf_set_si(cmp, FLINT_MAX(4, 2 * ord)); + arf_max(R2, R2, cmp); + + /* Set error */ + arb_one(b); + for (k = 0; k < g; k++) + { + arb_inv(temp, arb_mat_entry(C, k, k), lp); + arb_add_si(temp, temp, 1, lp); + arb_mul(b, b, temp, lp); + } + arb_mul_arf(b, b, eps, lp); + arb_get_ubound_arf(eps, b, lp); + + arb_clear(b); + arb_clear(temp); + arf_clear(cmp); +} diff --git a/src/acb_theta/naive_reduce.c b/src/acb_theta/naive_reduce.c new file mode 100644 index 0000000000..82d2c2c303 --- /dev/null +++ b/src/acb_theta/naive_reduce.c @@ -0,0 +1,157 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "arb_mat.h" +#include "acb_mat.h" +#include "acb_theta.h" + +static void +acb_theta_naive_round(arb_ptr a, arb_srcptr v, slong g) +{ + slong j; + fmpz_t m; + + fmpz_init(m); + + for (j = 0; j < g; j++) + { + if (arb_is_finite(&v[j]) + && arf_cmpabs_2exp_si(arb_midref(&v[j]), 1000000) <= 0) + { + arf_get_fmpz(m, arb_midref(&v[j]), ARF_RND_NEAR); + arb_set_fmpz(&a[j], m); + } + else + { + arb_zero(&a[j]); + } + } + + fmpz_clear(m); +} + +static void +_arb_vec_union(arb_ptr res, arb_srcptr v1, arb_srcptr v2, slong len, slong prec) +{ + slong j; + + for (j = 0; j < len; j++) + { + arb_union(&res[j], &v1[j], &v2[j], prec); + } +} + +static void +acb_theta_naive_reduce_one(arb_ptr v, acb_ptr new_z, arb_ptr a, acb_t c, arb_t u, + acb_srcptr z, const arb_mat_t X, const arb_mat_t Y, const arb_mat_t Yinv, + const arb_mat_t C, slong prec) +{ + slong g = arb_mat_nrows(X); + arb_ptr x, y, t, r, new_x, new_y; + + x = _arb_vec_init(g); + y = _arb_vec_init(g); + t = _arb_vec_init(g); + r = _arb_vec_init(g); + new_x = _arb_vec_init(g); + new_y = _arb_vec_init(g); + + acb_zero(c); + _acb_vec_get_real(x, z, g); + _acb_vec_get_imag(y, z, g); + + /* Get center t = Yinv y of ellipsoid, set c = - i y^T Yinv y and u */ + arb_mat_vector_mul_col(t, Yinv, y, prec); + arb_dot(acb_imagref(c), acb_imagref(c), 1, y, 1, t, 1, g, prec); + + arb_const_pi(u, prec); + arb_mul(u, u, acb_imagref(c), prec); + arb_neg(u, u); + arb_exp(u, u, prec); + + /* Round to nearest vector a = 0 mod 2 to not mess with characteristics */ + _arb_vec_scalar_mul_2exp_si(t, t, g, -1); + acb_theta_naive_round(a, t, g); + _arb_vec_scalar_mul_2exp_si(a, a, g, 1); + _arb_vec_scalar_mul_2exp_si(t, t, g, 1); + + /* Get r = t - a and v = C.r */ + _arb_vec_sub(r, t, a, g, prec); + arb_mat_vector_mul_col(v, C, r, prec); + + /* new_z is (x - Xa) + iYr; set new_x = x - Xa mod 4, t = Xa */ + arb_mat_vector_mul_col(t, X, a, prec); + _arb_vec_sub(new_x, x, t, g, prec); + _arb_vec_scalar_mul_2exp_si(new_x, new_x, g, -2); + acb_theta_naive_round(new_y, new_x, g); + _arb_vec_sub(new_x, new_x, new_y, g, prec); + _arb_vec_scalar_mul_2exp_si(new_x, new_x, g, 2); + + arb_mat_vector_mul_col(new_y, Y, r, prec); + _acb_vec_set_real_imag(new_z, new_x, new_y, g); + + /* add a^T X a - 2 a^T x + i r^T Y r to c */ + arb_dot(acb_realref(c), acb_realref(c), 0, a, 1, t, 1, g, prec); + _arb_vec_scalar_mul_2exp_si(a, a, g, 1); + arb_dot(acb_realref(c), acb_realref(c), 1, a, 1, x, 1, g, prec); + arb_dot(acb_imagref(c), acb_imagref(c), 0, r, 1, new_y, 1, g, prec); + _arb_vec_scalar_mul_2exp_si(a, a, g, -1); + + acb_exp_pi_i(c, c, prec); + + _arb_vec_clear(x, g); + _arb_vec_clear(y, g); + _arb_vec_clear(t, g); + _arb_vec_clear(r, g); + _arb_vec_clear(new_x, g); + _arb_vec_clear(new_y, g); +} + +void +acb_theta_naive_reduce(arb_ptr v, acb_ptr new_zs, arb_ptr as, acb_ptr cs, + arb_ptr us, acb_srcptr zs, slong nb, const acb_mat_t tau, slong prec) +{ + slong g = acb_mat_nrows(tau); + arb_mat_t X, Y, C, Yinv; + arb_ptr v1; + slong k; + + arb_mat_init(X, g, g); + arb_mat_init(Y, g, g); + arb_mat_init(C, g, g); + arb_mat_init(Yinv, g, g); + v1 = _arb_vec_init(g); + + acb_mat_get_real(X, tau); + acb_mat_get_imag(Y, tau); + acb_siegel_cho(C, tau, prec); + acb_siegel_yinv(Yinv, tau, prec); + + for (k = 0; k < nb; k++) + { + acb_theta_naive_reduce_one(v1, new_zs + k * g, as + k * g, &cs[k], &us[k], + zs + k * g, X, Y, Yinv, C, prec); + if (k == 0) + { + _arb_vec_set(v, v1, g); + } + else + { + _arb_vec_union(v, v, v1, g, prec); + } + } + + arb_mat_clear(X); + arb_mat_clear(Y); + arb_mat_clear(C); + arb_mat_clear(Yinv); + _arb_vec_clear(v1, g); +} diff --git a/src/acb_theta/naive_term.c b/src/acb_theta/naive_term.c new file mode 100644 index 0000000000..3e1fb5cb3f --- /dev/null +++ b/src/acb_theta/naive_term.c @@ -0,0 +1,60 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb_mat.h" +#include "acb_theta.h" + +void +acb_theta_naive_term(acb_t res, acb_srcptr z, const acb_mat_t tau, + const slong * tup, const slong * n, slong prec) +{ + slong g = acb_mat_nrows(tau); + acb_ptr v, w; + acb_t dot; + fmpz_t m, t; + slong k; + + v = _acb_vec_init(g); + w = _acb_vec_init(g); + acb_init(dot); + fmpz_init(m); + fmpz_init(t); + + for (k = 0; k < g; k++) + { + acb_set_si(&v[k], n[k]); + } + + acb_mat_vector_mul_col(w, tau, v, prec); + acb_dot(res, NULL, 0, v, 1, w, 1, g, prec); + acb_dot(dot, NULL, 0, v, 1, z, 1, g, prec); + acb_mul_2exp_si(dot, dot, 1); + acb_add(res, res, dot, prec); + acb_exp_pi_i(res, res, prec); + + if (tup != NULL) + { + fmpz_one(m); + for (k = 0; k < g; k++) + { + fmpz_set_si(t, n[k]); + fmpz_pow_ui(t, t, tup[k]); + fmpz_mul(m, m, t); + } + acb_mul_fmpz(res, res, m, prec); + } + + _acb_vec_clear(v, g); + _acb_vec_clear(w, g); + acb_clear(dot); + fmpz_clear(m); + fmpz_clear(t); +} diff --git a/src/acb_theta/naive_worker.c b/src/acb_theta/naive_worker.c new file mode 100644 index 0000000000..7c334ab5fd --- /dev/null +++ b/src/acb_theta/naive_worker.c @@ -0,0 +1,376 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include +#include "ulong_extras.h" +#include "acb_mat.h" +#include "acb_theta.h" + +static slong +acb_theta_naive_fullprec(const acb_theta_eld_t E, slong prec) +{ + return prec + FLINT_MAX(prec + ceil(n_flog(1 + acb_theta_eld_nb_pts(E), 2)), + ACB_THETA_LOW_PREC); +} + + +ACB_INLINE slong +acb_theta_naive_newprec(slong prec, slong coord, slong dist, slong max_dist, slong ord) +{ + double r = ((double) FLINT_MAX(0, dist - 1)) / (max_dist + 2); + double neg = r * r * prec; + double pos = ord * n_clog(1 + FLINT_ABS(coord), 2); + + return FLINT_MAX(ACB_THETA_LOW_PREC, ceil((double) prec - neg + pos)); +} + +/* Call worker in dimension 1: make vectors to use in acb_dot */ + +static void +acb_theta_naive_call_dim1(acb_ptr th, acb_ptr v1, acb_ptr v2, slong * precs, + const acb_t lin, const acb_t lin_inv, const acb_t cf, acb_srcptr sqr_pow, + const acb_theta_eld_t E, slong ord, slong prec, slong fullprec, + acb_theta_naive_worker_t worker) +{ + slong *coords; + slong g = acb_theta_eld_ambient_dim(E); + slong min = acb_theta_eld_min(E); + slong mid = acb_theta_eld_mid(E); + slong max = acb_theta_eld_max(E); + slong len = acb_theta_eld_nb_pts(E); + slong k; + + coords = flint_malloc(g * sizeof(slong)); + coords[0] = min; + for (k = 1; k < g; k++) + { + coords[k] = acb_theta_eld_coord(E, k); + } + + /* Store lin^k in v1 and square powers in v2; then call worker */ + for (k = mid; k <= max; k++) + { + precs[k - min] = acb_theta_naive_newprec(prec, k, k - mid, max - mid, ord); + if ((k == mid) && (k >= 0)) + { + acb_pow_ui(&v1[mid - min], lin, mid, prec); + } + else if ((k == mid) && (k <= 0)) + { + acb_pow_ui(&v1[mid - min], lin_inv, -mid, prec); + } + else if ((k > FLINT_MAX(2 * mid, 0)) && (k % 2 == 0)) + { + acb_sqr(&v1[k - min], &v1[(k / 2) - min], precs[k - min]); + } + else + { + acb_mul(&v1[k - min], &v1[k - 1 - min], lin, precs[k - min]); + } + acb_set_round(&v2[k - min], &sqr_pow[FLINT_ABS(k)], precs[k - min]); + } + for (k = mid - 1; k >= min; k--) + { + precs[k - min] = acb_theta_naive_newprec(prec, k, k - mid, max - mid, ord); + if ((k < FLINT_MIN(2 * mid, 0)) && (k % 2 == 0)) + { + acb_sqr(&v1[k - min], &v1[(k / 2) - min], precs[k - min]); + } + else + { + acb_mul(&v1[k - min], &v1[k + 1 - min], lin_inv, precs[k - min]); + } + acb_set_round(&v2[k - min], &sqr_pow[FLINT_ABS(k)], precs[k - min]); + } + + worker(th, v1, v2, precs, len, cf, coords, ord, g, prec, fullprec); + + flint_free(coords); +} + +/* Recursive call to smaller dimension; fall back to dim1 when appropriate */ + +static void +acb_theta_naive_worker_rec(acb_ptr th, acb_ptr v1, acb_ptr v2, slong * precs, + acb_mat_t lin_pow, acb_mat_t lin_pow_inv, const acb_t cf, acb_srcptr exp_z, + acb_srcptr exp_z_inv, const acb_mat_t exp_tau, const acb_mat_t exp_tau_inv, + const acb_ptr * sqr_pow, const acb_theta_eld_t E, slong ord, slong prec, + slong fullprec, acb_theta_naive_worker_t worker) +{ + slong d = acb_theta_eld_dim(E); + slong g = acb_theta_eld_ambient_dim(E); + slong nr = acb_theta_eld_nr(E); + slong nl = acb_theta_eld_nl(E); + slong min = acb_theta_eld_min(E); + slong mid = acb_theta_eld_mid(E); + slong max = acb_theta_eld_max(E); + acb_t start_cf, diff_cf, diff_cf_inv, lin_cf, lin_cf_inv, full_cf; + acb_ptr start_lin_pow, start_lin_pow_inv, diff_lin_pow, diff_lin_pow_inv; + slong newprec; + slong k, j, c; + + /* Catch cases: no points in ellipsoid; d=1 */ + if (acb_theta_eld_nb_pts(E) == 0) + { + return; + } + else if (d == 1) + { + acb_init(lin_cf); + acb_init(lin_cf_inv); + + acb_set(lin_cf, &exp_z[0]); + acb_set(lin_cf_inv, &exp_z_inv[0]); + for (k = 1; k < g; k++) + { + acb_mul(lin_cf, lin_cf, acb_mat_entry(lin_pow, 0, k), prec); + acb_mul(lin_cf_inv, lin_cf_inv, acb_mat_entry(lin_pow_inv, 0, k), prec); + } + acb_theta_naive_call_dim1(th, v1, v2, precs, lin_cf, lin_cf_inv, cf, + sqr_pow[0], E, ord, prec, fullprec, worker); + + acb_clear(lin_cf); + acb_clear(lin_cf_inv); + return; + } + + acb_init(start_cf); + acb_init(diff_cf); + acb_init(diff_cf_inv); + acb_init(lin_cf); + acb_init(full_cf); + start_lin_pow = _acb_vec_init(d - 1); + start_lin_pow_inv = _acb_vec_init(d - 1); + diff_lin_pow = _acb_vec_init(d - 1); + diff_lin_pow_inv = _acb_vec_init(d - 1); + + /* Set up things for new cofactor */ + acb_set(diff_cf, &exp_z[d - 1]); + acb_set(diff_cf_inv, &exp_z_inv[d - 1]); + for (k = d; k < g; k++) + { + acb_mul(diff_cf, diff_cf, acb_mat_entry(lin_pow, d - 1, k), prec); + acb_mul(diff_cf_inv, diff_cf_inv, acb_mat_entry(lin_pow_inv, d - 1, k), prec); + } + if (mid >= 0) + { + acb_pow_ui(start_cf, diff_cf, mid, prec); + } + else + { + acb_pow_ui(start_cf, diff_cf_inv, -mid, prec); + } + acb_mul(start_cf, start_cf, cf, prec); + + /* Set up things to update entries (k,d) of lin_pow, k < d */ + for (k = 0; k < d - 1; k++) + { + acb_set(&diff_lin_pow[k], acb_mat_entry(exp_tau, k, d - 1)); + acb_set(&diff_lin_pow_inv[k], acb_mat_entry(exp_tau_inv, k, d - 1)); + if (mid >= 0) + { + acb_pow_ui(&start_lin_pow[k], &diff_lin_pow[k], mid, prec); + acb_pow_ui(&start_lin_pow_inv[k], &diff_lin_pow_inv[k], mid, prec); + } + else + { + acb_pow_ui(&start_lin_pow[k], &diff_lin_pow_inv[k], -mid, prec); + acb_pow_ui(&start_lin_pow_inv[k], &diff_lin_pow[k], -mid, prec); + } + } + + /* Right loop */ + acb_set(lin_cf, start_cf); + for (k = 0; k < d - 1; k++) + { + acb_set(acb_mat_entry(lin_pow, k, d - 1), &start_lin_pow[k]); + acb_set(acb_mat_entry(lin_pow_inv, k, d - 1), &start_lin_pow_inv[k]); + } + for (k = 0; k < nr; k++) + { + c = mid + k; + newprec = acb_theta_naive_newprec(prec, c, c - mid, max - mid, ord); + if (k > 0) /* Update lin_cf, lin_pow using diff */ + { + for (j = 0; j < d - 1; j++) + { + acb_mul(acb_mat_entry(lin_pow, j, d - 1), + acb_mat_entry(lin_pow, j, d - 1), &diff_lin_pow[j], newprec); + acb_mul(acb_mat_entry(lin_pow_inv, j, d - 1), + acb_mat_entry(lin_pow_inv, j, d - 1), &diff_lin_pow_inv[j], newprec); + } + acb_mul(lin_cf, lin_cf, diff_cf, newprec); + } + + acb_mul(full_cf, lin_cf, &sqr_pow[d - 1][FLINT_ABS(c)], newprec); + acb_theta_naive_worker_rec(th, v1, v2, precs, lin_pow, lin_pow_inv, full_cf, + exp_z, exp_z_inv, exp_tau, exp_tau_inv, sqr_pow, acb_theta_eld_rchild(E, k), + ord, newprec, fullprec, worker); + } + + /* Left loop */ + acb_set(lin_cf, start_cf); + for (k = 0; k < d - 1; k++) + { + acb_set(acb_mat_entry(lin_pow, k, d - 1), &start_lin_pow[k]); + acb_set(acb_mat_entry(lin_pow_inv, k, d - 1), &start_lin_pow_inv[k]); + } + for (k = 0; k < nl; k++) + { + c = mid - (k + 1); + newprec = acb_theta_naive_newprec(prec, c, mid - c, mid - min, ord); + for (j = 0; j < d - 1; j++) + { + acb_mul(acb_mat_entry(lin_pow, j, d - 1), + acb_mat_entry(lin_pow, j, d - 1), &diff_lin_pow_inv[j], newprec); + acb_mul(acb_mat_entry(lin_pow_inv, j, d - 1), + acb_mat_entry(lin_pow_inv, j, d - 1), &diff_lin_pow[j], newprec); + } + acb_mul(lin_cf, lin_cf, diff_cf_inv, newprec); + + acb_mul(full_cf, lin_cf, &sqr_pow[d - 1][FLINT_ABS(c)], newprec); + acb_theta_naive_worker_rec(th, v1, v2, precs, lin_pow, lin_pow_inv, full_cf, + exp_z, exp_z_inv, exp_tau, exp_tau_inv, sqr_pow, acb_theta_eld_lchild(E, k), + ord, newprec, fullprec, worker); + } + + acb_clear(start_cf); + acb_clear(diff_cf); + acb_clear(diff_cf_inv); + acb_clear(lin_cf); + acb_clear(full_cf); + _acb_vec_clear(start_lin_pow, d - 1); + _acb_vec_clear(start_lin_pow_inv, d - 1); + _acb_vec_clear(diff_lin_pow, d - 1); + _acb_vec_clear(diff_lin_pow_inv, d - 1); +} + +static void +acb_theta_naive_precompute(acb_mat_t exp_tau, acb_mat_t exp_tau_inv, + acb_ptr * sqr_pow, const acb_mat_t tau, const acb_theta_eld_t E, slong prec) +{ + slong g = acb_mat_nrows(tau); + acb_t c, dc, ddc; + slong k, j; + + acb_init(c); + acb_init(dc); + acb_init(ddc); + + for (k = 0; k < g; k++) + { + for (j = k; j < g; j++) + { + acb_set(c, acb_mat_entry(tau, k, j)); + if (k != j) + { + acb_mul_2exp_si(c, c, 1); + } + acb_exp_pi_i(acb_mat_entry(exp_tau, k, j), c, prec); + acb_inv(acb_mat_entry(exp_tau_inv, k, j), acb_mat_entry(exp_tau, k, j), prec); + } + } + + /* Addition chains do not make a huge difference here. */ + for (k = 0; k < g; k++) + { + acb_one(c); + acb_set(dc, acb_mat_entry(exp_tau, k, k)); + acb_sqr(ddc, dc, prec); + for (j = 0; j <= acb_theta_eld_box(E, k); j++) + { + acb_set(&sqr_pow[k][j], c); + acb_mul(c, c, dc, prec); + acb_mul(dc, dc, ddc, prec); + } + } + + acb_clear(c); + acb_clear(dc); + acb_clear(ddc); +} + +/* User function */ + +void +acb_theta_naive_worker(acb_ptr th, slong len, acb_srcptr zs, slong nb, + const acb_mat_t tau, const acb_theta_eld_t E, slong ord, slong prec, + acb_theta_naive_worker_t worker) +{ + slong g = acb_theta_eld_ambient_dim(E); + slong fullprec = acb_theta_naive_fullprec(E, prec); + slong width = 0; + acb_mat_t exp_tau, exp_tau_inv, lin_pow, lin_pow_inv; + acb_ptr * sqr_pow; + acb_ptr v1, v2, exp_z, exp_z_inv, res; + slong * precs; + acb_t cf; + slong j, k; + + for (j = 0; j < g; j++) + { + width = FLINT_MAX(width, 2 * acb_theta_eld_box(E, j) + 1); + } + + acb_mat_init(exp_tau, g, g); + acb_mat_init(exp_tau_inv, g, g); + acb_mat_init(lin_pow, g, g); + acb_mat_init(lin_pow_inv, g, g); + sqr_pow = flint_malloc(g * sizeof(acb_ptr)); + for (j = 0; j < g; j++) + { + sqr_pow[j] = _acb_vec_init(acb_theta_eld_box(E, j) + 1); + } + v1 = _acb_vec_init(width); + v2 = _acb_vec_init(width); + exp_z = _acb_vec_init(g); + exp_z_inv = _acb_vec_init(g); + res = _acb_vec_init(len * nb); + acb_init(cf); + precs = flint_malloc(width * sizeof(slong)); + + acb_theta_naive_precompute(exp_tau, exp_tau_inv, sqr_pow, tau, E, prec); + acb_one(cf); + + for (j = 0; j < nb; j++) + { + for (k = 0; k < g; k++) + { + acb_mul_2exp_si(&exp_z[k], &zs[j * g + k], 1); + acb_exp_pi_i(&exp_z[k], &exp_z[k], prec); + acb_inv(&exp_z_inv[k], &exp_z[k], prec); + } + acb_mat_set(lin_pow, exp_tau); + acb_mat_set(lin_pow_inv, exp_tau_inv); + + acb_theta_naive_worker_rec(res + j * len, v1, v2, precs, lin_pow, lin_pow_inv, + cf, exp_z, exp_z_inv, exp_tau, exp_tau_inv, sqr_pow, E, ord, + fullprec, fullprec, worker); + } + _acb_vec_set(th, res, len * nb); + + acb_mat_clear(exp_tau); + acb_mat_clear(exp_tau_inv); + acb_mat_clear(lin_pow); + acb_mat_clear(lin_pow_inv); + for (j = 0; j < g; j++) + { + _acb_vec_clear(sqr_pow[j], acb_theta_eld_box(E, j) + 1); + } + flint_free(sqr_pow); + _acb_vec_clear(v1, width); + _acb_vec_clear(v2, width); + _acb_vec_clear(exp_z, g); + _acb_vec_clear(exp_z_inv, g); + _acb_vec_clear(res, len * nb); + acb_clear(cf); + flint_free(precs); +} diff --git a/src/acb_theta/profile/p-all.c b/src/acb_theta/profile/p-all.c new file mode 100644 index 0000000000..14be5d1ac6 --- /dev/null +++ b/src/acb_theta/profile/p-all.c @@ -0,0 +1,113 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include +#include "profiler.h" +#include "acb_mat.h" +#include "acb_theta.h" + +static int usage(char * argv[]) +{ + flint_printf("usage: %s g nb_steps hasz\n", argv[0]); + return 1; +} + +int main(int argc, char * argv[]) +{ + slong prec; + acb_mat_t tau; + acb_ptr th, z, t; + arb_ptr d0, d; + slong g, n, nb_steps, nbz, k; + slong guard = 16; + int hasz; + + if (argc < 4) + { + return usage(argv); + } + + g = atol(argv[1]); + n = 1 << (2 * g); + nb_steps = atol(argv[2]); + hasz = (int) atol(argv[3]); + nbz = (hasz ? 2 : 1); + + acb_mat_init(tau, g, g); + th = _acb_vec_init(n * nbz); + z = _acb_vec_init(g); + t = _acb_vec_init(g); + d0 = _arb_vec_init(1 << g); + d = _arb_vec_init(1 << g); + + acb_mat_onei(tau); + for (k = 0; k < g - 1; k++) + { + acb_onei(acb_mat_entry(tau, k, k + 1)); + acb_mul_2exp_si(acb_mat_entry(tau, k, k + 1), acb_mat_entry(tau, k, k + 1), -2); + acb_set(acb_mat_entry(tau, k + 1, k), acb_mat_entry(tau, k, k + 1)); + } + + prec = 32; + if (hasz) + { + acb_set_si(z, 2); + acb_sqrt(z, z, prec); + } + acb_theta_dist_a0(d0, t, tau, prec); + acb_theta_dist_a0(d, z, tau, prec); + + for (k = 0; k < nb_steps; k++) + { + if (hasz) + { + acb_set_si(z, 2); + acb_sqrt(z, z, prec); + } + + flint_printf("prec = %wd, acb_theta_naive_all:\n", prec); + TIMEIT_START; + acb_theta_naive_all(th, z, 1, tau, prec); + TIMEIT_STOP; + acb_printd(&th[0], 5); + flint_printf("\n"); + + flint_printf("prec = %wd, acb_theta_ql_a0:\n", prec); + TIMEIT_START; + acb_theta_ql_a0(th, t, z, d0, d, tau, guard, prec); + TIMEIT_STOP; + acb_printd(&th[hasz * n], 5); + flint_printf("\n"); + + flint_printf("prec = %wd, acb_theta_ql_all:\n", prec); + TIMEIT_START; + acb_theta_ql_all(th, z, tau, 0, prec); + TIMEIT_STOP; + acb_printd(&th[0], 5); + flint_printf("\n"); + + flint_printf("prec = %wd, acb_theta_all:\n", prec); + TIMEIT_START; + acb_theta_all(th, z, tau, 0, prec); + TIMEIT_STOP; + acb_printd(&th[0], 5); + flint_printf("\n\n"); + + prec *= 2; + } + + acb_mat_clear(tau); + _acb_vec_clear(th, n * nbz); + _arb_vec_clear(d0, 1 << g); + _arb_vec_clear(d, 1 << g); + _acb_vec_clear(z, g); + _acb_vec_clear(t, g); +} diff --git a/src/acb_theta/profile/p-jet_all.c b/src/acb_theta/profile/p-jet_all.c new file mode 100644 index 0000000000..db5341aded --- /dev/null +++ b/src/acb_theta/profile/p-jet_all.c @@ -0,0 +1,58 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "profiler.h" +#include "ulong_extras.h" +#include "acb_mat.h" +#include "acb_theta.h" + +int main(void) +{ + slong prec; + acb_mat_t tau; + acb_ptr dth, z; + slong nb = 3 * 16; + + acb_mat_init(tau, 2, 2); + dth = _acb_vec_init(nb); + z = _acb_vec_init(2); + + acb_onei(acb_mat_entry(tau, 0, 0)); + acb_onei(acb_mat_entry(tau, 1, 1)); + acb_onei(acb_mat_entry(tau, 1, 0)); + acb_mul_2exp_si(acb_mat_entry(tau, 1, 0), acb_mat_entry(tau, 1, 0), -2); + acb_set(acb_mat_entry(tau, 0, 1), acb_mat_entry(tau, 1, 0)); + acb_mat_printd(tau, 5); + + for (prec = 32; prec <= n_pow(2, 15); prec *= 2) + { + flint_printf("prec = %wd, naive:\n", prec); + + TIMEIT_START; + acb_theta_jet_naive_all(dth, z, tau, 1, prec); + TIMEIT_STOP; + + acb_printd(&dth[0], 5); + flint_printf("\n"); + flint_printf("prec = %wd, ql:\n", prec); + + TIMEIT_START; + acb_theta_jet_all(dth, z, tau, 1, prec); + TIMEIT_STOP; + + acb_printd(&dth[0], 5); + flint_printf("\n\n"); + } + + acb_mat_clear(tau); + _acb_vec_clear(dth, nb); + _acb_vec_clear(z, 2); +} diff --git a/src/acb_theta/profile/p-ql_a0.c b/src/acb_theta/profile/p-ql_a0.c new file mode 100644 index 0000000000..10271bdb6e --- /dev/null +++ b/src/acb_theta/profile/p-ql_a0.c @@ -0,0 +1,120 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include +#include +#include "profiler.h" +#include "acb_mat.h" +#include "acb_theta.h" + +static int usage(char * argv[]) +{ + flint_printf("usage: %s g pstep pmax\n", argv[0]); + return 1; +} + +int main(int argc, char * argv[]) +{ + slong iter = 0; + flint_rand_t state; + slong g, n, pstep, pmax, prec; + + if (argc < 4) + { + return usage(argv); + } + + g = atol(argv[1]); + n = 1 << g; + pstep = atol(argv[2]); + pmax = atol(argv[3]); + + flint_randinit(state); + + /* Profile with different number of steps on reduced input */ + for (prec = pstep; prec <= pmax; prec += pstep) + { + int hast = iter % 2; + int hasz = (iter % 4) / 2; + slong nbt = (hast ? 3 : 1); + slong nbz = (hasz ? 2 : 1); + slong guard = 2 * ACB_THETA_LOW_PREC; + slong lp = ACB_THETA_LOW_PREC; + acb_mat_t tau; + acb_ptr z, t, r; + arb_ptr dist, dist0; + arb_t test; + slong k; + int res = 0; + iter++; + + acb_mat_init(tau, g, g); + z = _acb_vec_init(g); + t = _acb_vec_init(g); + r = _acb_vec_init(nbz * nbt * n); + dist = _arb_vec_init(n); + dist0 = _arb_vec_init(n); + arb_init(test); + + while (!res) + { + acb_siegel_randtest_reduced(tau, state, prec, 4); + arb_sub_si(test, acb_imagref(acb_mat_entry(tau, g - 1, g - 1)), 3, prec); + res = arb_is_negative(test); + } + + for (k = 0; k < g; k++) + { + if (hasz) + { + acb_urandom(&z[k], state, prec); + } + if (hast) + { + arb_urandom(acb_realref(&t[k]), state, prec); + } + } + acb_theta_dist_a0(dist, z, tau, lp); + acb_theta_dist_a0(dist0, t, tau, lp); + + flint_printf("g = %wd, prec = %wd, hast = %wd, hasz = %wd, tau:\n", + g, prec, hast, hasz); + acb_mat_printd(tau, 2); + + TIMEIT_START; + res = acb_theta_ql_a0(r, t, z, dist0, dist, tau, guard, prec); + TIMEIT_STOP; + if (res) + { + flint_printf("result (expected rad e-%wd):\n", + (slong) ceil((double) prec * log(2)/log(10))); + acb_printd(&r[0], 5); + flint_printf("\n"); + } + else + { + flint_printf("FAIL\n"); + } + flint_printf("\n"); + + acb_mat_clear(tau); + _acb_vec_clear(z, g); + _acb_vec_clear(t, g); + _acb_vec_clear(r, nbz * nbt * n); + _arb_vec_clear(dist, n); + _arb_vec_clear(dist0, n); + arb_clear(test); + } + + flint_randclear(state); + flint_cleanup(); + return 0; +} diff --git a/src/acb_theta/profile/p-ql_a0_split.c b/src/acb_theta/profile/p-ql_a0_split.c new file mode 100644 index 0000000000..5cf169b1d3 --- /dev/null +++ b/src/acb_theta/profile/p-ql_a0_split.c @@ -0,0 +1,149 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include +#include "profiler.h" +#include "acb_mat.h" +#include "acb_theta.h" + +static int usage(char * argv[]) +{ + flint_printf("usage: %s g prec cstep cmax\n", argv[0]); + return 1; +} + +int main(int argc, char * argv[]) +{ + flint_rand_t state; + slong g, n, prec, c, cstep, cmax; + + if (argc < 5) + { + return usage(argv); + } + + g = atol(argv[1]); + n = 1 << g; + prec = atol(argv[2]); + cstep = atol(argv[3]); + cmax = atol(argv[4]); + + flint_randinit(state); + + /* Profile with different splittings on reduced input */ + for (c = cstep; c <= cmax; c += cstep) + { + slong guard = 2 * ACB_THETA_LOW_PREC; + slong lp = ACB_THETA_LOW_PREC; + acb_mat_t tau, tau1; + arb_mat_t cho; + acb_ptr t, r1, r2, r3; + arb_ptr dist0; + arb_t test; + slong nb_steps_1, nb_steps_2, split; + slong j, k; + int res = 0; + + acb_mat_init(tau, g, g); + acb_mat_init(tau1, g, g); + arb_mat_init(cho, g, g); + t = _acb_vec_init(g); + r1 = _acb_vec_init(n); + r2 = _acb_vec_init(n); + r3 = _acb_vec_init(n); + dist0 = _arb_vec_init(n); + arb_init(test); + + while (!res) + { + acb_siegel_randtest_reduced(tau, state, prec, 4); + arb_sub_si(test, acb_imagref(acb_mat_entry(tau, g - 1, g - 1)), 3, prec); + res = arb_is_negative(test); + } + + for (split = 1; split < g; split++) + { + acb_mat_set(tau1, tau); + for (j = split; j < g; j++) + { + for (k = split; k < g; k++) + { + acb_mul_si(acb_mat_entry(tau1, j, k), acb_mat_entry(tau1, j, k), + c, prec); + } + } + + flint_printf("g = %wd, prec = %wd, c = %wd, split = %wd, matrix:\n", + g, prec, c, split); + acb_mat_printd(tau1, 2); + + acb_theta_dist_a0(dist0, t, tau1, lp); + acb_siegel_cho(cho, tau1, lp); + nb_steps_1 = acb_theta_ql_a0_nb_steps(cho, split, prec); + nb_steps_2 = acb_theta_ql_a0_nb_steps(cho, 0, prec); + + flint_printf("time for split (nb_steps = %wd):\n", nb_steps_1); + TIMEIT_START; + res = acb_theta_ql_a0_steps(r1, t, t, dist0, dist0, tau1, nb_steps_1, + split, guard, prec, &acb_theta_ql_a0); + TIMEIT_STOP; + + if (res) + { + + flint_printf("time for non-split (nb_steps = %wd):\n", nb_steps_2); + TIMEIT_START; + res = acb_theta_ql_a0_steps(r2, t, t, dist0, dist0, tau1, nb_steps_2, + 0, guard, prec, &acb_theta_ql_a0); + TIMEIT_STOP; + } + + if (res) + { + flint_printf("time for ql_a0:\n"); + TIMEIT_START; + res = acb_theta_ql_a0(r3, t, t, dist0, dist0, tau1, guard, prec); + TIMEIT_STOP; + } + + if (res) + { + flint_printf("result for split (expected prec loss %wd):\n", + (guard + g) * nb_steps_1); + acb_printd(&r1[0], 5); + flint_printf("\nresult for non-split (expected prec loss %wd):\n", + (guard + g) * nb_steps_2); + acb_printd(&r2[0], 5); + flint_printf("\nresult for ql_a0:\n"); + acb_printd(&r3[0], 5); + flint_printf("\n\n"); + } + else + { + flint_printf("FAIL\n\n"); + } + } + + acb_mat_clear(tau); + acb_mat_clear(tau1); + arb_mat_clear(cho); + _acb_vec_clear(t, g); + _acb_vec_clear(r1, n); + _acb_vec_clear(r2, n); + _acb_vec_clear(r3, n); + _arb_vec_clear(dist0, n); + arb_clear(test); + } + + flint_randclear(state); + flint_cleanup(); + return 0; +} diff --git a/src/acb_theta/profile/p-ql_a0_steps.c b/src/acb_theta/profile/p-ql_a0_steps.c new file mode 100644 index 0000000000..6649d69073 --- /dev/null +++ b/src/acb_theta/profile/p-ql_a0_steps.c @@ -0,0 +1,133 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include +#include "profiler.h" +#include "arb_mat.h" +#include "acb_mat.h" +#include "acb_theta.h" + +static int usage(char * argv[]) +{ + flint_printf("usage: %s g pstep pmax\n", argv[0]); + return 1; +} + +int main(int argc, char * argv[]) +{ + slong iter = 0; + flint_rand_t state; + slong g, n, pstep, pmax, prec; + + if (argc < 4) + { + return usage(argv); + } + + g = atol(argv[1]); + n = 1 << g; + pstep = atol(argv[2]); + pmax = atol(argv[3]); + + flint_randinit(state); + + /* Profile with different number of steps on reduced input */ + for (prec = pstep; prec <= pmax; prec += pstep) + { + int hast = iter % 2; + int hasz = (iter % 4) / 2; + slong nbt = (hast ? 3 : 1); + slong nbz = (hasz ? 2 : 1); + slong guard = 2 * ACB_THETA_LOW_PREC; + slong lp = ACB_THETA_LOW_PREC; + acb_mat_t tau; + arb_mat_t cho; + acb_ptr z, t, r; + arb_ptr dist, dist0; + arb_t test; + slong nb_steps, split; + slong k; + int res = 0; + iter++; + + acb_mat_init(tau, g, g); + arb_mat_init(cho, g, g); + z = _acb_vec_init(g); + t = _acb_vec_init(g); + r = _acb_vec_init(nbz * nbt * n); + dist = _arb_vec_init(n); + dist0 = _arb_vec_init(n); + arb_init(test); + + while (!res) + { + acb_siegel_randtest_reduced(tau, state, prec, 4); + arb_sub_si(test, acb_imagref(acb_mat_entry(tau, g - 1, g - 1)), 3, prec); + res = arb_is_negative(test); + } + acb_siegel_cho(cho, tau, lp); + + for (k = 0; k < g; k++) + { + if (hasz) + { + acb_urandom(&z[k], state, prec); + } + if (hast) + { + arb_urandom(acb_realref(&t[k]), state, prec); + } + } + acb_theta_dist_a0(dist, z, tau, lp); + acb_theta_dist_a0(dist0, t, tau, lp); + + split = 0; + nb_steps = acb_theta_ql_a0_nb_steps(cho, 0, prec); + + flint_printf("(g = %wd, prec = %wd, hast = %wd, hasz = %wd) ideal nb_steps: %wd, tau:\n", + g, prec, hast, hasz, nb_steps); + acb_mat_printd(tau, 2); + + for (k = -FLINT_MIN(nb_steps, 2); k <= 2; k++) + { + flint_printf("nb_steps = %wd: ", nb_steps + k); + TIMEIT_START; + res = acb_theta_ql_a0_steps(r, t, z, dist0, dist, tau, nb_steps + k, split, + guard, prec, &acb_theta_ql_a0_naive); + TIMEIT_STOP; + if (res) + { + flint_printf("result (expected prec loss %wd):\n", + (guard + g) * (nb_steps + k)); + acb_printd(&r[0], 5); + flint_printf("\n"); + } + else + { + flint_printf("FAIL\n"); + } + } + flint_printf("\n"); + + acb_mat_clear(tau); + arb_mat_clear(cho); + _acb_vec_clear(z, g); + _acb_vec_clear(t, g); + _acb_vec_clear(r, nbz * nbt * n); + _arb_vec_clear(dist, n); + _arb_vec_clear(dist0, n); + arb_clear(test); + } + + flint_randclear(state); + flint_cleanup(); + return 0; +} diff --git a/src/acb_theta/profile/p-siegel_reduce.c b/src/acb_theta/profile/p-siegel_reduce.c new file mode 100644 index 0000000000..9127824241 --- /dev/null +++ b/src/acb_theta/profile/p-siegel_reduce.c @@ -0,0 +1,87 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include +#include "profiler.h" +#include "acb_mat.h" +#include "acb_theta.h" + +static int usage(char * argv[]) +{ + flint_printf("usage: %s g pstep pmax dstep dmax\n", argv[0]); + return 1; +} + +int main(int argc, char * argv[]) +{ + slong g; + slong prec, pmax, pstep; + slong d, dmax, dstep; + flint_rand_t state; + acb_mat_t tau, w; + arb_t r; + fmpz_mat_t mat; + slong j, k; + + if (argc < 6) + { + return usage(argv); + } + + g = atol(argv[1]); + pstep = atol(argv[2]); + pmax = atol(argv[3]); + dstep = atol(argv[4]); + dmax = atol(argv[5]); + + flint_randinit(state); + acb_mat_init(tau, g, g); + acb_mat_init(w, g, g); + arb_init(r); + fmpz_mat_init(mat, 2 * g, 2 * g); + + acb_siegel_randtest_reduced(tau, state, pmax, 2); + flint_printf("Starting matrix:\n"); + acb_mat_printd(tau, 5); + + for (prec = pstep; prec <= pmax; prec += pstep) + { + for (d = dstep; d <= dmax; d += dstep) + { + acb_mat_scalar_div_si(w, tau, d, prec); + for (j = 0; j < g; j++) + { + for (k = 0; k <= j; k++) + { + arb_urandom(r, state, prec); + acb_add_arb(acb_mat_entry(w, j, k), acb_mat_entry(w, j, k), + r, prec); + acb_set(acb_mat_entry(w, k, j), acb_mat_entry(w, j, k)); + } + } + + flint_printf("prec = %wd, d = %wd\n", prec, d); + + TIMEIT_START; + acb_siegel_reduce(mat, w, prec); + TIMEIT_STOP; + } + } + + flint_randclear(state); + acb_mat_clear(tau); + acb_mat_clear(w); + arb_clear(r); + fmpz_mat_clear(mat); + + flint_cleanup(); + return 0; +} diff --git a/src/acb_theta/ql_a0.c b/src/acb_theta/ql_a0.c new file mode 100644 index 0000000000..7414ea162e --- /dev/null +++ b/src/acb_theta/ql_a0.c @@ -0,0 +1,142 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "arb_mat.h" +#include "acb_mat.h" +#include "acb_theta.h" + +static slong +acb_theta_ql_split(const arb_mat_t cho) +{ + slong g = arb_mat_nrows(cho); + arb_t cmp; + slong k; + + arb_init(cmp); + + for (k = g - 1; k >= 1; k--) + { + arb_mul_2exp_si(cmp, arb_mat_entry(cho, k - 1, k - 1), + FLINT_MAX(1, 6 + k - 2 * g)); + if (arb_lt(cmp, arb_mat_entry(cho, k, k))) + { + break; + } + } + + arb_clear(cmp); + return k; +} + +int +acb_theta_ql_a0(acb_ptr r, acb_srcptr t, acb_srcptr z, arb_srcptr dist0, + arb_srcptr dist, const acb_mat_t tau, slong guard, slong prec) +{ + slong g = acb_mat_nrows(tau); + slong n = 1 << g; + int has_t = !_acb_vec_is_zero(t, g); + int has_z = !_acb_vec_is_zero(z, g); + slong nbt = (has_t ? 3 : 1); + slong nbz = (has_z ? 2 : 1); + slong nb_der = acb_theta_jet_nb(2, g); + arb_mat_t cho; + slong split, nb_steps, padding, lp; + acb_mat_t tau_mid; + acb_ptr t_mid, z_mid, dth; + arb_t err; + arf_t e; + slong k, j, a; + int res; + + arb_mat_init(cho, g, g); + acb_mat_init(tau_mid, g, g); + t_mid = _acb_vec_init(g); + z_mid = _acb_vec_init(g); + dth = _acb_vec_init(nb_der); + arb_init(err); + arf_init(e); + + acb_siegel_cho(cho, tau, ACB_THETA_LOW_PREC); + split = acb_theta_ql_split(cho); + nb_steps = acb_theta_ql_a0_nb_steps(cho, split, prec); + if (has_t || has_z) + { + nb_steps += 1; /* cf p-ql_a0_steps */ + } + padding = nb_steps * (guard + g); + arf_one(e); + arf_mul_2exp_si(e, e, -prec - padding); + + /* Expect precision loss of (guard + g) * nb_steps, so call ql_a0_steps at midpoint */ + acb_mat_get_mid(tau_mid, tau); + for (k = 0; k < g; k++) + { + for (j = 0; j <= k; j++) + { + acb_add_error_arf(acb_mat_entry(tau_mid, k, j), e); + acb_set(acb_mat_entry(tau_mid, j, k), acb_mat_entry(tau_mid, k, j)); + } + } + for (k = 0; k < g; k++) + { + acb_get_mid(&z_mid[k], &z[k]); + acb_get_mid(&t_mid[k], &t[k]); + if (has_z) + { + acb_add_error_arf(&z_mid[k], e); + } + if (has_t) + { + arb_add_error_arf(acb_realref(&t_mid[k]), e); + } + } + + res = acb_theta_ql_a0_steps(r, t_mid, z_mid, dist0, dist, tau_mid, nb_steps, + split, guard, prec + padding, &acb_theta_ql_a0); + + /* Add error, using z_mid as temp */ + for (k = 0; (k < nbz * nbt) && res; k++) + { + _acb_vec_zero(z_mid, g); + if (has_t) + { + _acb_vec_scalar_mul_ui(t_mid, t, g, k % 3, prec); + _acb_vec_add(z_mid, z_mid, t_mid, g, prec); + } + if (has_z && (k >= nbt)) + { + _acb_vec_add(z_mid, z_mid, z, g, prec); + } + for (a = 0; a < n; a++) + { + if (has_z && (k >= nbt)) + { + lp = FLINT_MAX(ACB_THETA_LOW_PREC, acb_theta_dist_addprec(&dist[a])); + } + else + { + lp = FLINT_MAX(ACB_THETA_LOW_PREC, acb_theta_dist_addprec(&dist0[a])); + } + acb_theta_jet_naive_fixed_ab(dth, a << g, z_mid, tau, 2, lp); + acb_theta_jet_error_bounds(err, z_mid, tau, dth, 0, lp); + acb_add_error_arb(&r[k * n + a], err); + } + } + + arb_mat_clear(cho); + acb_mat_clear(tau_mid); + _acb_vec_clear(t_mid, g); + _acb_vec_clear(z_mid, g); + _acb_vec_clear(dth, nb_der); + arb_clear(err); + arf_clear(e); + return res; +} diff --git a/src/acb_theta/ql_a0_naive.c b/src/acb_theta/ql_a0_naive.c new file mode 100644 index 0000000000..d510902ec2 --- /dev/null +++ b/src/acb_theta/ql_a0_naive.c @@ -0,0 +1,158 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb_mat.h" +#include "acb_modular.h" +#include "acb_theta.h" + +static int +acb_theta_ql_a0_naive_gen(acb_ptr th, acb_srcptr t, acb_srcptr z, arb_srcptr d0, + arb_srcptr d, const acb_mat_t tau, slong guard, slong prec) +{ + slong g = acb_mat_nrows(tau); + slong n = 1 << g; + int hast = !_acb_vec_is_zero(t, g); + int hasz = !_acb_vec_is_zero(z, g); + slong nbt = (hast ? 3 : 1); + slong nbz = (hasz ? 2 : 1); + acb_ptr x, aux; + slong j, k; + int res; + + x = _acb_vec_init(g * nbt); + aux = _acb_vec_init(nbt); + + for (k = 0; k < nbt; k++) + { + _acb_vec_scalar_mul_ui(x + k * g, t, g, k, prec); + } + for (k = 0; k < n; k++) + { + acb_theta_naive_fixed_ab(aux, k << g, x, nbt, tau, + prec + acb_theta_dist_addprec(&d0[k])); + for (j = 0; j < nbt; j++) + { + acb_set(&th[j * n + k], &aux[j]); + } + } + + if (hasz) + { + for (k = 0; k < nbt; k++) + { + _acb_vec_add(x + k * g, x + k * g, z, g, prec); + } + for (k = 0; k < n; k++) + { + acb_theta_naive_fixed_ab(aux, k << g, x, nbt, tau, + prec + acb_theta_dist_addprec(&d[k])); + for (j = 0; j < nbt; j++) + { + acb_set(&th[nbt * n + j * n + k], &aux[j]); + } + } + } + res = _acb_vec_is_finite(th, n * nbz * nbt); + + _acb_vec_clear(x, g * nbt); + _acb_vec_clear(aux, nbt); + return res; +} + +/* when g = 1, we don't go as far near the cusp and computing exponentials is + relatively more expensive */ +static int +acb_theta_ql_a0_naive_g1(acb_ptr th, acb_srcptr t, acb_srcptr z, arb_srcptr d0, + arb_srcptr d, const acb_mat_t tau, slong guard, slong prec) +{ + int hast = !acb_is_zero(t); + int hasz = !acb_is_zero(z); + slong nbt = (hast ? 3 : 1); + slong nbz = (hasz ? 2 : 1); + acb_t q4, q, u, v, w, t3, t1; + slong k; + int res, w_is_unit; + + acb_init(q4); + acb_init(q); + acb_init(u); + acb_init(v); + acb_init(w); + acb_init(t3); + acb_init(t1); + + for (k = 0; k < 2; k++) + { + prec = prec + FLINT_MAX(0, acb_theta_dist_addprec(&d[k])); + prec = prec + FLINT_MAX(0, acb_theta_dist_addprec(&d0[k])); + } + + /* compute q_{1/4}, q */ + acb_mul_2exp_si(q4, acb_mat_entry(tau, 0, 0), -2); + acb_exp_pi_i(q4, q4, prec); + acb_pow_ui(q, q4, 4, prec); + + /* compute v, w */ + acb_exp_pi_i(v, t, prec); + acb_exp_pi_i(w, z, prec); + w_is_unit = arb_is_zero(acb_imagref(z)); + + acb_one(u); + for (k = 0; k < nbt; k++) + { + if (k > 0) + { + acb_mul(u, u, v, prec); + } + acb_modular_theta_sum(t3, &th[2 * k + 1], &th[2 * k], t1, + u, 1, q, 1, prec); + acb_mul(&th[2 * k + 1], &th[2 * k + 1], q4, prec); + } + + if (hasz) + { + acb_set(u, w); + for (k = 0; k < nbt; k++) + { + if (k > 0) + { + acb_mul(u, u, v, prec); + } + acb_modular_theta_sum(t3, &th[2 * nbt + 2 * k + 1], &th[2 * nbt + 2 * k], t1, + u, w_is_unit, q, 1, prec); + acb_mul(&th[2 * nbt + 2 * k + 1], &th[2 * nbt + 2 * k + 1], q4, prec); + } + } + res = _acb_vec_is_finite(th, 2 * nbz * nbt); + + acb_clear(q4); + acb_clear(q); + acb_clear(u); + acb_clear(v); + acb_clear(w); + acb_clear(t3); + acb_clear(t1); + return res; +} + +int acb_theta_ql_a0_naive(acb_ptr th, acb_srcptr t, acb_srcptr z, arb_srcptr d0, + arb_srcptr d, const acb_mat_t tau, slong guard, slong prec) +{ + slong g = acb_mat_nrows(tau); + if (g == 1) + { + return acb_theta_ql_a0_naive_g1(th, t, z, d0, d, tau, guard, prec); + } + else + { + return acb_theta_ql_a0_naive_gen(th, t, z, d0, d, tau, guard, prec); + } +} diff --git a/src/acb_theta/ql_a0_nb_steps.c b/src/acb_theta/ql_a0_nb_steps.c new file mode 100644 index 0000000000..f6e511f8a3 --- /dev/null +++ b/src/acb_theta/ql_a0_nb_steps.c @@ -0,0 +1,67 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "arb_mat.h" +#include "acb_theta.h" + +slong +acb_theta_ql_a0_nb_steps(const arb_mat_t C, slong s, slong prec) +{ + slong g = arb_mat_nrows(C); + slong lp = ACB_THETA_LOW_PREC; + arb_t x, t; + slong res; + + FLINT_ASSERT(s >= 0 && s < g); + + arb_init(x); + arb_init(t); + + arb_sqr(x, arb_mat_entry(C, s, s), lp); + arb_const_log2(t, lp); + arb_div(x, x, t, lp); + arb_div_si(x, x, prec, lp); + arb_log(x, x, lp); + arb_div(x, x, t, lp); + + if (!arb_is_finite(x) || arf_cmpabs_2exp_si(arb_midref(x), FLINT_BITS - 4) > 0) + { + arb_clear(x); + arb_clear(t); + return 0; + } + + res = -arf_get_si(arb_midref(x), ARF_RND_NEAR); + if (s == 0) + { + if (g == 1) + { + res -= 7; + } + else if (g == 2) + { + res -= 3; + } + else if (g <= 5) + { + res -= 1; + } + } + else + { + res += 1; + } + res = FLINT_MAX(0, res); + + arb_clear(x); + arb_clear(t); + return res; +} diff --git a/src/acb_theta/ql_a0_split.c b/src/acb_theta/ql_a0_split.c new file mode 100644 index 0000000000..e93df0f27b --- /dev/null +++ b/src/acb_theta/ql_a0_split.c @@ -0,0 +1,242 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "arb_mat.h" +#include "acb_mat.h" +#include "acb_theta.h" + +static int +acb_theta_ql_a0_eld_points(slong ** pts, slong * nb_pts, arb_ptr v, + slong * fullprec, arf_t eps, arb_srcptr d, ulong a, arb_srcptr w, + const arb_mat_t C, const arb_mat_t C1, slong prec) +{ + slong g = arb_mat_nrows(C); + slong s = g - arb_mat_nrows(C1); + slong n = 1 << g; + slong nba = 1 << (g - s); + slong lp = ACB_THETA_LOW_PREC; + arb_t max_d; + arf_t R2; + acb_theta_eld_t E; + slong k; + int res; + + acb_theta_eld_init(E, g - s, g - s); + arf_init(R2); + arb_init(max_d); + + /* Get offset */ + acb_theta_char_get_arb(v, a, g - s); + _arb_vec_add(v, v, w + s, g - s, prec); + arb_mat_vector_mul_col(v, C1, v, prec); + + /* Get R2 */ + arb_zero(max_d); + for (k = a; k < n; k += nba) + { + arb_max(max_d, max_d, &d[k], lp); + } + *fullprec = prec + acb_theta_dist_addprec(max_d); + acb_theta_naive_radius(R2, eps, C, 0, *fullprec); + + /* List points in ellipsoid */ + res = acb_theta_eld_set(E, C1, R2, v); + if (res) + { + *nb_pts = acb_theta_eld_nb_pts(E); + *pts = flint_malloc(acb_theta_eld_nb_pts(E) * (g - s) * sizeof(slong)); + acb_theta_eld_points(*pts, E); + } + else + { + *nb_pts = 0; + *pts = flint_malloc(0); + } + + acb_theta_eld_clear(E); + arf_clear(R2); + arb_init(max_d); + return res; +} + +static int +acb_theta_ql_a0_split_term(acb_ptr th, slong * pt, ulong a, acb_srcptr t, acb_srcptr z, + arb_srcptr v, arb_srcptr d, arb_srcptr new_d0, const acb_mat_t tau0, + const acb_mat_t star, const acb_mat_t tau1, const arb_mat_t C1, slong guard, + slong prec, slong fullprec, acb_theta_ql_worker_t worker) +{ + slong s = acb_mat_nrows(tau0); + slong g = s + acb_mat_nrows(tau1); + slong lp = ACB_THETA_LOW_PREC; + slong n = 1 << g; + slong nba = 1 << (g - s); + slong nbth = 1 << s; + slong nbt = (_acb_vec_is_zero(t, g) ? 1 : 3); + slong new_prec; + acb_ptr u, w, new_z, new_th; + acb_t f, c; + arb_ptr new_d; + arb_t orth, x; + slong j, k; + int res; + + u = _acb_vec_init(g - s); + w = _acb_vec_init(g - s); + new_z = _acb_vec_init(s); + new_th = _acb_vec_init(2 * nbth * nbt); + new_d = _arb_vec_init(nbth); + acb_init(f); + acb_init(c); + arb_init(orth); + arb_init(x); + + /* Set u to pt + a1/2 */ + acb_theta_char_get_acb(u, a, g - s); + for (j = 0; j < g - s; j++) + { + acb_add_si(&u[j], &u[j], pt[j], prec); + } + + /* Get new_z and cofactor at 0 */ + acb_mat_vector_mul_col(new_z, star, u, prec); + _acb_vec_add(new_z, new_z, z, s, prec); + acb_dot(f, NULL, 0, u, 1, z + s, 1, g - s, prec); + acb_mul_2exp_si(f, f, 1); + acb_mat_vector_mul_col(w, tau1, u, prec); + acb_dot(f, f, 0, w, 1, u, 1, g - s, prec); + + /* Get new distances and relative precision */ + acb_theta_dist_a0(new_d, new_z, tau0, lp); + acb_theta_dist_pt(orth, v, C1, pt, lp); + new_prec = prec; + for (j = 0; j < nbth; j++) + { + arb_sub(x, &d[a + j * nba], orth, lp); + arb_sub(x, x, &new_d[j], lp); + new_prec = FLINT_MIN(new_prec, prec + acb_theta_dist_addprec(x)); + } + new_prec = FLINT_MAX(new_prec, lp); + + /* Call worker */ + res = worker(new_th, t, new_z, new_d0, new_d, tau0, guard, new_prec); + + if (!_acb_vec_is_zero(new_z, s)) + { + /* We are only interested in the values at z */ + _acb_vec_set(new_th, new_th + nbth * nbt, nbth * nbt); + } + + /* Rescale to set th; cofactor depends on t */ + for (k = 0; k < nbt; k++) + { + acb_dot(c, NULL, 0, u, 1, t + s, 1, g - s, prec); + acb_mul_si(c, c, 2 * k, prec); + acb_add(c, c, f, prec); + acb_exp_pi_i(c, c, prec); + _acb_vec_scalar_mul(new_th + k * nbth, new_th + k * nbth, + nbth, c, prec); + + for (j = 0; j < nbth; j++) + { + acb_add(&th[k * n + j * nba + a], &th[k * n + j * nba + a], + &new_th[k * nbth + j], fullprec); + } + } + + _acb_vec_clear(u, g - s); + _acb_vec_clear(w, g - s); + _acb_vec_clear(new_z, s); + _acb_vec_clear(new_th, 2 * nbth * nbt); + _arb_vec_clear(new_d, nbth); + acb_clear(f); + acb_clear(c); + arb_clear(orth); + arb_clear(x); + return res; +} + +int +acb_theta_ql_a0_split(acb_ptr th, acb_srcptr t, acb_srcptr z, arb_srcptr d, + const acb_mat_t tau, slong s, slong guard, slong prec, acb_theta_ql_worker_t worker) +{ + slong g = acb_mat_nrows(tau); + slong n = 1 << g; + slong nba = 1 << (g - s); + slong nbth = 1 << s; + slong nbt = (_acb_vec_is_zero(t, g) ? 1 : 3); + slong lp = ACB_THETA_LOW_PREC; + arb_mat_t C, C1, Yinv; + acb_mat_t tau0, star, tau1; + arb_ptr v, w, new_d0; + arf_t eps; + slong * pts; + slong fullprec, nb_pts; + slong a, j, k; + int res = 1; + + FLINT_ASSERT(s >= 1 && s < g); + + arb_mat_init(C, g, g); + arb_mat_init(Yinv, g, g); + acb_mat_window_init(tau0, tau, 0, 0, s, s); + acb_mat_window_init(star, tau, 0, s, s, g); + acb_mat_window_init(tau1, tau, s, s, g, g); + v = _arb_vec_init(g - s); + w = _arb_vec_init(g); + new_d0 = _arb_vec_init(nbth); + arf_init(eps); + + acb_siegel_yinv(Yinv, tau, prec); + acb_siegel_cho(C, tau, prec); + arb_mat_window_init(C1, C, s, s, g, g); + + acb_theta_dist_a0(new_d0, z, tau0, lp); + _acb_vec_get_imag(w, z, g); + arb_mat_vector_mul_col(w, Yinv, w, prec); + + _acb_vec_zero(th, n * nbt); + for (a = 0; (a < nba) && res; a++) + { + /* Get offset, fullprec, error and list of points in ellipsoid */ + res = acb_theta_ql_a0_eld_points(&pts, &nb_pts, v, &fullprec, eps, + d, a, w, C, C1, prec); + + /* Sum terms at each point using worker */ + for (k = 0; (k < nb_pts) && res; k++) + { + res = acb_theta_ql_a0_split_term(th, pts + k * (g - s), a, t, z, + v, d, new_d0, tau0, star, tau1, C1, guard, prec, fullprec, worker); + } + + /* Add error */ + for (k = 0; k < nbth; k++) + { + for (j = 0; j < nbt; j++) + { + acb_add_error_arf(&th[j * n + k * nba + a], eps); + } + } + + flint_free(pts); + } + + arb_mat_clear(C); + arb_mat_window_clear(C1); + arb_mat_clear(Yinv); + acb_mat_window_clear(tau0); + acb_mat_window_clear(star); + acb_mat_window_clear(tau1); + _arb_vec_clear(v, g - s); + _arb_vec_clear(w, g); + _arb_vec_clear(new_d0, nbth); + arf_clear(eps); + return res; +} diff --git a/src/acb_theta/ql_a0_steps.c b/src/acb_theta/ql_a0_steps.c new file mode 100644 index 0000000000..20b62d2b8a --- /dev/null +++ b/src/acb_theta/ql_a0_steps.c @@ -0,0 +1,377 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "arb_mat.h" +#include "acb_mat.h" +#include "acb_theta.h" + +static int +acb_theta_ql_roots_1(acb_ptr rts, acb_srcptr z, arb_srcptr d, + const arb_t f, const acb_mat_t tau, slong nb_steps, slong prec) +{ + slong g = acb_mat_nrows(tau); + slong n = 1 << g; + acb_mat_t w; + acb_ptr x; + arb_t c, h; + slong hprec, guard; + slong k, a; + int res = 1; + + acb_mat_init(w, g, g); + x = _acb_vec_init(g); + arb_init(c); + arb_init(h); + + for (k = 0; (k < nb_steps) && res; k++) + { + acb_mat_scalar_mul_2exp_si(w, tau, k); + _acb_vec_scalar_mul_2exp_si(x, z, g, k); + arb_mul_2exp_si(c, f, k); + arb_exp(c, c, prec); + + for (a = 0; (a < n) && res; a++) + { + arb_mul_2exp_si(h, &d[a], k); + res = 0; + for (guard = 16; (guard <= prec) && !res; guard += 16) + { + hprec = guard + acb_theta_dist_addprec(h); + acb_theta_naive_fixed_ab(&rts[k * n + a], a << g, x, 1, w, hprec); + if (acb_is_finite(&rts[k * n + a]) && !acb_contains_zero(&rts[k * n + a])) + { + res = 1; + } + } + } + + _acb_vec_scalar_mul_arb(rts + k * n, rts + k * n, n, c, prec); + } + + acb_mat_clear(w); + _acb_vec_clear(x, g); + arb_clear(c); + arb_clear(h); + return res; +} + +static int +acb_theta_ql_roots_3(acb_ptr rts, acb_srcptr t, acb_srcptr z, arb_srcptr d, + const acb_mat_t tau, slong nb_steps, slong guard, slong prec) +{ + slong g = acb_mat_nrows(tau); + slong n = 1 << g; + int has_t = !_acb_vec_is_zero(t, g); + arb_mat_t Yinv; + acb_ptr x; + arb_ptr y, w; + arb_t f, pi; + slong k; + int res = 1; + + arb_mat_init(Yinv, g, g); + x = _acb_vec_init(g); + y = _arb_vec_init(g); + w = _arb_vec_init(g); + arb_init(f); + arb_init(pi); + + acb_siegel_yinv(Yinv, tau, prec); + _acb_vec_get_imag(y, z, g); + arb_mat_vector_mul_col(w, Yinv, y, prec); + arb_dot(f, NULL, 1, y, 1, w, 1, g, prec); + arb_const_pi(pi, prec); + arb_mul(f, f, pi, prec); + + if (!has_t) + { + res = acb_theta_ql_roots_1(rts, z, d, f, tau, nb_steps, guard); + } + else + { + for (k = 1; (k < 3) && res; k++) + { + _acb_vec_scalar_mul_ui(x, t, g, k, prec); + _acb_vec_add(x, x, z, g, prec); + res = acb_theta_ql_roots_1(rts + (k - 1) * nb_steps * n, x, d, + f, tau, nb_steps, guard); + } + } + + arb_mat_clear(Yinv); + _acb_vec_clear(x, g); + _arb_vec_clear(y, g); + _arb_vec_clear(w, g); + arb_clear(f); + arb_clear(pi); + return res; +} + +static int +acb_theta_ql_roots(acb_ptr rts, acb_srcptr t, acb_srcptr z, arb_srcptr d0, + arb_srcptr d, const acb_mat_t tau, slong nb_steps, slong guard, slong prec) +{ + slong g = acb_mat_nrows(tau); + slong n = 1 << g; + int hasz = !_acb_vec_is_zero(z, g); + int hast = !_acb_vec_is_zero(t, g); + slong nbt = (hast ? 2 : 1); + acb_ptr x; + int res; + + x = _acb_vec_init(g); + + res = acb_theta_ql_roots_3(rts, t, x, d0, tau, nb_steps, guard, prec); + if (res && hasz) + { + res = acb_theta_ql_roots_3(rts + nbt * n * nb_steps, t, z, d, tau, + nb_steps, guard, prec); + } + + _acb_vec_clear(x, g); + return res; +} + +static int +acb_theta_ql_a0_start(acb_ptr th, acb_srcptr t, acb_srcptr z, arb_srcptr d0, + arb_srcptr d, const arb_t f, const acb_mat_t tau, slong nb_steps, slong s, + slong guard, slong prec, acb_theta_ql_worker_t worker) +{ + slong g = acb_mat_nrows(tau); + slong n = 1 << g; + int hast = !_acb_vec_is_zero(t, g); + int hasz = !_acb_vec_is_zero(z, g); + slong nbt = (hast ? 3 : 1); + acb_mat_t w; + acb_ptr x, u, zero; + arb_ptr new_d0, new_d; + arb_t c; + int res; + + acb_mat_init(w, g, g); + x = _acb_vec_init(g); + u = _acb_vec_init(g); + zero = _acb_vec_init(g); + new_d0 = _arb_vec_init(n); + new_d = _arb_vec_init(n); + arb_init(c); + + acb_mat_scalar_mul_2exp_si(w, tau, nb_steps); + _acb_vec_scalar_mul_2exp_si(u, t, g, nb_steps); + _acb_vec_scalar_mul_2exp_si(x, z, g, nb_steps); + _arb_vec_scalar_mul_2exp_si(new_d0, d0, n, nb_steps); + _arb_vec_scalar_mul_2exp_si(new_d, d, n, nb_steps); + arb_mul_2exp_si(c, f, nb_steps); + arb_exp(c, c, prec); + + if (s > 0) + { + res = acb_theta_ql_a0_split(th, u, zero, new_d0, w, s, guard, prec, worker); + if (res && hasz) + { + res = acb_theta_ql_a0_split(th + nbt * n, u, x, new_d, w, s, guard, prec, worker); + } + } + else + { + res = acb_theta_ql_a0_naive(th, u, x, new_d0, new_d, w, guard, prec); + } + + if (hasz) + { + _acb_vec_scalar_mul_arb(th + nbt * n, th + nbt * n, nbt * n, c, prec); + } + + acb_mat_clear(w); + _acb_vec_clear(x, g); + _acb_vec_clear(u, g); + _acb_vec_clear(zero, g); + _arb_vec_clear(new_d0, n); + _arb_vec_clear(new_d, n); + arb_clear(c); + return res; +} + +static void +acb_theta_ql_step_1(acb_ptr res, acb_srcptr th0, acb_srcptr th, acb_srcptr rts, + arb_srcptr d0, arb_srcptr d, slong g, slong prec) +{ + slong n = 1 << g; + + acb_theta_agm_mul_tight(res, th0, th, d0, d, g, prec); + _acb_vec_scalar_mul_2exp_si(res, res, n, g); + acb_theta_agm_sqrt(res, res, rts, n, prec); +} + +static void +acb_theta_ql_step_2(acb_ptr res, acb_srcptr th0, acb_srcptr th, acb_srcptr rts, + arb_srcptr d0, arb_srcptr d, slong g, slong prec) +{ + slong n = 1 << g; + acb_ptr aux; + + aux = _acb_vec_init(3 * n); + + acb_theta_agm_mul_tight(aux + n, th0, th + n, d0, d, g, prec); + acb_theta_agm_mul_tight(aux + 2 * n, th0, th + 2 * n, d0, d, g, prec); + _acb_vec_scalar_mul_2exp_si(aux + n, aux + n, 2 * n, g); + acb_theta_agm_sqrt(aux + n, aux + n, rts, 2 * n, prec); + + _acb_vec_set(res, aux, 3 * n); + _acb_vec_clear(aux, 3 * n); +} + + +static void +acb_theta_ql_step_3(acb_ptr res, acb_srcptr th0, acb_srcptr th, acb_srcptr rts, + arb_srcptr d0, arb_srcptr d, slong g, slong prec) +{ + slong n = 1 << g; + acb_ptr aux; + ulong a; + + aux = _acb_vec_init(3 * n); + + acb_theta_agm_mul_tight(aux + n, th0, th + n, d0, d, g, prec); + acb_theta_agm_mul_tight(aux + 2 * n, th0, th + 2 * n, d0, d, g, prec); + _acb_vec_scalar_mul_2exp_si(aux + n, aux + n, 2 * n, g); + acb_theta_agm_sqrt(aux + n, aux + n, rts, 2 * n, prec); + + acb_theta_agm_mul_tight(aux, th0 + n, th + n, d0, d, g, prec); + _acb_vec_scalar_mul_2exp_si(aux, aux, n, g); + for (a = 0; a < n; a++) + { + acb_div(&aux[a], &aux[a], &aux[2 * n + a], prec); + } + + _acb_vec_set(res, aux, 3 * n); + _acb_vec_clear(aux, 3 * n); +} + +static void +acb_theta_ql_a0_step(acb_ptr th, acb_srcptr all_rts, arb_srcptr d0, arb_srcptr d, + slong k, slong nb_steps, int hast, int hasz, slong g, slong prec) +{ + slong n = 1 << g; + arb_ptr new_d, new_d0; + acb_ptr next; + acb_ptr rts; + slong nbt = (hast ? 3 : 1); + slong nbr = (hast ? 2 : 1); + slong nbz = (hasz ? 2 : 1); + slong j; + + new_d = _arb_vec_init(n); + new_d0 = _arb_vec_init(n); + next = _acb_vec_init(nbz * nbt * n); + rts = _acb_vec_init(nbr * nbz * n); + + _arb_vec_scalar_mul_2exp_si(new_d, d, n, k + 1); + _arb_vec_scalar_mul_2exp_si(new_d0, d0, n, k + 1); + for (j = 0; j < nbz * nbr; j++) + { + _acb_vec_set(rts + j * n, all_rts + j * nb_steps * n + k * n, n); + } + + if (hast) + { + acb_theta_ql_step_3(next, th, th, rts, new_d0, new_d0, g, prec); + if (hasz && (k == 0)) + { + acb_theta_ql_step_3(next + nbt * n, th, th + nbt * n, + rts + nbr * n, new_d0, new_d, g, prec); + } + else if (hasz) + { + acb_theta_ql_step_2(next + nbt * n, th, th + nbt * n, + rts + nbr * n, new_d0, new_d, g, prec); + } + } + else + { + acb_theta_ql_step_1(next, th, th, rts, new_d0, new_d0, g, prec); + if (hasz) + { + acb_theta_ql_step_1(next + nbt * n, th, th + nbt * n, + rts + nbr * n, new_d0, new_d, g, prec); + } + } + _acb_vec_set(th, next, nbz * nbt * n); + + _arb_vec_clear(new_d, n); + _arb_vec_clear(new_d0, n); + _acb_vec_clear(next, nbz * nbt * n); + _acb_vec_clear(rts, nbr * nbz * n); +} + +int +acb_theta_ql_a0_steps(acb_ptr th, acb_srcptr t, acb_srcptr z, arb_srcptr d0, + arb_srcptr d, const acb_mat_t tau, slong nb_steps, slong s, + slong guard, slong prec, acb_theta_ql_worker_t worker) +{ + slong g = acb_mat_nrows(tau); + slong n = 1 << g; + int hast = !_acb_vec_is_zero(t, g); + int hasz = !_acb_vec_is_zero(z, g); + slong nbt = (hast ? 3 : 1); + slong nbr = (hast ? 2 : 1); + slong nbz = (hasz ? 2 : 1); + arb_mat_t Yinv; + acb_ptr x, rts; + arb_ptr y, w; + arb_t f, c; + slong k; + int res = 1; + + arb_mat_init(Yinv, g, g); + x = _acb_vec_init(g); + y = _arb_vec_init(g); + w = _arb_vec_init(g); + rts = _acb_vec_init(nbz * nbr * n * nb_steps); + arb_init(f); + arb_init(c); + + acb_siegel_yinv(Yinv, tau, prec); + _acb_vec_get_imag(y, z, g); + arb_mat_vector_mul_col(w, Yinv, y, prec); + arb_dot(f, NULL, 1, y, 1, w, 1, g, prec); + arb_const_pi(c, prec); + arb_mul(f, f, c, prec); + + res = acb_theta_ql_roots(rts, t, z, d0, d, tau, nb_steps, guard, prec); + if (res) + { + res = acb_theta_ql_a0_start(th, t, z, d0, d, f, tau, nb_steps, s, + guard, prec, worker); + } + if (res) + { + for (k = nb_steps - 1; k >= 0; k--) + { + acb_theta_ql_a0_step(th, rts, d0, d, k, nb_steps, hast, hasz, g, prec); + } + } + if (res && hasz) + { + arb_neg(f, f); + arb_exp(c, f, prec); + _acb_vec_scalar_mul_arb(th + nbt * n, th + nbt * n, n * nbt, c, prec); + } + + arb_mat_clear(Yinv); + _acb_vec_clear(x, g); + _arb_vec_clear(y, g); + _arb_vec_clear(w, g); + _acb_vec_clear(rts, nbz * nbr * n * nb_steps); + arb_clear(f); + arb_clear(c); + return res; +} diff --git a/src/acb_theta/ql_all.c b/src/acb_theta/ql_all.c new file mode 100644 index 0000000000..23f5a89b2c --- /dev/null +++ b/src/acb_theta/ql_all.c @@ -0,0 +1,403 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb_mat.h" +#include "acb_theta.h" + +#define ACB_THETA_QL_TRY 100 + +static void +acb_theta_ql_dupl(acb_ptr th2, acb_srcptr th0, acb_srcptr th, + arb_srcptr d0, arb_srcptr d, slong g, slong prec) +{ + slong n = 1 << g; + acb_ptr v; + ulong a, b; + + v = _acb_vec_init(n); + + for (a = 0; a < n; a++) + { + _acb_vec_set(v, th, n); + for (b = 0; b < n; b++) + { + if (acb_theta_char_dot(a, b, g) % 2 == 1) + { + acb_neg(&v[b], &v[b]); + } + } + acb_theta_agm_mul_tight(v, th0, v, d0, d, g, prec); + for (b = 0; b < n; b++) + { + acb_set(&th2[b * n + a], &v[b]); + } + } + _acb_vec_scalar_mul_2exp_si(th2, th2, n * n, g); + + _acb_vec_clear(v, n); +} + + +static int +acb_theta_ql_all_with_t(acb_ptr th, acb_srcptr t, acb_srcptr z, arb_srcptr d0, + arb_srcptr d, const acb_mat_t tau, slong guard, slong prec) +{ + slong g = acb_mat_nrows(tau); + slong n = 1 << g; + int hasz = !_acb_vec_is_zero(z, g); + int hast = !_acb_vec_is_zero(t, g); + slong nbz = (hasz ? 2 : 1); + slong nbt = (hast ? 3 : 1); + acb_mat_t new_tau; + acb_ptr rts, new_z, th_a0, aux; + arb_ptr new_d0, new_d; + slong hprec; + slong k, a; + int res = 1; + + acb_mat_init(new_tau, g, g); + rts = _acb_vec_init(n * n); + new_z = _acb_vec_init(g); + th_a0 = _acb_vec_init(n * nbz * nbt); + aux = _acb_vec_init(n * n); + new_d0 = _arb_vec_init(n); + new_d = _arb_vec_init(n); + + /* Collect roots: we only need theta_{a,b}(z + t, tau) */ + _acb_vec_add(new_z, z, t, g, prec); + + for (a = 0; (a < n) && res; a++) + { + hprec = guard + acb_theta_dist_addprec(&d[a]); + acb_theta_naive_fixed_a(rts + a * n, a, new_z, 1, tau, hprec); + for (k = 0; (k < n) && res; k++) + { + /* Ignore theta constants if z = t = 0 */ + if (acb_contains_zero(&rts[a * n + k]) + && (hasz || hast || acb_theta_char_is_even(a * n + k, g))) + { + res = 0; + } + } + } + + /* Get ql_a0 at 2z, t, 2tau */ + if (res) + { + acb_mat_scalar_mul_2exp_si(new_tau, tau, 1); + _acb_vec_scalar_mul_2exp_si(new_z, z, g, 1); + _arb_vec_scalar_mul_2exp_si(new_d, d, n, 1); + _arb_vec_scalar_mul_2exp_si(new_d0, d0, n, 1); + + res = acb_theta_ql_a0(th_a0, t, new_z, new_d0, new_d, new_tau, guard, prec); + } + + if (res) + { + /* Get theta_{a,b}(z + t, tau) from square roots */ + acb_theta_ql_dupl(th, th_a0, th_a0 + (nbz * nbt - 1) * n, + new_d0, new_d, g, prec); + acb_theta_agm_sqrt(th, th, rts, n * n, prec); + + if (hast) + { + /* Get theta_{a,b}(z, tau) from division */ + acb_theta_ql_dupl(aux, th_a0 + n, th_a0 + (3 * nbz - 2) * n, + new_d0, new_d, g, prec); + for (k = 0; k < n * n; k++) + { + acb_div(&th[k], &aux[k], &th[k], prec); + } + } + } + + /* Set odd theta constants to zero */ + if (!hasz) + { + for (a = 0; a < n * n; a++) + { + if (!acb_theta_char_is_even(a, g)) + { + acb_zero(&th[a]); + } + } + } + + acb_mat_clear(new_tau); + _acb_vec_clear(rts, n * n); + _acb_vec_clear(new_z, g); + _acb_vec_clear(th_a0, n * nbz * nbt); + _acb_vec_clear(aux, n * n); + _arb_vec_clear(new_d0, n); + _arb_vec_clear(new_d, n); + return res; +} + +static void +acb_theta_ql_all_red(acb_ptr th, acb_srcptr z, const acb_mat_t tau, slong prec) +{ + slong g = acb_mat_nrows(tau); + slong n = 1 << g; + slong lp = ACB_THETA_LOW_PREC; + slong guard = ACB_THETA_LOW_PREC; + slong nb_der = acb_theta_jet_nb(2, g); + flint_rand_t state; + arb_ptr d, d0; + acb_mat_t tau_mid; + acb_ptr t, z_mid, dth; + arb_t err; + arf_t e; + slong j, k; + int hasz = !_acb_vec_is_zero(z, g); + int res; + + flint_randinit(state); + d = _arb_vec_init(n); + d0 = _arb_vec_init(n); + acb_mat_init(tau_mid, g, g); + t = _acb_vec_init(g); + z_mid = _acb_vec_init(g); + dth = _acb_vec_init(n * n * nb_der); + arb_init(err); + arf_init(e); + + acb_theta_dist_a0(d, z, tau, lp); + acb_theta_dist_a0(d0, t, tau, lp); + + /* Get midpoints; ql_all_with_t is expected to lose guard + g bits of precision */ + arf_one(e); + arf_mul_2exp_si(e, e, -prec - guard - g); + for (j = 0; j < g; j++) + { + for (k = j; k < g; k++) + { + acb_get_mid(acb_mat_entry(tau_mid, j, k), acb_mat_entry(tau, j, k)); + acb_add_error_arf(acb_mat_entry(tau_mid, j, k), e); + acb_set(acb_mat_entry(tau_mid, k, j), acb_mat_entry(tau_mid, j, k)); + } + acb_get_mid(&z_mid[j], &z[j]); + if (hasz) + { + acb_add_error_arf(&z_mid[j], e); + } + } + + res = acb_theta_ql_all_with_t(th, t, z_mid, d0, d, tau_mid, + guard, prec + guard + g); + + for (j = 0; (j < ACB_THETA_QL_TRY) && !res; j++) + { + for (k = 0; k < g; k++) + { + arb_urandom(acb_realref(&t[k]), state, prec + guard + g); + } + _acb_vec_scalar_mul_2exp_si(t, t, g, 1); + res = acb_theta_ql_all_with_t(th, t, z_mid, d0, d, tau_mid, + guard, prec + guard + g); + guard += ACB_THETA_LOW_PREC; + } + + if (!res) + { + _acb_vec_indeterminate(th, n * n); + } + else + { + acb_theta_jet_naive_all(dth, z, tau, 2, ACB_THETA_LOW_PREC); + for (j = 0; j < n * n; j++) + { + acb_theta_jet_error_bounds(err, z, tau, dth + j * nb_der, 0, ACB_THETA_LOW_PREC); + acb_add_error_arb(&th[j], err); + } + } + + flint_randclear(state); + _arb_vec_clear(d, n); + _arb_vec_clear(d0, n); + acb_mat_clear(tau_mid); + _acb_vec_clear(t, g); + _acb_vec_clear(z_mid, g); + _acb_vec_clear(dth, n * n * nb_der); + arb_clear(err); + arf_clear(e); +} + +static void +acb_theta_ql_all_sqr_red(acb_ptr th2, acb_srcptr z, const acb_mat_t tau, slong prec) +{ + slong g = acb_mat_nrows(tau); + slong n = 1 << g; + slong lp = ACB_THETA_LOW_PREC; + slong guard = ACB_THETA_LOW_PREC; + int hasz = !_acb_vec_is_zero(z, g); + slong nbz = (hasz ? 2 : 1); + slong nbt = 1; + flint_rand_t state; + acb_mat_t w; + arb_ptr d, d0; + acb_ptr t, x, th; + slong j, k; + int res; + + flint_randinit(state); + acb_mat_init(w, g, g); + x = _acb_vec_init(g); + d = _arb_vec_init(n); + d0 = _arb_vec_init(n); + t = _acb_vec_init(g); + th = _acb_vec_init(n * 3 * nbz); + + acb_mat_scalar_mul_2exp_si(w, tau, 1); + _acb_vec_scalar_mul_2exp_si(x, z, g, 1); + + acb_theta_dist_a0(d, x, w, lp); + acb_theta_dist_a0(d0, t, w, lp); + + res = acb_theta_ql_a0(th, t, x, d0, d, w, guard, prec); + + for (j = 0; (j < ACB_THETA_QL_TRY) && !res; j++) + { + nbt = 3; + for (k = 0; k < g; k++) + { + arb_urandom(acb_realref(&t[k]), state, prec); + } + _acb_vec_scalar_mul_2exp_si(t, t, g, 1); + res = acb_theta_ql_a0(th, t, x, d0, d, w, guard, prec); + guard += ACB_THETA_LOW_PREC; + } + + if (!res) + { + _acb_vec_indeterminate(th2, n * n); + } + else if (hasz) + { + acb_theta_ql_dupl(th2, th, th + nbt * n, d0, d, g, prec); + } + else + { + acb_theta_ql_dupl(th2, th, th, d0, d0, g, prec); + } + + flint_randclear(state); + acb_mat_clear(w); + _acb_vec_clear(x, g); + _arb_vec_clear(d, n); + _arb_vec_clear(d0, n); + _acb_vec_clear(t, g); + _acb_vec_clear(th, n * 3 * nbz); +} + +void +acb_theta_ql_all(acb_ptr th, acb_srcptr z, const acb_mat_t tau, int sqr, slong prec) +{ + slong g = acb_mat_nrows(tau); + slong n2 = 1 << (2 * g); + acb_mat_t tau0; + acb_ptr new_z, aux; + acb_t c; + arb_t u, v; + arf_t b; + slong s; + slong * n1; + ulong ab, a0, a1, b0, b1, fixed_a1; + + acb_init(c); + arb_init(u); + arb_init(v); + arf_init(b); + new_z = _acb_vec_init(g); + n1 = flint_malloc(g * sizeof(slong)); + + s = acb_theta_ql_reduce(new_z, c, u, n1, z, tau, prec); + if (sqr) + { + acb_sqr(c, c, prec); + arb_sqr(u, u, prec); + } + + if (s == -1) + { + _acb_vec_zero(th, n2); + for (ab = 0; ab < n2; ab++) + { + acb_add_error_arb(&th[ab], u); + } + } + else + { + fixed_a1 = acb_theta_char_get_a(n1, g - s); + acb_mat_window_init(tau0, tau, 0, 0, s, s); + aux = _acb_vec_init(1 << (2 * s)); + + if (acb_is_finite(c)) + { + if (s > 0 && !sqr) + { + acb_theta_ql_all_red(aux, new_z, tau0, prec); + } + else if (s > 0) + { + acb_theta_ql_all_sqr_red(aux, new_z, tau0, prec); + } + else + { + acb_one(&aux[0]); + } + _acb_vec_scalar_mul(aux, aux, 1 << (2 * s), c, prec); + } + else + { + _acb_vec_indeterminate(aux, 1 << (2 * s)); + } + + for (ab = 0; ab < n2; ab++) + { + /* Write ab as a0 a1 b0 b1 */ + a0 = ab >> (g + (g - s)); + a1 = (ab >> g) % (1 << (g - s)); + b0 = (ab >> (g - s)) % (1 << s); + b1 = ab % (1 << (g - s)); + + if (a1 != fixed_a1) + { + acb_zero(&th[ab]); + } + else + { + acb_mul_i_pow_si(&th[ab], &aux[(a0 << s) + b0], + (sqr ? 2 : 1) * acb_theta_char_dot_slong(b1, n1, g - s)); + if (sqr) + { + acb_abs(v, &th[ab], prec); + arb_mul(v, v, u, prec); + arb_get_ubound_arf(b, v, prec); + arb_set_arf(v, b); + arb_sqrt(v, v, prec); + arb_mul_2exp_si(v, v, 1); + acb_add_error_arb(&th[ab], v); + } + } + acb_add_error_arb(&th[ab], u); + } + + acb_mat_window_clear(tau0); + _acb_vec_clear(aux, 1 << (2 * s)); + } + + _acb_vec_clear(new_z, g); + acb_clear(c); + arb_clear(u); + arb_clear(v); + arf_clear(b); + flint_free(n1); +} diff --git a/src/acb_theta/ql_reduce.c b/src/acb_theta/ql_reduce.c new file mode 100644 index 0000000000..17fdadf9a5 --- /dev/null +++ b/src/acb_theta/ql_reduce.c @@ -0,0 +1,123 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "arb_mat.h" +#include "acb_mat.h" +#include "acb_theta.h" + +slong +acb_theta_ql_reduce(acb_ptr new_z, acb_t c, arb_t u, slong * n1, acb_srcptr z, + const acb_mat_t tau, slong prec) +{ + slong g = acb_mat_nrows(tau); + acb_theta_eld_t E; + arb_mat_t C, W, C1; + acb_mat_t tau0, tau1, x; + acb_ptr t, w; + arb_ptr v, a; + acb_t f; + arf_t R2, eps; + slong s, k; + int r = 1; + + arb_mat_init(C, g, g); + v = _arb_vec_init(g); + a = _arb_vec_init(g); + acb_init(f); + arf_init(R2); + arf_init(eps); + + acb_siegel_cho(C, tau, prec); + acb_theta_naive_radius(R2, eps, C, 0, prec); + acb_theta_naive_reduce(v, new_z, a, c, u, z, 1, tau, prec); + arb_mul_arf(u, u, eps, prec); + + for (s = g; (s >= 1) && r; ) + { + s--; + acb_theta_eld_init(E, g - s, g - s); + arb_mat_window_init(W, C, s, s, g, g); + arb_mat_init(C1, g - s, g - s); + arb_mat_set(C1, W); + + arb_mat_scalar_mul_2exp_si(C1, C1, -1); + r = acb_theta_eld_set(E, C1, R2, v + s); + r = r && (acb_theta_eld_nb_pts(E) <= 1); + if (r && (acb_theta_eld_nb_pts(E) == 0)) + { + s = -2; + } + + acb_theta_eld_clear(E); + arb_mat_window_clear(W); + arb_mat_clear(C1); + } + s++; + + if ((s >= 0) && (s < g)) + { + /* We know E has exactly one point */ + acb_theta_eld_init(E, g - s, g - s); + arb_mat_window_init(W, C, s, s, g, g); + arb_mat_init(C1, g - s, g - s); + acb_mat_window_init(tau0, tau, 0, 0, s, s); + acb_mat_window_init(tau1, tau, s, s, g, g); + acb_mat_window_init(x, tau, 0, s, s, g); + t = _acb_vec_init(g); + w = _acb_vec_init(g); + + arb_mat_set(C1, W); + arb_mat_scalar_mul_2exp_si(C1, C1, -1); + acb_theta_eld_set(E, C1, R2, v + s); + acb_theta_eld_points(n1, E); + + /* Update new_z and c */ + for (k = 0; k < g - s; k++) + { + acb_set_si(&t[k], n1[k]); + } + _acb_vec_scalar_mul_2exp_si(t, t, g - s, -1); + acb_mat_vector_mul_col(w, x, t, prec); + _acb_vec_add(new_z, new_z, w, s, prec); + + acb_mat_vector_mul_col(w, tau1, t, prec); + _acb_vec_scalar_mul_2exp_si(w, w, g - s, -1); + _acb_vec_add(w, w, new_z + s, g - s, prec); + _acb_vec_scalar_mul_2exp_si(w, w, g - s, 1); + acb_dot(f, NULL, 0, t, 1, w, 1, g - s, prec); + acb_exp_pi_i(f, f, prec); + acb_mul(c, c, f, prec); + + acb_theta_eld_clear(E); + arb_mat_window_clear(W); + arb_mat_clear(C1); + acb_mat_window_clear(tau0); + acb_mat_window_clear(tau1); + acb_mat_window_clear(x); + _acb_vec_clear(t, g); + _acb_vec_clear(w, g); + } + + if (!arb_mat_is_finite(C)) /* early abort in ql_all */ + { + acb_indeterminate(c); + arb_pos_inf(u); + s = -1; + } + + arb_mat_clear(C); + _arb_vec_clear(v, g); + _arb_vec_clear(a, g); + acb_clear(f); + arf_clear(R2); + arf_clear(eps); + return s; +} diff --git a/src/acb_theta/siegel_cho.c b/src/acb_theta/siegel_cho.c new file mode 100644 index 0000000000..de9e446d6e --- /dev/null +++ b/src/acb_theta/siegel_cho.c @@ -0,0 +1,36 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "arb_mat.h" +#include "acb_mat.h" +#include "acb_theta.h" + +void +acb_siegel_cho(arb_mat_t C, const acb_mat_t tau, slong prec) +{ + arb_t pi; + int res; + + arb_init(pi); + arb_const_pi(pi, prec); + + acb_mat_get_imag(C, tau); + arb_mat_scalar_mul_arb(C, C, pi, prec); + res = arb_mat_cho(C, C, prec); + arb_mat_transpose(C, C); + + if (!res) + { + arb_mat_indeterminate(C); + } + + arb_clear(pi); +} diff --git a/src/acb_theta/siegel_cocycle.c b/src/acb_theta/siegel_cocycle.c new file mode 100644 index 0000000000..c850d84df4 --- /dev/null +++ b/src/acb_theta/siegel_cocycle.c @@ -0,0 +1,34 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb_mat.h" +#include "acb_theta.h" + +void +acb_siegel_cocycle(acb_mat_t c, const fmpz_mat_t mat, const acb_mat_t tau, slong prec) +{ + slong g = sp2gz_dim(mat); + fmpz_mat_t gamma, delta; + acb_mat_t r; + + fmpz_mat_window_init(gamma, mat, g, 0, 2 * g, g); + fmpz_mat_window_init(delta, mat, g, g, 2 * g, 2 * g); + acb_mat_init(r, g, g); + + acb_mat_set_fmpz_mat(c, gamma); + acb_mat_set_fmpz_mat(r, delta); + acb_mat_mul(c, c, tau, prec); + acb_mat_add(c, c, r, prec); + + fmpz_mat_window_clear(gamma); + fmpz_mat_window_clear(delta); + acb_mat_clear(r); +} diff --git a/src/acb_theta/siegel_is_reduced.c b/src/acb_theta/siegel_is_reduced.c new file mode 100644 index 0000000000..12f670fa2e --- /dev/null +++ b/src/acb_theta/siegel_is_reduced.c @@ -0,0 +1,82 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "arb_mat.h" +#include "acb_mat.h" +#include "acb_theta.h" + +int +acb_siegel_is_reduced(const acb_mat_t tau, slong tol_exp, slong prec) +{ + slong g = acb_mat_nrows(tau); + fmpz_mat_t mat; + acb_mat_t c; + arb_mat_t im; + acb_t det; + arb_t abs, t, u; + slong j, k; + int res = 1; + + fmpz_mat_init(mat, 2 * g, 2 * g); + acb_mat_init(c, g, g); + arb_mat_init(im, g, g); + acb_init(det); + arb_init(abs); + arb_init(t); + arb_init(u); + + arb_one(u); + arb_mul_2exp_si(u, u, tol_exp); + + arb_one(t); + arb_mul_2exp_si(t, t, -1); + arb_add(t, t, u, prec); + for (j = 0; (j < g) && res; j++) + { + for (k = 0; (k < g) && res; k++) + { + arb_abs(abs, acb_realref(acb_mat_entry(tau, j, k))); + if (!arb_lt(abs, t)) + { + res = 0; + } + } + } + + if (res) + { + acb_mat_get_imag(im, tau); + res = arb_mat_spd_is_lll_reduced(im, tol_exp, prec); + } + + arb_one(t); + arb_sub(t, t, u, prec); + for (k = 0; k < sp2gz_nb_fundamental(g); k++) + { + sp2gz_fundamental(mat, k); + acb_siegel_cocycle(c, mat, tau, prec); + acb_mat_det(det, c, prec); + acb_abs(abs, det, prec); + if (!arb_gt(abs, t)) + { + res = 0; + } + } + + fmpz_mat_clear(mat); + acb_mat_clear(c); + arb_mat_clear(im); + acb_clear(det); + arb_clear(abs); + arb_clear(t); + arb_clear(u); + return res; +} diff --git a/src/acb_theta/siegel_randtest.c b/src/acb_theta/siegel_randtest.c new file mode 100644 index 0000000000..e491c4d473 --- /dev/null +++ b/src/acb_theta/siegel_randtest.c @@ -0,0 +1,40 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "arb_mat.h" +#include "acb_mat.h" +#include "acb_theta.h" + +void +acb_siegel_randtest(acb_mat_t tau, flint_rand_t state, slong prec, slong mag_bits) +{ + slong g = arb_mat_nrows(tau); + arb_mat_t re, im; + slong k, j; + + arb_mat_init(re, g, g); + arb_mat_init(im, g, g); + + for (k = 0; k < g; k++) + { + for (j = k; j < g; j++) + { + arb_randtest_precise(arb_mat_entry(re, k, j), state, prec, mag_bits); + arb_set(arb_mat_entry(re, j, k), arb_mat_entry(re, k, j)); + } + } + + arb_mat_randtest_spd(im, state, prec, mag_bits); + acb_mat_set_real_imag(tau, re, im); + + arb_mat_clear(re); + arb_mat_clear(im); +} diff --git a/src/acb_theta/siegel_randtest_reduced.c b/src/acb_theta/siegel_randtest_reduced.c new file mode 100644 index 0000000000..1368a57283 --- /dev/null +++ b/src/acb_theta/siegel_randtest_reduced.c @@ -0,0 +1,54 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb_mat.h" +#include "acb_theta.h" + +void +acb_siegel_randtest_reduced(acb_mat_t tau, flint_rand_t state, slong prec, slong mag_bits) +{ + slong g = acb_mat_nrows(tau); + slong s = n_randint(state, g + 1); + slong n = n_randint(state, FLINT_MAX(1, mag_bits)); + fmpz_mat_t mat; + arb_t test; + int r = 0; + slong j, k; + + fmpz_mat_init(mat, 2 * g, 2 * g); + arb_init(test); + + for (k = 0; (k < 10) && !r; k++) + { + acb_siegel_randtest(tau, state, prec, 2); + acb_siegel_reduce(mat, tau, prec); + acb_siegel_transform(tau, mat, tau, prec); + r = acb_siegel_is_reduced(tau, -1, prec); + } + if (!r) + { + acb_mat_onei(tau); + } + + for (j = s; j < g; j++) + { + for (k = 0; k < g; k++) + { + arb_mul_2exp_si(acb_imagref(acb_mat_entry(tau, j, k)), + acb_imagref(acb_mat_entry(tau, j, k)), n); + arb_mul_2exp_si(acb_imagref(acb_mat_entry(tau, k, j)), + acb_imagref(acb_mat_entry(tau, k, j)), n); + } + } + + fmpz_mat_clear(mat); + arb_clear(test); +} diff --git a/src/acb_theta/siegel_randtest_vec.c b/src/acb_theta/siegel_randtest_vec.c new file mode 100644 index 0000000000..1ab4649c65 --- /dev/null +++ b/src/acb_theta/siegel_randtest_vec.c @@ -0,0 +1,41 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb.h" +#include "acb_theta.h" + +void +acb_siegel_randtest_vec(acb_ptr z, flint_rand_t state, slong g, slong prec) +{ + slong mag_bits = n_randint(state, 4); + slong k; + + for (k = 0; k < g; k++) + { + switch (n_randint(state, 10)) + { + case 0: + acb_randtest_param(&z[k], state, prec, mag_bits); + break; + case 1: + acb_randtest(&z[k], state, prec, mag_bits); + break; + case 2: + acb_randtest_precise(&z[k], state, prec, mag_bits); + break; + case 3: + acb_randtest(&z[k], state, prec, 20); + break; + default: + acb_urandom(&z[k], state, prec); + } + } +} diff --git a/src/acb_theta/siegel_reduce.c b/src/acb_theta/siegel_reduce.c new file mode 100644 index 0000000000..2a2a7ba89e --- /dev/null +++ b/src/acb_theta/siegel_reduce.c @@ -0,0 +1,212 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "arb_mat.h" +#include "acb_mat.h" +#include "acb_theta.h" + +#define ACB_SIEGEL_REDUCE_MAG_BOUND 1000000 + +static void +fmpz_mat_bound_inf_norm(mag_t b, const fmpz_mat_t mat) +{ + slong r = acb_mat_nrows(mat); + slong c = acb_mat_ncols(mat); + arb_mat_t m; + + arb_mat_init(m, r, c); + arb_mat_set_fmpz_mat(m, mat); + arb_mat_bound_inf_norm(b, m); + arb_mat_clear(m); +} + +/* Todo: better choice of precision here? */ +static slong +acb_siegel_reduce_real_lowprec(const mag_t ntau, const mag_t nmat, slong g, slong prec) +{ + slong lp = ACB_THETA_LOW_PREC; + slong res; + mag_t b; + + mag_init(b); + mag_mul(b, ntau, nmat); + res = FLINT_MIN(prec, g * (lp + FLINT_MAX(0, mag_get_d_log2_approx(b)))); + mag_clear(b); + + return res; +} + +static void +acb_siegel_reduce_real(fmpz_mat_t mat, const acb_mat_t tau) +{ + slong g = acb_mat_nrows(tau); + slong j, k; + fmpz_t c; + + fmpz_init(c); + + fmpz_mat_one(mat); + for (j = 0; j < g; j++) + { + for (k = j; k < g; k++) + { + /* this must succeed given the bounds on ndet and ntau */ + arf_get_fmpz(c, arb_midref(acb_realref(acb_mat_entry(tau, j, k))), + ARF_RND_NEAR); + fmpz_neg(fmpz_mat_entry(mat, j, k + g), c); + } + for (k = 0; k < j; k++) + { + fmpz_set(fmpz_mat_entry(mat, j, k + g), + fmpz_mat_entry(mat, k, j + g)); + } + } + + fmpz_clear(c); +} + +static slong +acb_siegel_reduce_imag_lowprec(const mag_t ntau, const mag_t ndet, const mag_t nmat, + slong g, slong prec) +{ + slong lp = ACB_THETA_LOW_PREC; + slong res; + mag_t b; + + mag_init(b); + mag_mul(b, ntau, nmat); + mag_mul(b, b, b); + mag_mul(b, b, ntau); + mag_div(b, b, ndet); + res = FLINT_MIN(prec, g * (lp + FLINT_MAX(0, mag_get_d_log2_approx(b)))); + mag_clear(b); + + return res; +} + +static void +acb_siegel_reduce_imag(fmpz_mat_t mat, const acb_mat_t tau, slong prec) +{ + slong g = acb_mat_nrows(tau); + arb_mat_t im; + fmpz_mat_t U; + + arb_mat_init(im, g, g); + fmpz_mat_init(U, g, g); + + acb_mat_get_imag(im, tau); + arb_mat_spd_lll_reduce(U, im, prec); + sp2gz_block_diag(mat, U); + + arb_mat_clear(im); + fmpz_mat_clear(U); +} + +void +acb_siegel_reduce(fmpz_mat_t mat, const acb_mat_t tau, slong prec) +{ + slong g = acb_mat_nrows(tau); + slong lp; + fmpz_mat_t m; + acb_mat_t w, c; + arb_mat_t im; + acb_t det; + arb_t abs; + arb_t t; + mag_t ntau, nmat, ndet; + + int stop = 0; + slong j, j0; + + fmpz_mat_init(m, 2 * g, 2 * g); + acb_mat_init(w, g, g); + acb_mat_init(c, g, g); + arb_mat_init(im, g, g); + acb_init(det); + arb_init(abs); + arb_init(t); + mag_init(ntau); + mag_init(nmat); + mag_init(ndet); + + acb_mat_bound_inf_norm(ntau, tau); + acb_mat_get_imag(im, tau); + arb_mat_det(abs, im, prec); + arb_get_mag_lower(ndet, abs); + if (mag_cmp_2exp_si(ntau, ACB_SIEGEL_REDUCE_MAG_BOUND) >= 0 + || mag_cmp_2exp_si(ndet, -ACB_SIEGEL_REDUCE_MAG_BOUND) <= 0) + { + stop = 1; + } + + fmpz_mat_one(mat); + while (!stop) + { + /* Choose precision, reduce imaginary part */ + fmpz_mat_bound_inf_norm(nmat, mat); + lp = acb_siegel_reduce_imag_lowprec(ntau, ndet, nmat, g, prec); + acb_siegel_transform(w, mat, tau, lp); + acb_siegel_reduce_imag(m, w, lp); + fmpz_mat_mul(mat, m, mat); + + /* Choose precision, check transform is reduced, reduce real part */ + fmpz_mat_bound_inf_norm(nmat, mat); + lp = acb_siegel_reduce_real_lowprec(ntau, nmat, g, prec); + acb_siegel_transform(w, m, w, lp); + acb_mat_get_imag(im, w); + if (!arb_mat_spd_is_lll_reduced(im, -10, lp)) + { + stop = 1; + break; + } + acb_siegel_reduce_real(m, w); + fmpz_mat_mul(mat, m, mat); + + /* Loop over fundamental matrices (keeping same precision) */ + acb_siegel_transform(w, m, w, lp); + j0 = -1; + arb_one(t); + for (j = 0; j < sp2gz_nb_fundamental(g); j++) + { + sp2gz_fundamental(m, j); + acb_siegel_cocycle(c, m, w, lp); + acb_mat_det(det, c, lp); + acb_abs(abs, det, lp); + if (arb_lt(abs, t)) + { + j0 = j; + arb_set(t, abs); + } + } + + /* Apply fundamental matrix if found */ + if (j0 != -1) + { + sp2gz_fundamental(m, j0); + fmpz_mat_mul(mat, m, mat); + } + else + { + stop = 1; + } + } + + fmpz_mat_clear(m); + acb_mat_clear(w); + acb_mat_clear(c); + arb_mat_clear(im); + acb_clear(det); + arb_clear(abs); + arb_clear(t); + mag_clear(ntau); + mag_clear(nmat); + mag_clear(ndet); +} diff --git a/src/acb_theta/siegel_transform.c b/src/acb_theta/siegel_transform.c new file mode 100644 index 0000000000..1a7754788e --- /dev/null +++ b/src/acb_theta/siegel_transform.c @@ -0,0 +1,28 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb_mat.h" +#include "acb_theta.h" + +void +acb_siegel_transform(acb_mat_t w, const fmpz_mat_t mat, const acb_mat_t tau, slong prec) +{ + slong g = sp2gz_dim(mat); + acb_mat_t c, cinv; + + acb_mat_init(c, g, g); + acb_mat_init(cinv, g, g); + + acb_siegel_transform_cocycle_inv(w, c, cinv, mat, tau, prec); + + acb_mat_clear(c); + acb_mat_clear(cinv); +} diff --git a/src/acb_theta/siegel_transform_cocycle_inv.c b/src/acb_theta/siegel_transform_cocycle_inv.c new file mode 100644 index 0000000000..bbdf227dc0 --- /dev/null +++ b/src/acb_theta/siegel_transform_cocycle_inv.c @@ -0,0 +1,47 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb_mat.h" +#include "acb_theta.h" + +void +acb_siegel_transform_cocycle_inv(acb_mat_t w, acb_mat_t c, acb_mat_t cinv, + const fmpz_mat_t mat, const acb_mat_t tau, slong prec) +{ + slong g = sp2gz_dim(mat); + fmpz_mat_t alpha; + fmpz_mat_t beta; + acb_mat_t x, num; + int r; + + fmpz_mat_window_init(alpha, mat, 0, 0, g, g); + fmpz_mat_window_init(beta, mat, 0, g, g, 2 * g); + acb_mat_init(x, g, g); + acb_mat_init(num, g, g); + + acb_mat_set_fmpz_mat(x, alpha); + acb_mat_mul(num, x, tau, prec); + acb_mat_set_fmpz_mat(x, beta); + acb_mat_add(num, num, x, prec); + + acb_siegel_cocycle(c, mat, tau, prec); + r = acb_mat_inv(cinv, c, prec); + if (!r) + { + acb_mat_indeterminate(cinv); + } + acb_mat_mul(w, num, cinv, prec); + + fmpz_mat_window_clear(alpha); + fmpz_mat_window_clear(beta); + acb_mat_clear(x); + acb_mat_clear(num); +} diff --git a/src/acb_theta/siegel_transform_z.c b/src/acb_theta/siegel_transform_z.c new file mode 100644 index 0000000000..1faaf00ac6 --- /dev/null +++ b/src/acb_theta/siegel_transform_z.c @@ -0,0 +1,31 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb_mat.h" +#include "acb_theta.h" + +void +acb_siegel_transform_z(acb_ptr r, acb_mat_t w, const fmpz_mat_t mat, + acb_srcptr z, const acb_mat_t tau, slong prec) +{ + slong g = sp2gz_dim(mat); + acb_mat_t c, cinv; + + acb_mat_init(c, g, g); + acb_mat_init(cinv, g, g); + + acb_siegel_transform_cocycle_inv(w, c, cinv, mat, tau, prec); + acb_mat_transpose(cinv, cinv); + acb_mat_vector_mul_col(r, cinv, z, prec); + + acb_mat_clear(c); + acb_mat_clear(cinv); +} diff --git a/src/acb_theta/siegel_yinv.c b/src/acb_theta/siegel_yinv.c new file mode 100644 index 0000000000..aa5cbb34e1 --- /dev/null +++ b/src/acb_theta/siegel_yinv.c @@ -0,0 +1,27 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "arb_mat.h" +#include "acb_mat.h" +#include "acb_theta.h" + +void +acb_siegel_yinv(arb_mat_t Yinv, const acb_mat_t tau, slong prec) +{ + int res; + + acb_mat_get_imag(Yinv, tau); + res = arb_mat_inv(Yinv, Yinv, prec); + if (!res) + { + arb_mat_indeterminate(Yinv); + } +} diff --git a/src/acb_theta/sp2gz_block_diag.c b/src/acb_theta/sp2gz_block_diag.c new file mode 100644 index 0000000000..5f7e0e8373 --- /dev/null +++ b/src/acb_theta/sp2gz_block_diag.c @@ -0,0 +1,40 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "fmpz.h" +#include "acb_theta.h" + +void +sp2gz_block_diag(fmpz_mat_t mat, const fmpz_mat_t U) +{ + slong g = sp2gz_dim(mat); + fmpz_mat_t D, zero; + fmpz_t den; + + fmpz_mat_init(D, g, g); + fmpz_mat_init(zero, g, g); + fmpz_init(den); + + fmpz_mat_inv(D, den, U); + fmpz_mat_transpose(D, D); + + if (!fmpz_is_one(den)) + { + fmpz_neg(den, den); + fmpz_mat_neg(D, D); + } + + sp2gz_set_blocks(mat, U, zero, zero, D); + + fmpz_mat_clear(D); + fmpz_mat_clear(zero); + fmpz_clear(den); +} diff --git a/src/acb_theta/sp2gz_decompose.c b/src/acb_theta/sp2gz_decompose.c new file mode 100644 index 0000000000..4eba01b728 --- /dev/null +++ b/src/acb_theta/sp2gz_decompose.c @@ -0,0 +1,412 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "fmpz.h" +#include "acb_theta.h" + +/* todo: move out? */ +static int +fmpz_mat_is_diagonal(const fmpz_mat_t A) +{ + slong r = fmpz_mat_nrows(A); + slong c = fmpz_mat_ncols(A); + slong j, k; + + for (j = 0; j < r; j++) + { + for (k = 0; k < c; k++) + { + if (j != k && !fmpz_is_zero(fmpz_mat_entry(A, j, k))) + { + return 0; + } + } + } + return 1; +} + +/* todo: move out? Adapt fmpz_mat_snf algorithms to also output transform? */ +/* Compute Smith normal form of A and invertible U, V s.t. S = UAV assuming A + is square */ +static void +fmpz_mat_snf_transform(fmpz_mat_t S, fmpz_mat_t U, fmpz_mat_t V, const fmpz_mat_t A) +{ + slong g = fmpz_mat_nrows(A); + fmpz_mat_t X, M; + fmpz_t d, u, v, p, q; + slong j, k; + + fmpz_mat_init(X, g, g); + fmpz_mat_init(M, g, g); + fmpz_init(d); + fmpz_init(u); + fmpz_init(v); + fmpz_init(p); + fmpz_init(q); + + fmpz_mat_set(X, A); + fmpz_mat_one(U); + fmpz_mat_one(V); + + while (!fmpz_mat_is_diagonal(X)) + { + fmpz_mat_hnf_transform(X, M, X); + fmpz_mat_mul(U, M, U); + fmpz_mat_transpose(X, X); + fmpz_mat_hnf_transform(X, M, X); + fmpz_mat_transpose(X, X); + fmpz_mat_transpose(M, M); + fmpz_mat_mul(V, V, M); + } + + for (j = 0; j < g; j++) + { + if (fmpz_is_one(fmpz_mat_entry(X, j, j))) + { + continue; + } + for (k = j + 1; k < g; k++) + { + if (fmpz_is_zero(fmpz_mat_entry(X, k, k))) + { + continue; + } + fmpz_xgcd_canonical_bezout(d, u, v, + fmpz_mat_entry(X, j, j), fmpz_mat_entry(X, k, k)); + fmpz_divexact(p, fmpz_mat_entry(X, j, j), d); + fmpz_divexact(q, fmpz_mat_entry(X, k, k), d); + + fmpz_mat_one(M); + fmpz_set(fmpz_mat_entry(M, j, k), v); + fmpz_set(fmpz_mat_entry(M, k, j), q); + fmpz_mul(fmpz_mat_entry(M, k, k), v, q); + fmpz_add_si(fmpz_mat_entry(M, k, k), fmpz_mat_entry(M, k, k), -1); + fmpz_mat_mul(U, M, U); + fmpz_mat_mul(X, M, X); + + fmpz_mat_one(M); + fmpz_set(fmpz_mat_entry(M, j, j), u); + fmpz_one(fmpz_mat_entry(M, k, j)); + fmpz_neg(fmpz_mat_entry(M, k, k), p); + fmpz_mul(fmpz_mat_entry(M, j, k), fmpz_mat_entry(M, k, k), u); + fmpz_add_si(fmpz_mat_entry(M, j, k), fmpz_mat_entry(M, j, k), 1); + fmpz_mat_mul(V, V, M); + fmpz_mat_mul(X, X, M); + } + } + + fmpz_mat_set(S, X); + + fmpz_mat_clear(X); + fmpz_mat_clear(M); + fmpz_clear(d); + fmpz_clear(u); + fmpz_clear(v); + fmpz_clear(p); + fmpz_clear(q); +} + +static fmpz_mat_struct * +sp2gz_decompose_g1(slong * nb, const fmpz_mat_t mat) +{ + fmpz_mat_struct * res; + + res = flint_malloc(1 * sizeof(fmpz_mat_struct)); + fmpz_mat_init(res, 2, 2); + fmpz_mat_set(res, mat); + *nb = 1; + return res; +} + +static fmpz_mat_struct * +sp2gz_decompose_nonsimplified(slong * nb, const fmpz_mat_t mat) +{ + slong g = sp2gz_dim(mat); + fmpz_mat_t gamma, delta, last; + fmpz_mat_t u, v, d; + fmpz_mat_t cur, left, right, m; + fmpz_mat_t w; + fmpz_mat_struct * vec; + fmpz_mat_struct * rec = NULL; + fmpz_mat_struct * res; + fmpz_t a; + slong nb_rec = 0; + slong nb_max; + slong nb_vec = 0; + slong r, k, j; + + + if (g == 1) + { + return sp2gz_decompose_g1(nb, mat); + } + + fmpz_mat_init(u, g, g); + fmpz_mat_init(v, g, g); + fmpz_mat_init(d, g, g); + fmpz_mat_init(cur, 2 * g, 2 * g); + fmpz_mat_init(left, 2 * g, 2 * g); + fmpz_mat_init(right, 2 * g, 2 * g); + fmpz_mat_init(m, 2 * g, 2 * g); + fmpz_init(a); + + fmpz_mat_set(cur, mat); + fmpz_mat_window_init(gamma, cur, g, 0, 2 * g, g); + fmpz_mat_snf_transform(d, u, v, gamma); + fmpz_mat_window_clear(gamma); + + r = fmpz_mat_rank(d); + fmpz_mat_transpose(u, u); + sp2gz_block_diag(left, u); + sp2gz_inv(left, left); + sp2gz_block_diag(right, v); + fmpz_mat_mul(cur, left, cur); + fmpz_mat_mul(cur, cur, right); + + nb_max = 3 * fmpz_bits(fmpz_mat_entry(d, 0, 0)) + 4; + vec = flint_malloc(nb_max * sizeof(fmpz_mat_struct)); + for (k = 0; k < nb_max; k++) + { + fmpz_mat_init(&vec[k], 2 * g, 2 * g); + } + + fmpz_mat_set(&vec[nb_vec], right); + nb_vec++; + + while (r == g) + { + /* Set u such that delta + gamma*u is reduced, update vec */ + fmpz_mat_zero(u); + for (j = 0; j < g; j++) + { + for (k = j; k < g; k++) + { + fmpz_smod(a, fmpz_mat_entry(cur, g + j, g + k), fmpz_mat_entry(cur, g + j, j)); + fmpz_sub(a, a, fmpz_mat_entry(cur, g + j, g + k)); + fmpz_divexact(fmpz_mat_entry(u, j, k), a, fmpz_mat_entry(cur, g + j, j)); + fmpz_set(fmpz_mat_entry(u, k, j), fmpz_mat_entry(u, j, k)); + } + } + sp2gz_trig(right, u); + fmpz_mat_set(&vec[nb_vec], right); + fmpz_mat_mul(cur, cur, right); + nb_vec++; + + /* Swap c, d */ + sp2gz_j(&vec[nb_vec]); + sp2gz_inv(&vec[nb_vec], &vec[nb_vec]); + fmpz_mat_mul(cur, cur, &vec[nb_vec]); + nb_vec++; + + /* Recompute SNF */ + fmpz_mat_window_init(gamma, cur, g, 0, 2 * g, g); + fmpz_mat_snf_transform(d, u, v, gamma); + fmpz_mat_window_clear(gamma); + + r = fmpz_mat_rank(d); + fmpz_mat_transpose(u, u); + sp2gz_block_diag(m, u); + sp2gz_inv(m, m); + fmpz_mat_mul(left, m, left); + fmpz_mat_mul(cur, m, cur); + + sp2gz_block_diag(&vec[nb_vec], v); + fmpz_mat_mul(cur, cur, &vec[nb_vec]); + nb_vec++; + } + + /* Now r < g: make HNF on colums for the bottom of delta and recursive call */ + fmpz_mat_init(last, g, g - r); + for (k = 0; k < g - r; k++) + { + for (j = 0; j < g; j++) + { + fmpz_set(fmpz_mat_entry(last, j, k), fmpz_mat_entry(cur, g + r + k, g + j)); + } + } + fmpz_mat_hnf_transform(last, u, last); + for (j = 0; j < g - r; j++) + { + fmpz_mat_swap_rows(u, NULL, g - 1 - j, g - 1 - j - r); + } + + sp2gz_block_diag(&vec[nb_vec], u); + sp2gz_inv(&vec[nb_vec], &vec[nb_vec]); + fmpz_mat_mul(cur, cur, &vec[nb_vec]); + nb_vec++; + + if (r > 0) + { + fmpz_mat_init(w, 2 * r, 2 * r); + sp2gz_restrict(w, cur); + rec = sp2gz_decompose(&nb_rec, w); + + sp2gz_embed(right, w); + sp2gz_inv(right, right); + fmpz_mat_mul(cur, right, cur); + + fmpz_mat_window_init(delta, cur, g, g, 2 * g, 2 * g); + + sp2gz_block_diag(&vec[nb_vec], delta); + fmpz_mat_transpose(&vec[nb_vec], &vec[nb_vec]); + fmpz_mat_mul(cur, cur, &vec[nb_vec]); + nb_vec++; + + fmpz_mat_window_clear(delta); + fmpz_mat_clear(w); + } + sp2gz_inv(&vec[nb_vec], cur); + nb_vec++; + + /* Make final vector */ + *nb = 1 + nb_rec + nb_vec; + res = flint_malloc(*nb * sizeof(fmpz_mat_struct)); + for (k = 0; k < *nb; k++) + { + fmpz_mat_init(&res[k], 2 * g, 2 * g); + } + + sp2gz_inv(&res[0], left); + for (k = 0; k < nb_rec; k++) + { + sp2gz_embed(&res[1 + k], &rec[k]); + } + for (k = 0; k < nb_vec; k++) + { + sp2gz_inv(&res[1 + nb_rec + k], &vec[nb_vec - 1 - k]); + } + + fmpz_mat_clear(u); + fmpz_mat_clear(v); + fmpz_mat_clear(d); + fmpz_mat_clear(cur); + fmpz_mat_clear(left); + fmpz_mat_clear(right); + fmpz_mat_clear(m); + fmpz_mat_clear(last); + fmpz_clear(a); + for (k = 0; k < nb_max; k++) + { + fmpz_mat_clear(&vec[k]); + } + flint_free(vec); + if (r > 0) + { + for (k = 0; k < nb_rec; k++) + { + fmpz_mat_clear(&rec[k]); + } + flint_free(rec); + } + return res; +} + +fmpz_mat_struct * sp2gz_decompose(slong * nb, const fmpz_mat_t mat) +{ + slong g = sp2gz_dim(mat); + fmpz_mat_struct * rec; + slong nb_rec; + fmpz_mat_struct * res; + fmpz_mat_t u, beta, delta; + slong k, next_k, j; + + fmpz_mat_init(u, g, g); + rec = sp2gz_decompose_nonsimplified(&nb_rec, mat); + + /* Move block-diagonal matrices to the left of rec as much as possible */ + k = 0; + while (k < nb_rec) + { + for (j = k; j < nb_rec; j++) + if (!sp2gz_is_block_diag(&rec[j]) + && !sp2gz_is_trig(&rec[j]) + && !sp2gz_is_j(&rec[j])) + break; + next_k = j + 1; + + /* Move all block-diag matrices between k and next_k to the left */ + for (j = next_k - 2; j >= k; j--) + if (sp2gz_is_block_diag(&rec[j])) + break; + + for (; j >= k + 1; j--) + { + /* Commutation of rec[j-1] and rec[j] */ + if (sp2gz_is_block_diag(&rec[j - 1])) + { + fmpz_mat_mul(&rec[j - 1], &rec[j - 1], &rec[j]); + fmpz_mat_one(&rec[j]); + } + else if (sp2gz_is_trig(&rec[j - 1])) + { + fmpz_mat_window_init(beta, &rec[j - 1], 0, g, g, 2 * g); + fmpz_mat_window_init(delta, &rec[j], g, g, 2 * g, 2 * g); + fmpz_mat_transpose(u, delta); + fmpz_mat_mul(u, u, beta); + fmpz_mat_mul(u, u, delta); + + fmpz_mat_set(&rec[j - 1], &rec[j]); + sp2gz_trig(&rec[j], u); + fmpz_mat_window_clear(beta); + fmpz_mat_window_clear(delta); + } + else if (sp2gz_is_j(&rec[j - 1])) + { + sp2gz_inv(&rec[j - 1], &rec[j]); + fmpz_mat_transpose(&rec[j - 1], &rec[j - 1]); + sp2gz_j(&rec[j]); + } + } + k = next_k; + } + + /* Move trigonal matrices to the left of rec as much as possible */ + for (k = nb_rec - 1; k >= 1; k--) + { + if (sp2gz_is_trig(&rec[k]) && sp2gz_is_trig(&rec[k - 1])) + { + fmpz_mat_mul(&rec[k - 1], &rec[k - 1], &rec[k]); + fmpz_mat_one(&rec[k]); + } + } + + *nb = 0; + for (k = 0; k < nb_rec; k++) + { + if (!fmpz_mat_is_one(&rec[k])) + { + (*nb)++; + } + } + res = flint_malloc(*nb * sizeof(fmpz_mat_struct)); + for (k = 0; k < *nb; k++) + { + fmpz_mat_init(&res[k], 2 * g, 2 * g); + } + + k = 0; + for (j = 0; j < nb_rec; j++) + { + if (!fmpz_mat_is_one(&rec[j])) + { + fmpz_mat_set(&res[k], &rec[j]); + k++; + } + } + + fmpz_mat_clear(u); + for (k = 0; k < nb_rec; k++) + { + fmpz_mat_clear(&rec[k]); + } + flint_free(rec); + return res; +} diff --git a/src/acb_theta/sp2gz_embed.c b/src/acb_theta/sp2gz_embed.c new file mode 100644 index 0000000000..755d91f43f --- /dev/null +++ b/src/acb_theta/sp2gz_embed.c @@ -0,0 +1,32 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "fmpz.h" +#include "acb_theta.h" + +void +sp2gz_embed(fmpz_mat_t res, const fmpz_mat_t mat) +{ + slong j, k, u, v; + slong g = sp2gz_dim(res); + slong g1 = sp2gz_dim(mat); + + fmpz_mat_one(res); + for (j = 0; j < 2 * g1; j++) + { + for (k = 0; k < 2 * g1; k++) + { + u = j + (j >= g1 ? g - g1 : 0); + v = k + (k >= g1 ? g - g1 : 0); + fmpz_set(fmpz_mat_entry(res, u, v), fmpz_mat_entry(mat, j, k)); + } + } +} diff --git a/src/acb_theta/sp2gz_fundamental.c b/src/acb_theta/sp2gz_fundamental.c new file mode 100644 index 0000000000..0a9a403754 --- /dev/null +++ b/src/acb_theta/sp2gz_fundamental.c @@ -0,0 +1,212 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "fmpz.h" +#include "acb_theta.h" + +static void +sp2gz_fundamental_g2(fmpz_mat_t mat, slong j) +{ + slong g = 2; + fmpz_mat_t a, b, c, d; + + fmpz_mat_init(a, g, g); + fmpz_mat_init(b, g, g); + fmpz_mat_init(c, g, g); + fmpz_mat_init(d, g, g); + + if (j < 15) + { + fmpz_mat_zero(a); + fmpz_mat_one(c); + fmpz_mat_neg(b, c); + } + if (15 <= j && j < 17) + { + fmpz_mat_one(a); + fmpz_mat_neg(b, a); + } + if (17 <= j && j < 19) + { + fmpz_mat_zero(b); + fmpz_one(fmpz_mat_entry(c, 0, 0)); + fmpz_set_si(fmpz_mat_entry(c, 0, 1), -1); + fmpz_set_si(fmpz_mat_entry(c, 1, 0), -1); + fmpz_one(fmpz_mat_entry(c, 1, 1)); + } + + switch (j) + { + case 0: + fmpz_mat_zero(d); + break; + case 1: + fmpz_one(fmpz_mat_entry(d, 0, 0)); + break; + case 2: + fmpz_set_si(fmpz_mat_entry(d, 0, 0), -1); + break; + case 3: + fmpz_one(fmpz_mat_entry(d, 1, 1)); + break; + case 4: + fmpz_set_si(fmpz_mat_entry(d, 1, 1), -1); + break; + case 5: + fmpz_mat_one(d); + break; + case 6: + fmpz_mat_one(d); + fmpz_mat_neg(d, d); + break; + case 7: + fmpz_set_si(fmpz_mat_entry(d, 0, 0), -1); + fmpz_one(fmpz_mat_entry(d, 1, 1)); + break; + case 8: + fmpz_one(fmpz_mat_entry(d, 0, 0)); + fmpz_set_si(fmpz_mat_entry(d, 1, 1), -1); + break; + case 9: + fmpz_one(fmpz_mat_entry(d, 0, 1)); + fmpz_one(fmpz_mat_entry(d, 1, 0)); + break; + case 10: + fmpz_set_si(fmpz_mat_entry(d, 0, 1), -1); + fmpz_set_si(fmpz_mat_entry(d, 1, 0), -1); + break; + case 11: + fmpz_one(fmpz_mat_entry(d, 0, 0)); + fmpz_one(fmpz_mat_entry(d, 0, 1)); + fmpz_one(fmpz_mat_entry(d, 1, 0)); + break; + case 12: + fmpz_set_si(fmpz_mat_entry(d, 0, 0), -1); + fmpz_set_si(fmpz_mat_entry(d, 0, 1), -1); + fmpz_set_si(fmpz_mat_entry(d, 1, 0), -1); + break; + case 13: + fmpz_one(fmpz_mat_entry(d, 0, 1)); + fmpz_one(fmpz_mat_entry(d, 1, 0)); + fmpz_one(fmpz_mat_entry(d, 1, 1)); + break; + case 14: + fmpz_set_si(fmpz_mat_entry(d, 0, 1), -1); + fmpz_set_si(fmpz_mat_entry(d, 1, 0), -1); + fmpz_set_si(fmpz_mat_entry(d, 1, 1), -1); + break; + case 15: + fmpz_one(fmpz_mat_entry(c, 0, 0)); + fmpz_one(fmpz_mat_entry(d, 1, 1)); + break; + case 16: + fmpz_one(fmpz_mat_entry(c, 1, 1)); + fmpz_one(fmpz_mat_entry(d, 0, 0)); + break; + case 17: + fmpz_mat_one(a); + fmpz_mat_one(d); + break; + default: //case 18: + fmpz_mat_one(a); + fmpz_mat_neg(a, a); + fmpz_mat_set(d, a); + } + + sp2gz_set_blocks(mat, a, b, c, d); + + fmpz_mat_clear(a); + fmpz_mat_clear(b); + fmpz_mat_clear(c); + fmpz_mat_clear(d); +} + +static void +sp2gz_fundamental_gen_1(fmpz_mat_t mat, slong j) +{ + slong g = sp2gz_dim(mat); + slong k = 0; + slong cnt = 0; + slong l, u, v; + fmpz_mat_t mat_g2; + + fmpz_mat_init(mat_g2, 4, 4); + + while (cnt + (g - 1 - k) <= j/19) + { + cnt += g - 1 - k; + k++; + } + l = k + 1 + (j/19 - cnt); + sp2gz_fundamental_g2(mat_g2, j % 19); + + fmpz_mat_one(mat); + for (u = 0; u < 2; u++) + { + for (v = 0; v < 2; v++) + { + fmpz_set(fmpz_mat_entry(mat, k + u * g, k + v * g), + fmpz_mat_entry(mat_g2, 2 * u, 2 * v)); + fmpz_set(fmpz_mat_entry(mat, k + u * g, l + v * g), + fmpz_mat_entry(mat_g2, 2 * u, 2 * v + 1)); + fmpz_set(fmpz_mat_entry(mat, l + u * g, k + v * g), + fmpz_mat_entry(mat_g2, 2 * u + 1, 2 * v)); + fmpz_set(fmpz_mat_entry(mat, l + u * g, l + v * g), + fmpz_mat_entry(mat_g2, 2 * u + 1, 2 * v + 1)); + } + } + + fmpz_mat_clear(mat_g2); +} + +static void +sp2gz_fundamental_gen_2(fmpz_mat_t mat, slong j) +{ + slong g = sp2gz_dim(mat); + slong k; + + fmpz_mat_one(mat); + + for (k = g - 1; k >= 0; k--) + { + if (j % 2 == 1) + { + fmpz_zero(fmpz_mat_entry(mat, k, k)); + fmpz_one(fmpz_mat_entry(mat, k, k + g)); + fmpz_set_si(fmpz_mat_entry(mat, k + g, k), -1); + fmpz_zero(fmpz_mat_entry(mat, k + g, k + g)); + } + } +} + +void +sp2gz_fundamental(fmpz_mat_t mat, slong j) +{ + slong g = sp2gz_dim(mat); + slong nb_1 = 19 * ((g * (g - 1))/2); + + if (g == 1) + { + sp2gz_j(mat); + } + else if (g == 2) + { + sp2gz_fundamental_g2(mat, j); + } + else if (j < nb_1) + { + sp2gz_fundamental_gen_1(mat, j); + } + else + { + sp2gz_fundamental_gen_2(mat, j - nb_1); + } +} diff --git a/src/acb_theta/sp2gz_inv.c b/src/acb_theta/sp2gz_inv.c new file mode 100644 index 0000000000..9e624bc385 --- /dev/null +++ b/src/acb_theta/sp2gz_inv.c @@ -0,0 +1,29 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb_theta.h" + +void +sp2gz_inv(fmpz_mat_t inv, const fmpz_mat_t mat) +{ + slong g = sp2gz_dim(mat); + fmpz_mat_t j; + + fmpz_mat_init(j, 2 * g, 2 * g); + + sp2gz_j(j); + fmpz_mat_transpose(inv, mat); + fmpz_mat_mul(inv, inv, j); + fmpz_mat_mul(inv, j, inv); + fmpz_mat_neg(inv, inv); + + fmpz_mat_clear(j); +} diff --git a/src/acb_theta/sp2gz_is_block_diag.c b/src/acb_theta/sp2gz_is_block_diag.c new file mode 100644 index 0000000000..7489f6c2b9 --- /dev/null +++ b/src/acb_theta/sp2gz_is_block_diag.c @@ -0,0 +1,29 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb_theta.h" + +int +sp2gz_is_block_diag(const fmpz_mat_t mat) +{ + slong g = sp2gz_dim(mat); + fmpz_mat_t beta, gamma; + int res; + + fmpz_mat_window_init(beta, mat, 0, g, g, 2 * g); + fmpz_mat_window_init(gamma, mat, g, 0, 2 * g, g); + + res = fmpz_mat_is_zero(beta) && fmpz_mat_is_zero(gamma); + + fmpz_mat_window_clear(beta); + fmpz_mat_window_clear(gamma); + return res; +} diff --git a/src/acb_theta/sp2gz_is_correct.c b/src/acb_theta/sp2gz_is_correct.c new file mode 100644 index 0000000000..56415ade18 --- /dev/null +++ b/src/acb_theta/sp2gz_is_correct.c @@ -0,0 +1,41 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb_theta.h" + +int +sp2gz_is_correct(const fmpz_mat_t mat) +{ + slong r = fmpz_mat_nrows(mat); + slong c = fmpz_mat_ncols(mat); + slong g = r / 2; + fmpz_mat_t J, test; + int res; + + if (r != c || r % 2 != 0) + { + return 0; + } + + fmpz_mat_init(J, 2 * g, 2 * g); + fmpz_mat_init(test, 2 * g, 2 * g); + + sp2gz_j(J); + fmpz_mat_transpose(test, mat); + fmpz_mat_mul(test, test, J); + fmpz_mat_mul(test, test, mat); + + res = fmpz_mat_equal(test, J); + + fmpz_mat_clear(J); + fmpz_mat_clear(test); + return res; +} diff --git a/src/acb_theta/sp2gz_is_embedded.c b/src/acb_theta/sp2gz_is_embedded.c new file mode 100644 index 0000000000..e1e36a20a5 --- /dev/null +++ b/src/acb_theta/sp2gz_is_embedded.c @@ -0,0 +1,29 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb_theta.h" + +int +sp2gz_is_embedded(fmpz_mat_t res, const fmpz_mat_t mat) +{ + slong g = sp2gz_dim(mat); + fmpz_mat_t t; + int r; + + fmpz_mat_init(t, 2 * g, 2 * g); + + sp2gz_restrict(res, mat); + sp2gz_embed(t, res); + r = fmpz_mat_equal(t, mat); + + fmpz_mat_clear(t); + return r; +} diff --git a/src/acb_theta/sp2gz_is_j.c b/src/acb_theta/sp2gz_is_j.c new file mode 100644 index 0000000000..9161426b1a --- /dev/null +++ b/src/acb_theta/sp2gz_is_j.c @@ -0,0 +1,40 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb_theta.h" + +int +sp2gz_is_j(const fmpz_mat_t mat) +{ + slong g = sp2gz_dim(mat); + fmpz_mat_t block; + int res; + + fmpz_mat_window_init(block, mat, 0, 0, g, g); + res = fmpz_mat_is_zero(block); + fmpz_mat_window_clear(block); + + if (res) + { + fmpz_mat_window_init(block, mat, 0, g, g, 2 * g); + res = fmpz_mat_is_one(block); + fmpz_mat_window_clear(block); + } + + if (res) + { + fmpz_mat_window_init(block, mat, g, g, 2 * g, 2 * g); + res = fmpz_mat_is_zero(block); + fmpz_mat_window_clear(block); + } + + return res; +} diff --git a/src/acb_theta/sp2gz_is_trig.c b/src/acb_theta/sp2gz_is_trig.c new file mode 100644 index 0000000000..66756fdc54 --- /dev/null +++ b/src/acb_theta/sp2gz_is_trig.c @@ -0,0 +1,29 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb_theta.h" + +int +sp2gz_is_trig(const fmpz_mat_t mat) +{ + slong g = sp2gz_dim(mat); + fmpz_mat_t alpha, gamma; + int res; + + fmpz_mat_window_init(alpha, mat, 0, 0, g, g); + fmpz_mat_window_init(gamma, mat, g, 0, 2 * g, g); + + res = fmpz_mat_is_one(alpha) && fmpz_mat_is_zero(gamma); + + fmpz_mat_window_clear(alpha); + fmpz_mat_window_clear(gamma); + return res; +} diff --git a/src/acb_theta/sp2gz_j.c b/src/acb_theta/sp2gz_j.c new file mode 100644 index 0000000000..6b8f808485 --- /dev/null +++ b/src/acb_theta/sp2gz_j.c @@ -0,0 +1,31 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb_theta.h" + +void +sp2gz_j(fmpz_mat_t mat) +{ + slong g = sp2gz_dim(mat); + fmpz_mat_t zero, one, minus_one; + + fmpz_mat_init(zero, g, g); + fmpz_mat_init(one, g, g); + fmpz_mat_init(minus_one, g, g); + + fmpz_mat_one(one); + fmpz_mat_neg(minus_one, one); + sp2gz_set_blocks(mat, zero, one, minus_one, zero); + + fmpz_mat_clear(zero); + fmpz_mat_clear(one); + fmpz_mat_clear(minus_one); +} diff --git a/src/acb_theta/sp2gz_nb_fundamental.c b/src/acb_theta/sp2gz_nb_fundamental.c new file mode 100644 index 0000000000..85eb2444b7 --- /dev/null +++ b/src/acb_theta/sp2gz_nb_fundamental.c @@ -0,0 +1,23 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb_theta.h" + +slong +sp2gz_nb_fundamental(slong g) +{ + if (g == 1) + return 1; + if (g == 2) + return 19; + else + return 19 * ((g * (g - 1)) / 2) + (1 << g); +} diff --git a/src/acb_theta/sp2gz_randtest.c b/src/acb_theta/sp2gz_randtest.c new file mode 100644 index 0000000000..4f279d2a1a --- /dev/null +++ b/src/acb_theta/sp2gz_randtest.c @@ -0,0 +1,76 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb_theta.h" + +static void +sp2gz_randtest_trig(fmpz_mat_t mat, flint_rand_t state, slong bits) +{ + slong g = sp2gz_dim(mat); + fmpz_mat_t b, bt; + + fmpz_mat_init(b, g, g); + fmpz_mat_init(bt, g, g); + bits = FLINT_MAX(bits, 1); + + fmpz_mat_randbits(b, state, bits); + fmpz_mat_transpose(bt, b); + fmpz_mat_add(b, b, bt); + fmpz_mat_scalar_tdiv_q_2exp(b, b, 1); + sp2gz_trig(mat, b); + + fmpz_mat_clear(b); + fmpz_mat_clear(bt); +} + +static void +sp2gz_randtest_block_diag(fmpz_mat_t mat, flint_rand_t state, slong bits) +{ + slong g = sp2gz_dim(mat); + fmpz_mat_t u; + + fmpz_mat_init(u, g, g); + bits = FLINT_MAX(bits, 1); + + fmpz_mat_one(u); + fmpz_mat_randops(u, state, 2 * bits * g); + sp2gz_block_diag(mat, u); + + fmpz_mat_clear(u); +} + +void +sp2gz_randtest(fmpz_mat_t mat, flint_rand_t state, slong bits) +{ + slong g = sp2gz_dim(mat); + slong b = bits/5 + 1; + fmpz_mat_t aux; + + fmpz_mat_init(aux, 2 * g, 2 * g); + + sp2gz_randtest_block_diag(mat, state, b); + sp2gz_randtest_trig(aux, state, b); + fmpz_mat_mul(mat, mat, aux); + sp2gz_j(aux); + fmpz_mat_mul(mat, mat, aux); + sp2gz_randtest_trig(aux, state, b); + fmpz_mat_mul(mat, mat, aux); + sp2gz_j(aux); + fmpz_mat_mul(mat, mat, aux); + sp2gz_randtest_trig(aux, state, b); + fmpz_mat_mul(mat, mat, aux); + sp2gz_j(aux); + fmpz_mat_mul(mat, mat, aux); + sp2gz_randtest_trig(aux, state, b); + fmpz_mat_mul(mat, mat, aux); + + fmpz_mat_clear(aux); +} diff --git a/src/acb_theta/sp2gz_restrict.c b/src/acb_theta/sp2gz_restrict.c new file mode 100644 index 0000000000..07a45b2609 --- /dev/null +++ b/src/acb_theta/sp2gz_restrict.c @@ -0,0 +1,32 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb_theta.h" + +void +sp2gz_restrict(fmpz_mat_t res, const fmpz_mat_t mat) +{ + slong g = sp2gz_dim(mat); + slong g1 = sp2gz_dim(res); + fmpz_mat_t a, b, c, d; + + fmpz_mat_window_init(a, mat, 0, 0, g1, g1); + fmpz_mat_window_init(b, mat, 0, g, g1, g + g1); + fmpz_mat_window_init(c, mat, g, 0, g + g1, g1); + fmpz_mat_window_init(d, mat, g, g, g + g1, g + g1); + + sp2gz_set_blocks(res, a, b, c, d); + + fmpz_mat_window_clear(a); + fmpz_mat_window_clear(b); + fmpz_mat_window_clear(c); + fmpz_mat_window_clear(d); +} diff --git a/src/acb_theta/sp2gz_set_blocks.c b/src/acb_theta/sp2gz_set_blocks.c new file mode 100644 index 0000000000..c9d9fc59a5 --- /dev/null +++ b/src/acb_theta/sp2gz_set_blocks.c @@ -0,0 +1,32 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "fmpz.h" +#include "acb_theta.h" + +void +sp2gz_set_blocks(fmpz_mat_t mat, const fmpz_mat_t alpha, const fmpz_mat_t beta, + const fmpz_mat_t gamma, const fmpz_mat_t delta) +{ + slong g = sp2gz_dim(mat); + slong j, k; + + for (j = 0; j < g; j++) + { + for (k = 0; k < g; k++) + { + fmpz_set(fmpz_mat_entry(mat, j, k), fmpz_mat_entry(alpha, j, k)); + fmpz_set(fmpz_mat_entry(mat, j, k + g), fmpz_mat_entry(beta, j, k)); + fmpz_set(fmpz_mat_entry(mat, j + g, k), fmpz_mat_entry(gamma, j, k)); + fmpz_set(fmpz_mat_entry(mat, j + g, k + g), fmpz_mat_entry(delta, j, k)); + } + } +} diff --git a/src/acb_theta/sp2gz_trig.c b/src/acb_theta/sp2gz_trig.c new file mode 100644 index 0000000000..b53ee345c3 --- /dev/null +++ b/src/acb_theta/sp2gz_trig.c @@ -0,0 +1,28 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb_theta.h" + +void +sp2gz_trig(fmpz_mat_t mat, const fmpz_mat_t S) +{ + slong g = sp2gz_dim(mat); + fmpz_mat_t zero, one; + + fmpz_mat_init(zero, g, g); + fmpz_mat_init(one, g, g); + + fmpz_mat_one(one); + sp2gz_set_blocks(mat, one, S, zero, one); + + fmpz_mat_clear(zero); + fmpz_mat_clear(one); +} diff --git a/src/acb_theta/test/main.c b/src/acb_theta/test/main.c new file mode 100644 index 0000000000..134feb0dd4 --- /dev/null +++ b/src/acb_theta/test/main.c @@ -0,0 +1,164 @@ +/* + Copyright (C) 2023 Albin Ahlbäck + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include +#include + +/* Include functions *********************************************************/ + +#include "t-agm_hadamard.c" +#include "t-agm_mul.c" +#include "t-agm_mul_tight.c" +#include "t-agm_sqrt.c" +#include "t-all.c" +#include "t-char_dot.c" +#include "t-char_get_a.c" +#include "t-char_is_even.c" +#include "t-char_is_goepel.c" +#include "t-char_is_syzygous.c" +#include "t-dist_a0.c" +#include "t-dist_lat.c" +#include "t-dist_pt.c" +#include "t-eld_border.c" +#include "t-eld_points.c" +#include "t-g2_character.c" +#include "t-g2_chi10.c" +#include "t-g2_chi12.c" +#include "t-g2_chi35.c" +#include "t-g2_chi3_6.c" +#include "t-g2_chi5.c" +#include "t-g2_covariants.c" +#include "t-g2_covariants_lead.c" +#include "t-g2_detk_symj.c" +#include "t-g2_jet_naive_1.c" +#include "t-g2_psi4.c" +#include "t-g2_psi6.c" +#include "t-g2_sextic.c" +#include "t-g2_sextic_chi5.c" +#include "t-g2_transvectant.c" +#include "t-g2_transvectant_lead.c" +#include "t-jet_all.c" +#include "t-jet_compose.c" +#include "t-jet_error_bounds.c" +#include "t-jet_mul.c" +#include "t-jet_naive_00.c" +#include "t-jet_naive_all.c" +#include "t-jet_naive_fixed_ab.c" +#include "t-jet_naive_radius.c" +#include "t-jet_ql_all.c" +#include "t-jet_ql_bounds.c" +#include "t-jet_ql_finite_diff.c" +#include "t-jet_ql_radius.c" +#include "t-jet_tuples.c" +#include "t-naive_00.c" +#include "t-naive_all.c" +#include "t-naive_fixed_ab.c" +#include "t-naive_fixed_a.c" +#include "t-naive_radius.c" +#include "t-naive_reduce.c" +#include "t-naive_term.c" +#include "t-ql_a0.c" +#include "t-ql_a0_split.c" +#include "t-ql_a0_steps.c" +#include "t-ql_all.c" +#include "t-ql_reduce.c" +#include "t-siegel_cocycle.c" +#include "t-siegel_is_reduced.c" +#include "t-siegel_reduce.c" +#include "t-siegel_transform.c" +#include "t-siegel_transform_z.c" +#include "t-sp2gz_decompose.c" +#include "t-sp2gz_inv.c" +#include "t-sp2gz_is_correct.c" +#include "t-sp2gz_set_blocks.c" +#include "t-transform_char.c" +#include "t-transform_kappa.c" +#include "t-transform_proj.c" +#include "t-transform_sqrtdet.c" + +/* Array of test functions ***************************************************/ + +test_struct tests[] = +{ + TEST_FUNCTION(acb_theta_agm_hadamard), + TEST_FUNCTION(acb_theta_agm_mul), + TEST_FUNCTION(acb_theta_agm_mul_tight), + TEST_FUNCTION(acb_theta_agm_sqrt), + TEST_FUNCTION(acb_theta_all), + TEST_FUNCTION(acb_theta_char_dot), + TEST_FUNCTION(acb_theta_char_get_a), + TEST_FUNCTION(acb_theta_char_is_even), + TEST_FUNCTION(acb_theta_char_is_goepel), + TEST_FUNCTION(acb_theta_char_is_syzygous), + TEST_FUNCTION(acb_theta_dist_a0), + TEST_FUNCTION(acb_theta_dist_lat), + TEST_FUNCTION(acb_theta_dist_pt), + TEST_FUNCTION(acb_theta_eld_border), + TEST_FUNCTION(acb_theta_eld_points), + TEST_FUNCTION(acb_theta_g2_character), + TEST_FUNCTION(acb_theta_g2_chi10), + TEST_FUNCTION(acb_theta_g2_chi12), + TEST_FUNCTION(acb_theta_g2_chi35), + TEST_FUNCTION(acb_theta_g2_chi3_6), + TEST_FUNCTION(acb_theta_g2_chi5), + TEST_FUNCTION(acb_theta_g2_covariants), + TEST_FUNCTION(acb_theta_g2_covariants_lead), + TEST_FUNCTION(acb_theta_g2_detk_symj), + TEST_FUNCTION(acb_theta_g2_jet_naive_1), + TEST_FUNCTION(acb_theta_g2_psi4), + TEST_FUNCTION(acb_theta_g2_psi6), + TEST_FUNCTION(acb_theta_g2_sextic), + TEST_FUNCTION(acb_theta_g2_sextic_chi5), + TEST_FUNCTION(acb_theta_g2_transvectant), + TEST_FUNCTION(acb_theta_g2_transvectant_lead), + TEST_FUNCTION(acb_theta_jet_all), + TEST_FUNCTION(acb_theta_jet_compose), + TEST_FUNCTION(acb_theta_jet_error_bounds), + TEST_FUNCTION(acb_theta_jet_mul), + TEST_FUNCTION(acb_theta_jet_naive_00), + TEST_FUNCTION(acb_theta_jet_naive_all), + TEST_FUNCTION(acb_theta_jet_naive_fixed_ab), + TEST_FUNCTION(acb_theta_jet_naive_radius), + TEST_FUNCTION(acb_theta_jet_ql_all), + TEST_FUNCTION(acb_theta_jet_ql_bounds), + TEST_FUNCTION(acb_theta_jet_ql_finite_diff), + TEST_FUNCTION(acb_theta_jet_ql_radius), + TEST_FUNCTION(acb_theta_jet_tuples), + TEST_FUNCTION(acb_theta_naive_00), + TEST_FUNCTION(acb_theta_naive_all), + TEST_FUNCTION(acb_theta_naive_fixed_ab), + TEST_FUNCTION(acb_theta_naive_fixed_a), + TEST_FUNCTION(acb_theta_naive_radius), + TEST_FUNCTION(acb_theta_naive_reduce), + TEST_FUNCTION(acb_theta_naive_term), + TEST_FUNCTION(acb_theta_ql_a0), + TEST_FUNCTION(acb_theta_ql_a0_split), + TEST_FUNCTION(acb_theta_ql_a0_steps), + TEST_FUNCTION(acb_theta_ql_all), + TEST_FUNCTION(acb_theta_ql_reduce), + TEST_FUNCTION(acb_theta_siegel_cocycle), + TEST_FUNCTION(acb_theta_siegel_is_reduced), + TEST_FUNCTION(acb_theta_siegel_reduce), + TEST_FUNCTION(acb_theta_siegel_transform), + TEST_FUNCTION(acb_theta_siegel_transform_z), + TEST_FUNCTION(acb_theta_sp2gz_decompose), + TEST_FUNCTION(acb_theta_sp2gz_inv), + TEST_FUNCTION(acb_theta_sp2gz_is_correct), + TEST_FUNCTION(acb_theta_sp2gz_set_blocks), + TEST_FUNCTION(acb_theta_transform_char), + TEST_FUNCTION(acb_theta_transform_kappa), + TEST_FUNCTION(acb_theta_transform_proj), + TEST_FUNCTION(acb_theta_transform_sqrtdet) +}; + +/* main function *************************************************************/ + +TEST_MAIN(tests) diff --git a/src/acb_theta/test/t-agm_hadamard.c b/src/acb_theta/test/t-agm_hadamard.c new file mode 100644 index 0000000000..2967ecb013 --- /dev/null +++ b/src/acb_theta/test/t-agm_hadamard.c @@ -0,0 +1,58 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "acb.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_agm_hadamard, state) +{ + slong iter; + + /* Test: twice Hadamard should be multiplication by 2^g */ + for (iter = 0; iter < 1000 * flint_test_multiplier(); iter++) + { + slong g = 1 + n_randint(state, 5); + slong prec = 20 + n_randint(state, 200); + slong mag_bits = n_randint(state, 4); + acb_ptr s; + acb_ptr r; + acb_ptr test; + slong n = 1 << g; + slong k; + + s = _acb_vec_init(n); + r = _acb_vec_init(n); + test = _acb_vec_init(n); + + for (k = 0; k < n; k++) + { + acb_randtest_precise(&s[k], state, prec, mag_bits); + } + acb_theta_agm_hadamard(r, s, g, prec); + acb_theta_agm_hadamard(test, r, g, prec); + _acb_vec_scalar_mul_2exp_si(test, test, n, -g); + + if (!_acb_vec_contains(test, s, n)) + { + flint_printf("FAIL (overlap):\n"); + _acb_vec_printd(s, n, 10); + _acb_vec_printd(test, n, 10); + flint_abort(); + } + + _acb_vec_clear(s, n); + _acb_vec_clear(r, n); + _acb_vec_clear(test, n); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-agm_mul.c b/src/acb_theta/test/t-agm_mul.c new file mode 100644 index 0000000000..c2194dd1c2 --- /dev/null +++ b/src/acb_theta/test/t-agm_mul.c @@ -0,0 +1,89 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "acb_mat.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_agm_mul, state) +{ + slong iter; + + /* Test: duplication formula */ + for (iter = 0; iter < 20 * flint_test_multiplier(); iter++) + { + slong g = 1 + n_randint(state, 3); + slong prec = 100 + n_randint(state, 200); + slong mag_bits = n_randint(state, 2); + slong rad_exp = -5; + slong n = 1 << g; + + acb_mat_t tau; + acb_ptr z; + arf_t rad; + acb_ptr th; + acb_ptr th_dupl; + acb_ptr test; + arb_t err; + slong k; + + acb_mat_init(tau, g, g); + arf_init(rad); + z = _acb_vec_init(2 * g); + th = _acb_vec_init(2 * n); + th_dupl = _acb_vec_init(2 * n); + test = _acb_vec_init(2 * n); + arb_init(err); + + acb_siegel_randtest_reduced(tau, state, prec, mag_bits); + arf_one(rad); + arf_mul_2exp_si(rad, rad, rad_exp); + for (k = 0; k < g; k++) + { + acb_urandom(&z[k], state, prec); + } + _acb_vec_scalar_mul_2exp_si(z, z, g, rad_exp); + + acb_theta_naive_0b(th, z, 2, tau, prec); + acb_mat_scalar_mul_2exp_si(tau, tau, 1); + acb_theta_naive_0b(th_dupl, z, 2, tau, prec); + _acb_vec_sqr(th_dupl, th_dupl, 2 * n, prec); + + acb_theta_agm_mul(test, th, th + n, g, prec); + acb_theta_agm_mul(test + n, th + n, th + n, g, prec); + + if (!_acb_vec_overlaps(test, th_dupl, 2 * n)) + { + flint_printf("FAIL (overlap)\n"); + flint_printf("g = %wd, prec = %wd, tau, z:\n", g, prec); + acb_mat_printd(tau, 10); + _acb_vec_printd(z, g, 10); + flint_printf("theta:\n"); + _acb_vec_printd(th, 2 * n, 10); + flint_printf("dupl:\n"); + _acb_vec_printd(th_dupl, 2 * n, 10); + flint_printf("test:\n"); + _acb_vec_printd(test, 2 * n, 10); + fflush(stdout); + flint_abort(); + } + + acb_mat_clear(tau); + arf_clear(rad); + _acb_vec_clear(z, 2 * g); + _acb_vec_clear(th, 2 * n); + _acb_vec_clear(th_dupl, 2 * n); + _acb_vec_clear(test, 2 * n); + arb_clear(err); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-agm_mul_tight.c b/src/acb_theta/test/t-agm_mul_tight.c new file mode 100644 index 0000000000..5973683483 --- /dev/null +++ b/src/acb_theta/test/t-agm_mul_tight.c @@ -0,0 +1,132 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "acb_mat.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_agm_mul_tight, state) +{ + slong iter; + + /* Test: respects relative precision */ + for (iter = 0; iter < 50 * flint_test_multiplier(); iter++) + { + slong g = 1 + n_randint(state, 4); + slong n = 1 << g; + slong mprec = 50 + n_randint(state, 500); + slong prec = mprec + 50; + slong bits = n_randint(state, 3); + slong delta = 25; + acb_mat_t tau; + acb_ptr z; + acb_ptr th, th0, r; + arb_ptr d, d0; + arb_t x, t; + arf_t eps; + slong k; + + acb_mat_init(tau, g, g); + z = _acb_vec_init(g); + r = _acb_vec_init(n); + th = _acb_vec_init(n); + th0 = _acb_vec_init(n); + d = _arb_vec_init(n); + d0 = _arb_vec_init(n); + arb_init(x); + arb_init(t); + arf_init(eps); + + /* Generate distances, not too crazy */ + acb_siegel_randtest_reduced(tau, state, prec, bits); + acb_theta_dist_a0(d0, z, tau, prec); + for (k = 0; k < g; k++) + { + acb_randtest_precise(&z[k], state, prec, bits); + } + acb_theta_dist_a0(d, z, tau, prec); + + /* Generate values */ + for (k = 0; k < n; k++) + { + arb_neg(x, &d[k]); + arb_exp(x, x, prec); + acb_urandom(&th[k], state, prec); + acb_mul_arb(&th[k], &th[k], x, prec); + + arb_neg(x, &d0[k]); + arb_exp(x, x, prec); + acb_urandom(&th0[k], state, prec); + acb_mul_arb(&th0[k], &th0[k], x, prec); + } + + acb_theta_agm_mul_tight(r, th0, th, d0, d, g, mprec); + + for (k = 0; k < n; k++) + { + acb_abs(x, &r[k], prec); + arb_neg(t, &d[k]); + arb_exp(t, t, prec); + if (arb_gt(x, t)) + { + flint_printf("FAIL (absolute value, k = %wd)\n", k); + flint_printf("g = %wd, prec = %wd, tau:\n", g, prec); + acb_mat_printd(tau, 5); + flint_printf("distances:\n"); + _arb_vec_printd(d0, n, 5); + _arb_vec_printd(d, n, 5); + flint_printf("values:\n"); + _acb_vec_printd(th0, n, 5); + _acb_vec_printd(th, n, 5); + flint_printf("result:\n"); + _acb_vec_printd(r, n, 5); + flint_abort(); + } + + acb_get_rad_ubound_arf(eps, &r[k], prec); + arb_set_arf(x, eps); + arb_mul_2exp_si(t, t, -mprec + delta); + if (arb_gt(x, t)) + { + flint_printf("FAIL (precision loss, k = %wd)\n", k); + flint_printf("g = %wd, prec = %wd, tau:\n", g, prec); + acb_mat_printd(tau, 5); + flint_printf("distances:\n"); + _arb_vec_printd(d0, n, 5); + _arb_vec_printd(d, n, 5); + flint_printf("values:\n"); + _acb_vec_printd(th0, n, 5); + _acb_vec_printd(th, n, 5); + flint_printf("result:\n"); + _acb_vec_printd(r, n, 5); + flint_printf("x, t:\n"); + arb_printd(x, 5); + flint_printf("\n"); + arb_printd(t, 5); + flint_printf("\n"); + flint_abort(); + } + } + + acb_mat_clear(tau); + _acb_vec_clear(z, g); + _acb_vec_clear(r, n); + _acb_vec_clear(th, n); + _acb_vec_clear(th0, n); + _arb_vec_clear(d, n); + _arb_vec_clear(d0, n); + arb_clear(x); + arb_clear(t); + arf_clear(eps); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-agm_sqrt.c b/src/acb_theta/test/t-agm_sqrt.c new file mode 100644 index 0000000000..110ec774ab --- /dev/null +++ b/src/acb_theta/test/t-agm_sqrt.c @@ -0,0 +1,96 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "acb.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_agm_sqrt, state) +{ + slong iter; + + /* Test: + - if nonzero, value of square root should agree; precision remains high + - no abort on wrong values of rt_low */ + for (iter = 0; iter < 1000 * flint_test_multiplier(); iter++) + { + slong prec = 100 + n_randint(state, 1000); + slong mag_bits = n_randint(state, 4); + slong lowprec = 10 + n_randint(state, 10); + slong delta = (1 << mag_bits) + 10; + acb_t rt, x, rt_low, t; + arf_t err; + + acb_init(rt); + acb_init(x); + acb_init(rt_low); + acb_init(t); + arf_init(err); + + acb_randtest_precise(rt, state, prec, mag_bits); + if (iter % 10 == 0) + { + acb_union(rt, rt, x, prec); + } + + acb_sqr(x, rt, prec); + acb_one(t); + acb_mul_2exp_si(t, t, -lowprec); + acb_add_si(t, t, 1, lowprec); + acb_mul(rt_low, rt, t, lowprec); + + acb_theta_agm_sqrt(t, x, rt_low, 1, prec); + acb_get_rad_ubound_arf(err, t, prec); + + if (!acb_contains(t, rt)) + { + flint_printf("FAIL (value)\n"); + acb_printd(rt, 5); + flint_printf("\n"); + acb_printd(t, 5); + flint_printf("\n"); + acb_printd(rt_low, 5); + flint_printf("\n"); + flint_abort(); + } + + if (!acb_is_finite(t)) + { + flint_printf("FAIL (infinite)\n"); + flint_abort(); + } + + if (!acb_contains_zero(rt) && (arf_cmp_2exp_si(err, -prec + delta) > 0)) + { + flint_printf("FAIL (precision)\n"); + flint_printf("prec = %wd, result:\n", prec, mag_bits); + acb_printd(t, 10); + flint_printf("\nrt_low:\n"); + acb_printd(rt_low, 10); + flint_printf("\n"); + flint_abort(); + } + + if (iter % 10 == 1) + { + acb_randtest(rt_low, state, prec, mag_bits); + acb_theta_agm_sqrt(t, x, rt_low, 1, prec); + } + + acb_clear(rt); + acb_clear(x); + acb_clear(rt_low); + acb_clear(t); + arf_clear(err); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-all.c b/src/acb_theta/test/t-all.c new file mode 100644 index 0000000000..99975c259e --- /dev/null +++ b/src/acb_theta/test/t-all.c @@ -0,0 +1,79 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "acb_mat.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_all, state) +{ + slong iter; + + /* Test: agrees with naive_all */ + for (iter = 0; iter < 20 * flint_test_multiplier(); iter++) + { + slong g = 1 + n_randint(state, 2); + slong n2 = 1 << (2 * g); + slong prec = 100 + n_randint(state, 400); + slong bits = n_randint(state, 4); + int sqr = iter % 2; + acb_mat_t tau; + acb_ptr z; + acb_ptr th, test; + slong k; + + acb_mat_init(tau, g, g); + z = _acb_vec_init(g); + th = _acb_vec_init(n2); + test = _acb_vec_init(n2); + + /* Sample tau not too far from reduced domain */ + acb_siegel_randtest_reduced(tau, state, prec, bits); + acb_mat_scalar_mul_2exp_si(tau, tau, -1); + acb_siegel_randtest_vec(z, state, g, prec); + + /* Sometimes phony input too */ + if (n_randint(state, 20) == 0) + { + k = n_randint(state, g); + arb_zero(acb_imagref(acb_mat_entry(tau, k, k))); + } + + acb_theta_all(th, z, tau, sqr, prec); + acb_theta_naive_all(test, z, 1, tau, prec); + if (sqr) + { + for (k = 0; k < n2; k++) + { + acb_sqr(&test[k], &test[k], prec); + } + } + + if (!_acb_vec_overlaps(th, test, n2)) + { + flint_printf("FAIL\n"); + flint_printf("g = %wd, prec = %wd, sqr = %wd, tau, z:\n", g, prec, sqr); + acb_mat_printd(tau, 5); + _acb_vec_printd(z, g, 5); + flint_printf("th, test:\n"); + _acb_vec_printd(th, n2, 5); + _acb_vec_printd(test, n2, 5); + flint_abort(); + } + + acb_mat_clear(tau); + _acb_vec_clear(z, g); + _acb_vec_clear(th, n2); + _acb_vec_clear(test, n2); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-char_dot.c b/src/acb_theta/test/t-char_dot.c new file mode 100644 index 0000000000..37c3731b61 --- /dev/null +++ b/src/acb_theta/test/t-char_dot.c @@ -0,0 +1,80 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "acb.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_char_dot, state) +{ + slong iter; + + /* Test: various dots are the same */ + for (iter = 0; iter < 1000 * flint_test_multiplier(); iter++) + { + slong g = 1 + n_randint(state, 10); + slong prec = 100; + ulong a, b; + slong * n; + acb_ptr v, w; + fmpz_t m; + slong x1, x2; + acb_t x3, x4; + int res; + + n = flint_malloc(g * sizeof(slong)); + v = _acb_vec_init(g); + w = _acb_vec_init(g); + a = n_randint(state, 1 << g); + b = n_randint(state, 1 << g); + acb_init(x3); + acb_init(x4); + fmpz_init(m); + + x1 = acb_theta_char_dot(a, b, g); + acb_theta_char_get_slong(n, b, g); + x2 = acb_theta_char_dot_slong(a, n, g); + acb_theta_char_get_acb(v, b, g); + acb_theta_char_dot_acb(x3, a, v, g, prec); + acb_theta_char_get_acb(w, a, g); + acb_dot(x4, NULL, 0, v, 1, w, 1, g, prec); + + if (x1 != x2 + || !acb_overlaps(x3, x4)) + { + flint_printf("FAIL\n"); + flint_printf("x1 = %wd, x2 = %wd, x3, x4:\n", x1, x2); + acb_printd(x3, 10); + flint_printf("\n"); + acb_printd(x4, 10); + flint_printf("\n"); + flint_abort(); + } + + acb_mul_2exp_si(x3, x3, 2); + res = acb_get_unique_fmpz(m, x3); + fmpz_sub_si(m, m, x1); + if (!res || !fmpz_divisible_si(m, 4)) + { + flint_printf("FAIL (mod 4)\n"); + flint_abort(); + } + + flint_free(n); + _acb_vec_clear(v, g); + _acb_vec_clear(w, g); + acb_clear(x3); + acb_clear(x4); + fmpz_clear(m); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-char_get_a.c b/src/acb_theta/test/t-char_get_a.c new file mode 100644 index 0000000000..3c883ccb58 --- /dev/null +++ b/src/acb_theta/test/t-char_get_a.c @@ -0,0 +1,43 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_char_get_a, state) +{ + slong iter; + + /* Test: inverse of char_get_slong */ + for (iter = 0; iter < 1000 * flint_test_multiplier(); iter++) + { + slong g = 1 + n_randint(state, 10); + slong * n; + ulong a, t; + + n = flint_malloc(g * sizeof(slong)); + a = n_randint(state, 1 << g); + + acb_theta_char_get_slong(n, a, g); + t = acb_theta_char_get_a(n, g); + + if (a != t) + { + flint_printf("FAIL\n"); + flint_printf("a, t: %wd, %wd\n", a, t); + flint_abort(); + } + + flint_free(n); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-char_is_even.c b/src/acb_theta/test/t-char_is_even.c new file mode 100644 index 0000000000..80483a24c1 --- /dev/null +++ b/src/acb_theta/test/t-char_is_even.c @@ -0,0 +1,49 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_char_is_even, state) +{ + slong iter; + + /* Test: values for g = 2 */ + for (iter = 0; iter < 1; iter++) + { + slong g = 2; + slong even[10] = {0, 1, 2, 3, 4, 6, 8, 9, 12, 15}; + slong odd[6] = {5, 7, 10, 11, 13, 14}; + slong i; + + for (i = 0; i < 10; i++) + { + if (!acb_theta_char_is_even(even[i], g)) + { + flint_printf("FAIL (even)\n"); + flint_printf("i = %wd, ab = %wd\n", i, even[i]); + flint_abort(); + } + } + + for (i = 0; i < 6; i++) + { + if (acb_theta_char_is_even(odd[i], g)) + { + flint_printf("FAIL (odd)\n"); + flint_printf("i = %wd, ab = %wd\n", i, odd[i]); + flint_abort(); + } + } + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-char_is_goepel.c b/src/acb_theta/test/t-char_is_goepel.c new file mode 100644 index 0000000000..7b8a87fdd2 --- /dev/null +++ b/src/acb_theta/test/t-char_is_goepel.c @@ -0,0 +1,52 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_char_is_goepel, state) +{ + slong iter; + + /* Test: there are 15 Goepel quadruples for g = 2 */ + for (iter = 0; iter < 1; iter++) + { + slong g = 2; + slong n = 1 << (2 * g); + ulong ch1, ch2, ch3, ch4; + slong cnt = 0; + + for (ch1 = 0; ch1 < n; ch1++) + { + for (ch2 = ch1; ch2 < n; ch2++) + { + for (ch3 = ch2; ch3 < n; ch3++) + { + for (ch4 = ch3; ch4 < n; ch4++) + { + if (acb_theta_char_is_goepel(ch1, ch2, ch3, ch4, g)) + { + cnt++; + } + } + } + } + } + + if (cnt != 15) + { + flint_printf("FAIL (cnt = %wd)\n", cnt); + flint_abort(); + } + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-char_is_syzygous.c b/src/acb_theta/test/t-char_is_syzygous.c new file mode 100644 index 0000000000..78f0500eb7 --- /dev/null +++ b/src/acb_theta/test/t-char_is_syzygous.c @@ -0,0 +1,49 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_char_is_syzygous, state) +{ + slong iter; + + /* Test: there are 60 syzygous triples for g = 2 */ + for (iter = 0; iter < 1; iter++) + { + slong g = 2; + slong n = 1 << (2 * g); + ulong ch1, ch2, ch3; + slong cnt = 0; + + for (ch1 = 0; ch1 < n; ch1++) + { + for (ch2 = ch1; ch2 < n; ch2++) + { + for (ch3 = ch2; ch3 < n; ch3++) + { + if (acb_theta_char_is_syzygous(ch1, ch2, ch3, g)) + { + cnt++; + } + } + } + } + + if (cnt != 60) + { + flint_printf("FAIL (cnt = %wd)\n", cnt); + flint_abort(); + } + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-dist_a0.c b/src/acb_theta/test/t-dist_a0.c new file mode 100644 index 0000000000..645d152f23 --- /dev/null +++ b/src/acb_theta/test/t-dist_a0.c @@ -0,0 +1,68 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "acb_mat.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_dist_a0, state) +{ + slong iter; + + /* Test: find zero value when z = tau a/2 + real stuff */ + for (iter = 0; iter < 20 * flint_test_multiplier(); iter++) + { + slong g = 1 + n_randint(state, 4); + slong n = 1 << g; + slong prec = ACB_THETA_LOW_PREC; + slong hprec = 200; + slong bits = n_randint(state, 5); + acb_mat_t tau; + acb_ptr z; + arb_ptr d; + arb_t c; + ulong a = n_randint(state, n); + slong k; + + acb_mat_init(tau, g, g); + z = _acb_vec_init(g); + d = _arb_vec_init(n); + arb_init(c); + + acb_siegel_randtest_reduced(tau, state, hprec, bits); + acb_theta_char_get_acb(z, a, g); + acb_mat_vector_mul_col(z, tau, z, prec); + for (k = 0; k < g; k++) + { + arb_urandom(c, state, prec); + acb_add_arb(&z[k], &z[k], c, prec); + } + + acb_theta_dist_a0(d, z, tau, prec); + + if (!arb_contains_zero(&d[a])) + { + flint_printf("FAIL\n"); + flint_printf("g = %wd, a = %wd, tau:\n", g, a); + acb_mat_printd(tau, 5); + flint_printf("distances:\n"); + _arb_vec_printd(d, n, 5); + flint_abort(); + } + + acb_mat_clear(tau); + _acb_vec_clear(z, g); + _arb_vec_clear(d, n); + arb_clear(c); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-dist_lat.c b/src/acb_theta/test/t-dist_lat.c new file mode 100644 index 0000000000..66c50fe8cb --- /dev/null +++ b/src/acb_theta/test/t-dist_lat.c @@ -0,0 +1,121 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "arb_mat.h" +#include "acb_mat.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_dist_lat, state) +{ + slong iter; + + /* Test: make ellipsoid to check it is indeed the minimal distance */ + for (iter = 0; iter < 20 * flint_test_multiplier(); iter++) + { + slong g = 1 + n_randint(state, 4); + slong prec = ACB_THETA_LOW_PREC; + slong hprec = 200; + slong bits = n_randint(state, 5); + acb_mat_t tau; + arb_mat_t C; + acb_ptr z; + arb_ptr v, y; + arb_t d, test, x, s; + arf_t R2; + acb_theta_eld_t E; + slong *pts; + slong k; + int r; + + acb_mat_init(tau, g, g); + arb_mat_init(C, g, g); + z = _acb_vec_init(g); + v = _arb_vec_init(g); + y = _arb_vec_init(g); + arb_init(d); + arb_init(test); + arb_init(x); + arb_init(s); + acb_theta_eld_init(E, g, g); + arf_init(R2); + + /* Get reduced C */ + acb_siegel_randtest_reduced(tau, state, hprec, bits); + acb_siegel_randtest_vec(z, state, g, prec); + + _acb_vec_get_imag(v, z, g); + acb_siegel_cho(C, tau, prec); + + acb_theta_dist_lat(d, v, C, prec); + arb_get_ubound_arf(R2, d, prec); + + /* Test: ellipsoid has points and d is the minimum distance */ + r = acb_theta_eld_set(E, C, R2, v); + + if (r) + { + if (acb_theta_eld_nb_pts(E) == 0) + { + flint_printf("FAIL (no points)\n"); + flint_printf("g = %wd, C:\n", g); + arb_mat_printd(C, 10); + flint_printf("offset:\n"); + _arb_vec_printn(v, g, 10, 0); + flint_printf("\n"); + flint_printf("Distance: "); + arf_printd(R2, 10); + flint_printf("\n"); + flint_abort(); + } + + pts = flint_malloc(acb_theta_eld_nb_pts(E) * sizeof(slong) * g); + acb_theta_eld_points(pts, E); + + arb_pos_inf(test); + for (k = 0; k < acb_theta_eld_nb_pts(E); k++) + { + acb_theta_dist_pt(x, v, C, pts + k * g, prec); + arb_min(test, test, x, prec); + } + + if (!arb_overlaps(d, test)) + { + flint_printf("FAIL (wrong distance)\n"); + flint_printf("g = %wd, C:\n", g); + arb_mat_printd(C, 10); + flint_printf("offset:\n"); + _arb_vec_printn(v, g, 10, 0); + flint_printf("\n"); + flint_printf("Distance: "); + arf_printd(R2, 10); + flint_printf("\n"); + flint_abort(); + } + + flint_free(pts); + } + + acb_mat_clear(tau); + arb_mat_clear(C); + _acb_vec_clear(z, g); + _arb_vec_clear(v, g); + _arb_vec_clear(y, g); + arb_clear(d); + arb_clear(test); + arb_clear(x); + arb_clear(s); + acb_theta_eld_clear(E); + arf_clear(R2); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-dist_pt.c b/src/acb_theta/test/t-dist_pt.c new file mode 100644 index 0000000000..f589b9e075 --- /dev/null +++ b/src/acb_theta/test/t-dist_pt.c @@ -0,0 +1,85 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "arb_mat.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_dist_pt, state) +{ + slong iter; + + /* Test: symmetric using C * pt as offset */ + for (iter = 0; iter < 1000 * flint_test_multiplier(); iter++) + { + slong g = 1 + n_randint(state, 6); + slong prec = ACB_THETA_LOW_PREC; + slong bits = n_randint(state, 3); + arb_mat_t C; + arb_ptr v; + arb_t d1, d2; + slong * pt1; + slong * pt2; + slong k; + + arb_mat_init(C, g, g); + v = _arb_vec_init(g); + arb_init(d1); + arb_init(d2); + pt1 = flint_malloc(g * sizeof(slong)); + pt2 = flint_malloc(g * sizeof(slong)); + + arb_mat_randtest_cho(C, state, prec, bits); + arb_mat_transpose(C, C); + + for (k = 0; k < g; k++) + { + pt1[k] = n_randint(state, 100); + pt2[k] = n_randint(state, 100); + } + + for (k = 0; k < g; k++) + { + arb_set_si(&v[k], pt1[k]); + } + arb_mat_vector_mul_col(v, C, v, prec); + acb_theta_dist_pt(d1, v, C, pt2, prec); + + for (k = 0; k < g; k++) + { + arb_set_si(&v[k], pt2[k]); + } + arb_mat_vector_mul_col(v, C, v, prec); + acb_theta_dist_pt(d2, v, C, pt1, prec); + + if (!arb_overlaps(d1, d2)) + { + flint_printf("FAIL\n"); + flint_printf("C:\n"); + arb_mat_printd(C, 5); + flint_printf("distances:\n"); + arb_printd(d1, 10); + flint_printf("\n"); + arb_printd(d2, 10); + flint_printf("\n"); + flint_abort(); + } + + arb_mat_clear(C); + _arb_vec_clear(v, g); + arb_clear(d1); + arb_clear(d2); + flint_free(pt1); + flint_free(pt2); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-eld_border.c b/src/acb_theta/test/t-eld_border.c new file mode 100644 index 0000000000..3327faaf17 --- /dev/null +++ b/src/acb_theta/test/t-eld_border.c @@ -0,0 +1,100 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "arb_mat.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_eld_border, state) +{ + slong iter; + + /* Test: border points are not contained in the ellipsoid, + nor any children */ + for (iter = 0; iter < 500 * flint_test_multiplier(); iter++) + { + slong g = 1 + n_randint(state, 4); + slong prec = ACB_THETA_LOW_PREC; + slong mag_bits = n_randint(state, 2); + acb_theta_eld_t E; + arb_mat_t C; + arb_t x; + arf_t R2; + arb_ptr v; + slong k, j; + slong *all_pts; + int r; + + acb_theta_eld_init(E, g, g); + arb_mat_init(C, g, g); + arf_init(R2); + v = _arb_vec_init(g); + arb_init(x); + + arb_mat_randtest_cho(C, state, prec, mag_bits); + arb_mat_transpose(C, C); + arb_randtest_positive(x, state, prec, mag_bits); + arf_set(R2, arb_midref(x)); + arf_mul_si(R2, R2, 1 + n_randint(state, 10), prec, ARF_RND_UP); + for (k = 0; k < g; k++) + { + arb_randtest_precise(&v[k], state, prec, mag_bits); + } + + r = acb_theta_eld_set(E, C, R2, v); + if (!r) + { + flint_printf("FAIL (ellipsoid)\n"); + flint_abort(); + } + + all_pts = flint_malloc(acb_theta_eld_nb_border(E) * g * sizeof(slong)); + acb_theta_eld_border(all_pts, E); + + for (k = 0; k < acb_theta_eld_nb_border(E); k++) + { + for (j = 0; j < g; j++) + { + if (acb_theta_eld_contains(E, all_pts + k * g)) + { + flint_printf("FAIL: point inside ellipsoid\n"); + flint_abort(); + } + } + + for (j = 0; j < acb_theta_eld_nr(E); j++) + { + if (acb_theta_eld_contains(acb_theta_eld_rchild(E, j), all_pts + k * g)) + { + flint_printf("FAIL: point inside right child %wd\n", j); + flint_abort(); + } + } + for (j = 0; j < acb_theta_eld_nl(E); j++) + { + if (acb_theta_eld_contains(acb_theta_eld_lchild(E, j), all_pts + k * g)) + { + flint_printf("FAIL: point inside left child %wd\n", j); + flint_abort(); + } + } + } + + acb_theta_eld_clear(E); + arb_mat_clear(C); + arf_clear(R2); + _arb_vec_clear(v, g); + arb_clear(x); + flint_free(all_pts); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-eld_points.c b/src/acb_theta/test/t-eld_points.c new file mode 100644 index 0000000000..ada938d943 --- /dev/null +++ b/src/acb_theta/test/t-eld_points.c @@ -0,0 +1,190 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "arb_mat.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_eld_points, state) +{ + slong iter; + + /* Test: all ellipsoid points must be within the box + Then, generate random points: + - points inside ellipsoid must appear in all_pts + - points outside ellipsoid must have norm greater than R2 */ + for (iter = 0; iter < 500 * flint_test_multiplier(); iter++) + { + slong g = 1 + n_randint(state, 4); + slong prec = ACB_THETA_LOW_PREC; + slong mag_bits = n_randint(state, 2); + acb_theta_eld_t E; + arb_mat_t C; + arf_t R2; + arb_ptr v; + slong k, j; + slong try; + slong *all_pts; + slong *pt; + int res; + arb_mat_t vec; + arb_t sqr, sum; + + acb_theta_eld_init(E, g, g); + arb_mat_init(C, g, g); + arf_init(R2); + v = _arb_vec_init(g); + pt = flint_malloc(g * sizeof(slong)); + arb_mat_init(vec, g, 1); + arb_init(sqr); + arb_init(sum); + + arb_mat_randtest_cho(C, state, prec, mag_bits); + arb_mat_transpose(C, C); + arb_randtest_positive(sqr, state, prec, mag_bits); + arf_set(R2, arb_midref(sqr)); + arf_mul_si(R2, R2, 1 + n_randint(state, 10), prec, ARF_RND_UP); + + for (k = 0; k < g; k++) + { + arb_randtest_precise(&v[k], state, prec, mag_bits); + } + + res = acb_theta_eld_set(E, C, R2, v); + if (!res) + { + flint_printf("FAIL (ellipsoid)\n"); + flint_abort(); + } + + all_pts = flint_malloc(acb_theta_eld_nb_pts(E) * g * sizeof(slong)); + acb_theta_eld_points(all_pts, E); + + for (k = 0; k < acb_theta_eld_nb_pts(E); k++) + { + for (j = 0; j < g; j++) + { + if (FLINT_ABS(all_pts[k * g + j]) > acb_theta_eld_box(E, j)) + { + flint_printf("FAIL: point outside box\n"); + flint_printf("\n"); + flint_abort(); + } + } + } + + for (try = 0; try < 100; try++) + { + for (k = 0; k < g; k++) + { + pt[k] = n_randint(state, acb_theta_eld_box(E, k) + 1); + } + if (acb_theta_eld_contains(E, pt)) + { + for (k = 0; k < acb_theta_eld_nb_pts(E); k++) + { + res = 1; + for (j = 0; j < g; j++) + { + if (all_pts[k * g + j] != pt[j]) + { + res = 0; + break; + } + } + if (res == 1) + { + break; + } + } + if (!res) + { + flint_printf("FAIL: point not listed:\n"); + for (j = 0; j < g; j++) + { + flint_printf("%wd ", pt[j]); + } + flint_abort(); + } + } + + if (!acb_theta_eld_contains(E, pt)) + { + arb_mat_zero(vec); + for (k = 0; k < g; k++) + { + arb_set_si(arb_mat_entry(vec, k, 0), pt[k]); + } + + arb_mat_mul(vec, C, vec, prec); + arb_zero(sum); + for (k = 0; k < g; k++) + { + arb_add(arb_mat_entry(vec, k, 0), + arb_mat_entry(vec, k, 0), &v[k], prec); + arb_sqr(sqr, arb_mat_entry(vec, k, 0), prec); + arb_add(sum, sum, sqr, prec); + } + arb_sub_arf(sum, sum, R2, prec); + if (arb_is_negative(sum)) + { + flint_printf("FAIL: small point not in ellipsoid\n"); + for (j = 0; j < g; j++) + { + flint_printf("%wd ", pt[j]); + } + flint_printf("\nCholesky:\n"); + arb_mat_printd(C, 10); + flint_printf("Norm of point: "); + arb_printd(sum, 10); + flint_printf("\nCoordinates:\n"); + for (j = 0; j < g; j++) + { + arb_printd(arb_mat_entry(vec, j, 0), 10); + flint_printf("\n"); + } + flint_printf("Upper bound: "); + arf_printd(R2, 10); + flint_printf("\ntotal nb of points = %wd\n", acb_theta_eld_nb_pts(E)); + flint_printf("Offset:\n"); + for (j = 0; j < g; j++) + { + arb_printd(&v[j], 10); + flint_printf("\n"); + } + flint_printf("Points:\n"); + for (k = 0; k < acb_theta_eld_nb_pts(E); k++) + { + for (j = 0; j < g; j++) + { + flint_printf("%wd ", all_pts[k * g + j]); + } + + flint_printf("\n"); + } + flint_abort(); + } + } + } + + acb_theta_eld_clear(E); + arb_mat_clear(C); + arf_clear(R2); + _arb_vec_clear(v, g); + flint_free(all_pts); + flint_free(pt); + arb_mat_clear(vec); + arb_clear(sqr); + arb_clear(sum); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-g2_character.c b/src/acb_theta/test/t-g2_character.c new file mode 100644 index 0000000000..770a51aec3 --- /dev/null +++ b/src/acb_theta/test/t-g2_character.c @@ -0,0 +1,62 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_g2_character, state) +{ + slong iter; + + /* Test: agrees with kappa2 */ + for (iter = 0; iter < 200 * flint_test_multiplier(); iter++) + { + fmpz_mat_t mat; + slong bits = n_randint(state, 10); + slong ab, eps, u, test; + + fmpz_mat_init(mat, 4, 4); + sp2gz_randtest(mat, state, bits); + + eps = acb_theta_g2_character(mat); + + test = 10 * acb_theta_transform_kappa2(mat); /* 10 theta constants */ + for (ab = 0; ab < 16; ab++) + { + if (acb_theta_char_is_even(ab, 2)) + { + acb_theta_transform_char(&u, mat, ab); + test += u; + } + } + if (test % 4 != 0) + { + flint_printf("FAIL (%wd mod 4)\n", test % 4); + fmpz_mat_print_pretty(mat); + flint_printf("\n"); + flint_abort(); + } + test = (test / 4) % 2; + + if (eps != test) + { + flint_printf("FAIL (%wd != %wd)\n", eps, test); + fmpz_mat_print_pretty(mat); + flint_printf("\n"); + flint_abort(); + } + + fmpz_mat_clear(mat); + } + + TEST_FUNCTION_END(state); +} + diff --git a/src/acb_theta/test/t-g2_chi10.c b/src/acb_theta/test/t-g2_chi10.c new file mode 100644 index 0000000000..96374d5ed0 --- /dev/null +++ b/src/acb_theta/test/t-g2_chi10.c @@ -0,0 +1,69 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "acb.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_g2_chi10, state) +{ + slong iter; + + /* Test: is a modular form */ + for (iter = 0; iter < 100 * flint_test_multiplier(); iter++) + { + slong g = 2; + slong n2 = 16; + slong prec = 100 + n_randint(state, 500); + slong mag_bits = n_randint(state, 2); + fmpz_mat_t mat; + acb_ptr th2; + acb_t r, s; + slong k; + + fmpz_mat_init(mat, 2 * g, 2 * g); + th2 = _acb_vec_init(n2); + acb_init(r); + acb_init(s); + + sp2gz_randtest(mat, state, mag_bits); + for (k = 0; k < n2; k++) + { + acb_randtest_precise(&th2[k], state, prec, mag_bits); + } + + acb_theta_g2_chi10(r, th2, prec); + acb_theta_transform_proj(th2, mat, th2, 1, prec); + acb_theta_g2_chi10(s, th2, prec); + if (acb_theta_transform_kappa2(mat) % 2 == 1) + { + acb_neg(s, s); + } + + if (!acb_overlaps(r, s)) + { + flint_printf("FAIL\n"); + fmpz_mat_print_pretty(mat); + acb_printd(r, 10); + flint_printf("\n"); + acb_printd(s, 10); + flint_printf("\n"); + flint_abort(); + } + + fmpz_mat_clear(mat); + _acb_vec_clear(th2, n2); + acb_clear(r); + acb_clear(s); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-g2_chi12.c b/src/acb_theta/test/t-g2_chi12.c new file mode 100644 index 0000000000..c35f59f667 --- /dev/null +++ b/src/acb_theta/test/t-g2_chi12.c @@ -0,0 +1,65 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "acb.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_g2_chi12, state) +{ + slong iter; + + /* Test: is a modular form */ + for (iter = 0; iter < 100 * flint_test_multiplier(); iter++) + { + slong g = 2; + slong n2 = 16; + slong prec = 100 + n_randint(state, 500); + slong mag_bits = n_randint(state, 4); + fmpz_mat_t mat; + acb_ptr th2; + acb_t r, s; + slong k; + + fmpz_mat_init(mat, 2 * g, 2 * g); + th2 = _acb_vec_init(n2); + acb_init(r); + acb_init(s); + + sp2gz_randtest(mat, state, mag_bits); + for (k = 0; k < n2; k++) + { + acb_randtest_precise(&th2[k], state, prec, mag_bits); + } + + acb_theta_g2_chi12(r, th2, prec); + acb_theta_transform_proj(th2, mat, th2, 1, prec); + acb_theta_g2_chi12(s, th2, prec); + + if (!acb_overlaps(r, s)) + { + flint_printf("FAIL\n"); + fmpz_mat_print_pretty(mat); + acb_printd(r, 10); + flint_printf("\n"); + acb_printd(s, 10); + flint_printf("\n"); + flint_abort(); + } + + fmpz_mat_clear(mat); + _acb_vec_clear(th2, n2); + acb_clear(r); + acb_clear(s); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-g2_chi35.c b/src/acb_theta/test/t-g2_chi35.c new file mode 100644 index 0000000000..924eeb6260 --- /dev/null +++ b/src/acb_theta/test/t-g2_chi35.c @@ -0,0 +1,67 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "acb_mat.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_g2_chi35, state) +{ + slong iter; + + /* Test: transforms like a modular form */ + for (iter = 0; iter < 50 * flint_test_multiplier(); iter++) + { + slong g = 2; + slong n2 = 1 << (2 * g); + slong prec = 100 + n_randint(state, 500); + slong mag_bits = n_randint(state, 2); + fmpz_mat_t mat; + acb_mat_t tau; + acb_ptr th, z; + acb_t r, s; + + fmpz_mat_init(mat, 2 * g, 2 * g); + acb_mat_init(tau, g, g); + th = _acb_vec_init(n2); + z = _acb_vec_init(g); + acb_init(r); + acb_init(s); + + sp2gz_randtest(mat, state, mag_bits); + acb_siegel_randtest_reduced(tau, state, prec, mag_bits); + acb_theta_all(th, z, tau, 0, prec); + + acb_theta_g2_chi35(r, th, prec); + acb_theta_transform_proj(th, mat, th, 0, prec); + acb_theta_g2_chi35(s, th, prec); + acb_mul_i_pow_si(s, s, -acb_theta_transform_kappa2(mat)); + + if (!acb_overlaps(r, s)) + { + flint_printf("FAIL\n"); + acb_printd(r, 10); + flint_printf("\n"); + acb_printd(s, 10); + flint_printf("\n"); + flint_abort(); + } + + fmpz_mat_clear(mat); + acb_mat_clear(tau); + _acb_vec_clear(th, n2); + _acb_vec_clear(z, g); + acb_clear(r); + acb_clear(s); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-g2_chi3_6.c b/src/acb_theta/test/t-g2_chi3_6.c new file mode 100644 index 0000000000..69eef7247b --- /dev/null +++ b/src/acb_theta/test/t-g2_chi3_6.c @@ -0,0 +1,96 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "acb_poly.h" +#include "acb_mat.h" +#include "acb_theta.h" + +static void +acb_theta_g2_chi8_6(acb_poly_t res, const acb_mat_t tau, slong prec) +{ + acb_ptr z, dth; + acb_t c; + slong k; + + dth = _acb_vec_init(3 * 16); + z = _acb_vec_init(2); + acb_init(c); + + acb_theta_jet_all(dth, z, tau, 1, prec); + acb_theta_g2_chi3_6(res, dth, prec); + for (k = 0; k < 16; k++) + { + acb_set(&dth[k], &dth[3 * k]); + } + acb_theta_g2_chi5(c, dth, prec); + acb_poly_scalar_mul(res, res, c, prec); + + _acb_vec_clear(dth, 3 * 16); + _acb_vec_clear(z, 2); + acb_clear(c); +} + +TEST_FUNCTION_START(acb_theta_g2_chi3_6, state) +{ + slong iter; + + /* Test: chi5 * chi3_6 transforms like a modular form */ + for (iter = 0; iter < 10 * flint_test_multiplier(); iter++) + { + slong g = 2; + slong prec = 100 + n_randint(state, 200); + slong mag_bits = n_randint(state, 2); + fmpz_mat_t mat; + acb_mat_t tau, w, c, cinv; + acb_poly_t r, s; + + fmpz_mat_init(mat, 2 * g, 2 * g); + acb_mat_init(tau, g, g); + acb_mat_init(w, g, g); + acb_mat_init(c, g, g); + acb_mat_init(cinv, g, g); + acb_poly_init(r); + acb_poly_init(s); + + sp2gz_randtest(mat, state, mag_bits); + acb_siegel_randtest_reduced(tau, state, prec, mag_bits); + + acb_theta_g2_chi8_6(r, tau, prec); + acb_siegel_transform_cocycle_inv(w, c, cinv, mat, tau, prec); + acb_theta_g2_chi8_6(s, w, prec); + acb_theta_g2_detk_symj(s, cinv, s, 8, 6, prec); + + if (!acb_poly_overlaps(r, s)) + { + flint_printf("FAIL\n"); + acb_mat_printd(tau, 5); + fmpz_mat_print_pretty(mat); + flint_printf("values at tau, m*tau:\n"); + acb_poly_printd(r, 10); + flint_printf("\n"); + acb_poly_printd(s, 10); + flint_printf("\n"); + flint_abort(); + } + + fmpz_mat_clear(mat); + acb_mat_clear(tau); + acb_mat_clear(w); + acb_mat_clear(c); + acb_mat_clear(cinv); + acb_poly_clear(r); + acb_poly_clear(s); + } + + TEST_FUNCTION_END(state); +} + diff --git a/src/acb_theta/test/t-g2_chi5.c b/src/acb_theta/test/t-g2_chi5.c new file mode 100644 index 0000000000..88d8e82d19 --- /dev/null +++ b/src/acb_theta/test/t-g2_chi5.c @@ -0,0 +1,61 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "acb.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_g2_chi5, state) +{ + slong iter; + + /* Test: square is chi10 */ + for (iter = 0; iter < 100 * flint_test_multiplier(); iter++) + { + slong g = 2; + slong n2 = 1 << (2 * g); + slong prec = 100 + n_randint(state, 500); + slong mag_bits = n_randint(state, 10); + acb_ptr th; + slong k; + acb_t r, s; + + th = _acb_vec_init(n2); + acb_init(r); + acb_init(s); + + for (k = 0; k < n2; k++) + { + acb_randtest_precise(&th[k], state, prec, mag_bits); + } + + acb_theta_g2_chi5(r, th, prec); + acb_sqr(r, r, prec); + _acb_vec_sqr(th, th, n2, prec); + acb_theta_g2_chi10(s, th, prec); + + if (!acb_overlaps(r, s)) + { + flint_printf("FAIL\n"); + acb_printd(r, 10); + flint_printf("\n"); + acb_printd(s, 10); + flint_printf("\n"); + flint_abort(); + } + + _acb_vec_clear(th, n2); + acb_clear(r); + acb_clear(s); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-g2_covariants.c b/src/acb_theta/test/t-g2_covariants.c new file mode 100644 index 0000000000..3048f4668d --- /dev/null +++ b/src/acb_theta/test/t-g2_covariants.c @@ -0,0 +1,163 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "fmpz_poly.h" +#include "acb_poly.h" +#include "acb_mat.h" +#include "acb_theta.h" + +#define ACB_THETA_G2_COV_K {1,2,2,2,3,3,3,3,4,4,4,4,5,5,5,6,6,6,7,7,8,9,10,10,12,15} +#define ACB_THETA_G2_COV_J {6,0,4,8,2,6,8,12,0,4,6,10,2,4,8,0,6,6,2,4,2,4,0,2,2,0} + +TEST_FUNCTION_START(acb_theta_g2_covariants, state) +{ + slong iter; + + /* Test: + - agrees with g2_psi4 using psi4 = -(Co20 - 3*Co40)/20 + - covariants transform as det^(k-j/2)Sym^j + - covariants take integral values on integral polynomials */ + for (iter = 0; iter < 5 * flint_test_multiplier(); iter++) + { + slong prec = 200 + n_randint(state, 200); + slong g = 2; + slong n = 1 << (2 * g); + slong bits = 2; + slong jlist[26] = ACB_THETA_G2_COV_J; + slong klist[26] = ACB_THETA_G2_COV_K; + fmpz_mat_t mat; + acb_mat_t tau, w, c; + acb_ptr z, th2; + acb_poly_struct * cov1; + acb_poly_struct * cov2; + acb_poly_t u, v; + fmpz_poly_t pol; + acb_t psi4, test; + slong k; + + fmpz_mat_init(mat, 2 * g, 2 * g); + acb_mat_init(tau, g, g); + acb_mat_init(w, g, g); + acb_mat_init(c, g, g); + z = _acb_vec_init(g); + th2 = _acb_vec_init(n); + cov1 = flint_malloc(26 * sizeof(acb_poly_struct)); + cov2 = flint_malloc(26 * sizeof(acb_poly_struct)); + for (k = 0; k < 26; k++) + { + acb_poly_init(&cov1[k]); + acb_poly_init(&cov2[k]); + } + acb_poly_init(u); + acb_poly_init(v); + fmpz_poly_init(pol); + acb_init(psi4); + acb_init(test); + + acb_siegel_randtest_reduced(tau, state, prec, bits); + sp2gz_randtest(mat, state, bits); + + acb_theta_all(th2, z, tau, 1, prec); + acb_theta_g2_psi4(psi4, th2, prec); + acb_theta_g2_sextic(u, tau, prec); + acb_theta_g2_covariants(cov1, u, prec); + + acb_siegel_transform(w, mat, tau, prec); + acb_siegel_cocycle(c, mat, tau, prec); + acb_theta_g2_sextic(u, w, prec); + acb_theta_g2_covariants(cov2, u, prec); + + /* Test psi4 */ + acb_poly_set_si(u, -3); + acb_poly_mul(u, u, &cov1[8], prec); + acb_poly_mul(v, &cov1[1], &cov1[1], prec); + acb_poly_add(u, u, v, prec); + acb_poly_get_coeff_acb(test, u, 0); + acb_div_si(test, test, -20, prec); + + if (!acb_overlaps(psi4, test)) + { + flint_printf("FAIL (psi4)\n"); + acb_mat_printd(tau, 5); + flint_printf("psi4, test:\n"); + acb_printd(psi4, 10); + flint_printf("\n"); + acb_printd(test, 10); + flint_printf("\nu:\n"); + acb_poly_printd(u, 5); + flint_printf("\ncovariants:\n"); + for (k = 0; k < 26; k++) + { + acb_poly_printd(&cov1[k], 5); + flint_printf("\n"); + } + flint_abort(); + } + + /* Test transformation */ + for (k = 0; k < 26; k++) + { + acb_theta_g2_detk_symj(u, c, &cov1[k], klist[k] - jlist[k]/2, jlist[k], prec); + if (!acb_poly_overlaps(u, &cov2[k])) + { + flint_printf("FAIL (transform, k = %wd)\n", k); + acb_mat_printd(tau, 5); + fmpz_mat_print_pretty(mat); + flint_printf("\n"); + acb_poly_printd(u, 5); + flint_printf("\n"); + acb_poly_printd(&cov2[k], 5); + flint_printf("\n"); + flint_abort(); + } + } + + /* Test integrality */ + acb_poly_zero(u); + for (k = 0; k <= 6; k++) + { + acb_poly_set_coeff_si(u, k, n_randint(state, 10)); + } + acb_theta_g2_covariants(cov1, u, prec); + for (k = 0; k < 26; k++) + { + if (!acb_poly_get_unique_fmpz_poly(pol, &cov1[k])) + { + flint_printf("FAIL (integrality, k = %wd)\n", k); + acb_poly_printd(&cov1[k], 5); + flint_printf("\n"); + flint_abort(); + } + } + + fmpz_mat_clear(mat); + acb_mat_clear(tau); + acb_mat_clear(w); + acb_mat_clear(c); + _acb_vec_clear(z, g); + _acb_vec_clear(th2, n); + for (k = 0; k < 26; k++) + { + acb_poly_clear(&cov1[k]); + acb_poly_clear(&cov2[k]); + } + flint_free(cov1); + flint_free(cov2); + acb_poly_clear(u); + acb_poly_clear(v); + fmpz_poly_clear(pol); + acb_clear(psi4); + acb_clear(test); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-g2_covariants_lead.c b/src/acb_theta/test/t-g2_covariants_lead.c new file mode 100644 index 0000000000..9cbb7d6f46 --- /dev/null +++ b/src/acb_theta/test/t-g2_covariants_lead.c @@ -0,0 +1,77 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "acb_poly.h" +#include "acb_theta.h" + +#define ACB_THETA_G2_COV_J {6,0,4,8,2,6,8,12,0,4,6,10,2,4,8,0,6,6,2,4,2,4,0,2,2,0} + +TEST_FUNCTION_START(acb_theta_g2_covariants_lead, state) +{ + slong iter; + + /* Test: agrees with g2_covariants */ + for (iter = 0; iter < 20 * flint_test_multiplier(); iter++) + { + slong prec = 200 + n_randint(state, 100); + slong bits = 2; + slong nb = ACB_THETA_G2_COV_NB; + slong jlist[] = ACB_THETA_G2_COV_J; + acb_poly_struct * cov; + acb_ptr res, test; + acb_poly_t r; + slong k; + + cov = flint_malloc(nb * sizeof(acb_poly_struct)); + for (k = 0; k < nb; k++) + { + acb_poly_init(&cov[k]); + } + acb_poly_init(r); + res = _acb_vec_init(nb); + test = _acb_vec_init(nb); + + acb_poly_randtest(r, state, 7, prec, bits); + + acb_theta_g2_covariants(cov, r, prec); + acb_theta_g2_covariants_lead(res, r, prec); + for (k = 0; k < nb; k++) + { + acb_poly_get_coeff_acb(&test[k], &cov[k], jlist[k]); + } + + if (!_acb_vec_overlaps(res, test, nb)) + { + flint_printf("FAIL\n"); + _acb_vec_printd(res, nb, 5); + _acb_vec_printd(test, nb, 5); + flint_printf("\ncovariants:\n"); + for (k = 0; k < nb; k++) + { + acb_poly_printd(&cov[k], 5); + flint_printf("\n"); + } + flint_abort(); + } + + for (k = 0; k < nb; k++) + { + acb_poly_clear(&cov[k]); + } + flint_free(cov); + acb_poly_clear(r); + _acb_vec_clear(res, nb); + _acb_vec_clear(test, nb); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-g2_detk_symj.c b/src/acb_theta/test/t-g2_detk_symj.c new file mode 100644 index 0000000000..442d7330c8 --- /dev/null +++ b/src/acb_theta/test/t-g2_detk_symj.c @@ -0,0 +1,72 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "acb_poly.h" +#include "acb_mat.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_g2_detk_symj, state) +{ + slong iter; + + /* Test: chain rule */ + for (iter = 0; iter < 100 * flint_test_multiplier(); iter++) + { + slong g = 2; + acb_mat_t c1, c2, c3; + acb_poly_t s, r, t; + slong k = n_randint(state, 10); + slong j = n_randint(state, 10); + slong bits = 2; + slong prec = 100 + n_randint(state, 200); + + acb_mat_init(c1, g, g); + acb_mat_init(c2, g, g); + acb_mat_init(c3, g, g); + acb_poly_init(s); + acb_poly_init(r); + acb_poly_init(t); + + acb_mat_randtest(c1, state, prec, bits); + acb_mat_randtest(c2, state, prec, bits); + acb_mat_mul(c3, c1, c2, prec); + acb_poly_randtest(s, state, j + 1, prec, bits); + + acb_theta_g2_detk_symj(r, c2, s, k, j, prec); + acb_theta_g2_detk_symj(r, c1, r, k, j, prec); + acb_theta_g2_detk_symj(t, c3, s, k, j, prec); + + if (!acb_poly_overlaps(t, r)) + { + flint_printf("FAIL\n"); + acb_mat_printd(c1, 5); + acb_mat_printd(c2, 5); + flint_printf("source:\n"); + acb_poly_printd(s, 5); + flint_printf("\nvalues:\n"); + acb_poly_printd(r, 5); + flint_printf("\n"); + acb_poly_printd(t, 5); + flint_printf("\n"); + flint_abort(); + } + + acb_mat_clear(c1); + acb_mat_clear(c2); + acb_mat_clear(c3); + acb_poly_clear(s); + acb_poly_clear(r); + acb_poly_clear(t); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-g2_jet_naive_1.c b/src/acb_theta/test/t-g2_jet_naive_1.c new file mode 100644 index 0000000000..46ddc6d05c --- /dev/null +++ b/src/acb_theta/test/t-g2_jet_naive_1.c @@ -0,0 +1,76 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "acb_mat.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_g2_jet_naive_1, state) +{ + slong iter; + + /* Test: agrees with usual jet_naive at the right indices */ + for (iter = 0; iter < 20 * flint_test_multiplier(); iter++) + { + slong g = 2; + slong n = 1 << (2 * g); + slong nb = acb_theta_jet_nb(1, g + 1); + slong prec = 100 + n_randint(state, 3000); + slong bits = n_randint(state, 4); + acb_mat_t tau; + acb_ptr z, dth, test; + slong k; + int res; + + acb_mat_init(tau, g, g); + z = _acb_vec_init(g); + dth = _acb_vec_init(n * nb); + test = _acb_vec_init(n * nb); + + acb_siegel_randtest_reduced(tau, state, prec, bits); + if (iter % 10 == 0) + { + acb_zero(acb_mat_entry(tau, 0, 0)); + } + + acb_theta_g2_jet_naive_1(dth, tau, prec); + acb_theta_jet_naive_all(test, z, tau, 1, prec); + + for (k = 0; k < n; k++) + { + if (acb_theta_char_is_even(k, 2)) + { + res = acb_overlaps(&dth[3 * k], &test[3 * k]); + } + else + { + res = _acb_vec_overlaps(&dth[3 * k + 1], &test[3 * k + 1], 2); + } + + if (!res) + { + flint_printf("FAIL (k = %wd)\n", k); + acb_mat_printd(tau, 5); + flint_printf("values:\n"); + _acb_vec_printd(&dth[3 * k], 3, 5); + _acb_vec_printd(&test[3 * k], 3, 5); + flint_abort(); + } + } + + acb_mat_clear(tau); + _acb_vec_clear(z, g); + _acb_vec_clear(dth, n * nb); + _acb_vec_clear(test, n * nb); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-g2_psi4.c b/src/acb_theta/test/t-g2_psi4.c new file mode 100644 index 0000000000..fd8bc39e83 --- /dev/null +++ b/src/acb_theta/test/t-g2_psi4.c @@ -0,0 +1,65 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "acb.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_g2_psi4, state) +{ + slong iter; + + /* Test: is a modular form */ + for (iter = 0; iter < 100 * flint_test_multiplier(); iter++) + { + slong g = 2; + slong n2 = 16; + slong prec = 100 + n_randint(state, 500); + slong mag_bits = n_randint(state, 4); + fmpz_mat_t mat; + acb_ptr th2; + acb_t r, s; + slong k; + + fmpz_mat_init(mat, 2 * g, 2 * g); + th2 = _acb_vec_init(n2); + acb_init(r); + acb_init(s); + + sp2gz_randtest(mat, state, mag_bits); + for (k = 0; k < n2; k++) + { + acb_randtest_precise(&th2[k], state, prec, mag_bits); + } + + acb_theta_g2_psi4(r, th2, prec); + acb_theta_transform_proj(th2, mat, th2, 1, prec); + acb_theta_g2_psi4(s, th2, prec); + + if (!acb_overlaps(r, s)) + { + flint_printf("FAIL\n"); + fmpz_mat_print_pretty(mat); + acb_printd(r, 10); + flint_printf("\n"); + acb_printd(s, 10); + flint_printf("\n"); + flint_abort(); + } + + fmpz_mat_clear(mat); + _acb_vec_clear(th2, n2); + acb_clear(r); + acb_clear(s); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-g2_psi6.c b/src/acb_theta/test/t-g2_psi6.c new file mode 100644 index 0000000000..1960f4b51f --- /dev/null +++ b/src/acb_theta/test/t-g2_psi6.c @@ -0,0 +1,69 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "acb.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_g2_psi6, state) +{ + slong iter; + + /* Test: is a modular form */ + for (iter = 0; iter < 100 * flint_test_multiplier(); iter++) + { + slong g = 2; + slong n2 = 16; + slong prec = 100 + n_randint(state, 500); + slong mag_bits = n_randint(state, 2); + fmpz_mat_t mat; + acb_ptr th2; + acb_t r, s; + slong k; + + fmpz_mat_init(mat, 2 * g, 2 * g); + th2 = _acb_vec_init(n2); + acb_init(r); + acb_init(s); + + sp2gz_randtest(mat, state, mag_bits); + for (k = 0; k < n2; k++) + { + acb_randtest_precise(&th2[k], state, prec, mag_bits); + } + + acb_theta_g2_psi6(r, th2, prec); + acb_theta_transform_proj(th2, mat, th2, 1, prec); + acb_theta_g2_psi6(s, th2, prec); + if (acb_theta_transform_kappa2(mat) % 2 == 1) + { + acb_neg(s, s); + } + + if (!acb_overlaps(r, s)) + { + flint_printf("FAIL\n"); + fmpz_mat_print_pretty(mat); + acb_printd(r, 10); + flint_printf("\n"); + acb_printd(s, 10); + flint_printf("\n"); + flint_abort(); + } + + fmpz_mat_clear(mat); + _acb_vec_clear(th2, n2); + acb_clear(r); + acb_clear(s); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-g2_sextic.c b/src/acb_theta/test/t-g2_sextic.c new file mode 100644 index 0000000000..5b4fea5061 --- /dev/null +++ b/src/acb_theta/test/t-g2_sextic.c @@ -0,0 +1,96 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "acb_poly.h" +#include "acb_mat.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_g2_sextic, state) +{ + slong iter; + + /* Test: discriminant of sextic is chi10 */ + for (iter = 0; iter < 10 * flint_test_multiplier(); iter++) + { + slong g = 2; + slong n = 1 << (2 * g); + slong prec = 100 + n_randint(state, 100); + slong bits = n_randint(state, 4); + acb_mat_t tau; + acb_ptr z, th2, roots; + acb_poly_t f; + acb_t d, t; + slong nb, k, j; + + if (iter % 20 == 0) + { + prec += 10000; /* necessary for full test coverage */ + } + + acb_mat_init(tau, g, g); + z = _acb_vec_init(g); + th2 = _acb_vec_init(n); + roots = _acb_vec_init(6); + acb_poly_init(f); + acb_init(d); + acb_init(t); + + acb_siegel_randtest_reduced(tau, state, prec, bits); + acb_mat_scalar_mul_2exp_si(tau, tau, -2); + + acb_theta_g2_sextic(f, tau, prec); + nb = acb_poly_find_roots(roots, f, NULL, 0, prec); + + if (nb == 6) + { + acb_one(d); + for (k = 0; k < 6; k++) + { + for (j = k + 1; j < 6; j++) + { + acb_sub(t, &roots[k], &roots[j], prec); + acb_mul(d, d, t, prec); + } + } + acb_sqr(d, d, prec); + acb_poly_get_coeff_acb(t, f, 6); + acb_pow_ui(t, t, 10, prec); + acb_mul(d, d, t, prec); + acb_mul_2exp_si(d, d, -12); + + acb_theta_all(th2, z, tau, 1, prec); + acb_theta_g2_chi10(t, th2, prec); + + if (!acb_overlaps(d, t)) + { + flint_printf("FAIL\n"); + flint_printf("roots, discr, chi10:\n"); + _acb_vec_printd(roots, 6, 5); + acb_printd(d, 5); + flint_printf("\n"); + acb_printd(t, 5); + flint_printf("\n"); + flint_abort(); + } + } + + acb_mat_clear(tau); + _acb_vec_clear(z, g); + _acb_vec_clear(th2, n); + _acb_vec_clear(roots, 6); + acb_poly_clear(f); + acb_clear(d); + acb_clear(t); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-g2_sextic_chi5.c b/src/acb_theta/test/t-g2_sextic_chi5.c new file mode 100644 index 0000000000..d83b0405c7 --- /dev/null +++ b/src/acb_theta/test/t-g2_sextic_chi5.c @@ -0,0 +1,80 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "acb_poly.h" +#include "acb_mat.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_g2_sextic_chi5, state) +{ + slong iter; + + /* Test: agrees with sextic and chi5 */ + for (iter = 0; iter < 20 * flint_test_multiplier(); iter++) + { + slong g = 2; + slong n = 1 << (2 * g); + slong prec = 100 + n_randint(state, 100); + slong bits = n_randint(state, 4); + acb_mat_t tau; + acb_ptr z, th; + acb_poly_t s1, s2; + acb_t c1, c2; + + acb_mat_init(tau, g, g); + z = _acb_vec_init(g); + th = _acb_vec_init(n); + acb_poly_init(s1); + acb_poly_init(s2); + acb_init(c1); + acb_init(c2); + + acb_siegel_randtest_reduced(tau, state, prec, bits); + acb_mat_scalar_mul_2exp_si(tau, tau, -2); + + acb_theta_g2_sextic_chi5(s1, c1, tau, prec); + acb_theta_g2_sextic(s2, tau, prec); + + if (!acb_poly_overlaps(s1, s2)) + { + flint_printf("FAIL (sextic)\n"); + acb_poly_printd(s1, 5); + flint_printf("\n"); + acb_poly_printd(s2, 5); + flint_printf("\n"); + flint_abort(); + } + + acb_theta_all(th, z, tau, 0, prec); + acb_theta_g2_chi5(c2, th, prec); + + if (!acb_overlaps(c1, c2)) + { + flint_printf("FAIL (chi5)\n"); + acb_printd(c1, 10); + flint_printf("\n"); + acb_printd(c2, 10); + flint_printf("\n"); + flint_abort(); + } + + acb_mat_clear(tau); + _acb_vec_clear(z, g); + _acb_vec_clear(th, n); + acb_poly_clear(s1); + acb_poly_clear(s2); + acb_clear(c1); + acb_clear(c2); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-g2_transvectant.c b/src/acb_theta/test/t-g2_transvectant.c new file mode 100644 index 0000000000..51836d9e84 --- /dev/null +++ b/src/acb_theta/test/t-g2_transvectant.c @@ -0,0 +1,78 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "acb_poly.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_g2_transvectant, state) +{ + slong iter; + + /* Test: (f,f)_6 = -3*a3^2 + 8*a2*a4 - 20*a1*a5 + 120*a0*a6 */ + for (iter = 0; iter < 500 * flint_test_multiplier(); iter++) + { + slong prec = 200; + slong bits = 2; + acb_poly_t f, g; + acb_t c, test; + + acb_poly_init(f); + acb_poly_init(g); + acb_init(c); + acb_init(test); + + acb_poly_randtest(f, state, 6, prec, bits); + acb_poly_set_coeff_si(f, 6, 1); + + acb_theta_g2_transvectant(g, f, f, 6, 6, 6, prec); + + if (acb_poly_degree(g) > 0) + { + flint_printf("FAIL (degree)\n"); + acb_poly_printd(f, 5); + flint_printf("\n"); + acb_poly_printd(g, 5); + flint_printf("\n"); + flint_abort(); + } + + acb_mul(c, acb_poly_get_coeff_ptr(f, 0), acb_poly_get_coeff_ptr(f, 6), prec); + acb_addmul_si(test, c, 120, prec); + acb_mul(c, acb_poly_get_coeff_ptr(f, 1), acb_poly_get_coeff_ptr(f, 5), prec); + acb_addmul_si(test, c, -20, prec); + acb_mul(c, acb_poly_get_coeff_ptr(f, 2), acb_poly_get_coeff_ptr(f, 4), prec); + acb_addmul_si(test, c, 8, prec); + acb_sqr(c, acb_poly_get_coeff_ptr(f, 3), prec); + acb_addmul_si(test, c, -3, prec); + + acb_poly_get_coeff_acb(c, g, 0); + acb_mul_si(c, c, 60, prec); + + if (!acb_overlaps(test, c)) + { + flint_printf("FAIL (value)\n"); + acb_printd(test, 5); + flint_printf("\n"); + acb_printd(c, 5); + flint_printf("\n"); + flint_abort(); + } + + acb_poly_clear(f); + acb_poly_clear(g); + acb_clear(c); + acb_clear(test); + } + + TEST_FUNCTION_END(state); +} + diff --git a/src/acb_theta/test/t-g2_transvectant_lead.c b/src/acb_theta/test/t-g2_transvectant_lead.c new file mode 100644 index 0000000000..a6be81b82d --- /dev/null +++ b/src/acb_theta/test/t-g2_transvectant_lead.c @@ -0,0 +1,68 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "acb_poly.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_g2_transvectant_lead, state) +{ + slong iter; + + /* Test: matches leading coefficient of g2_transvectant */ + for (iter = 0; iter < 100 * flint_test_multiplier(); iter++) + { + slong prec = 200; + slong bits = 2; + acb_poly_t f, g, h; + acb_t c, t; + slong m = n_randint(state, 10); + slong n = n_randint(state, 10); + slong k = n_randint(state, FLINT_MIN(m, n) + 1); + + acb_poly_init(f); + acb_poly_init(g); + acb_poly_init(h); + acb_init(c); + acb_init(t); + + acb_poly_randtest(f, state, m, prec, bits); + acb_poly_set_coeff_si(f, m, 1); + acb_poly_randtest(g, state, n, prec, bits); + acb_poly_set_coeff_si(g, n, 1); + + acb_theta_g2_transvectant(h, f, g, m, n, k, prec); + acb_poly_get_coeff_acb(t, h, m + n - 2 * k); + acb_theta_g2_transvectant_lead(c, f, g, m, n, k, prec); + + if (!acb_overlaps(c, t)) + { + flint_printf("FAIL\n"); + flint_printf("m = %wd, n = %wd, k = %wd, f, g:\n", m, n, k); + acb_poly_printd(f, 5); + acb_poly_printd(g, 5); + flint_printf("lead, test:\n"); + acb_printd(c, 5); + flint_printf("\n"); + acb_printd(t, 5); + flint_printf("\n"); + flint_abort(); + } + + acb_poly_clear(f); + acb_poly_clear(g); + acb_poly_clear(h); + acb_clear(c); + acb_clear(t); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-jet_all.c b/src/acb_theta/test/t-jet_all.c new file mode 100644 index 0000000000..da1bc38571 --- /dev/null +++ b/src/acb_theta/test/t-jet_all.c @@ -0,0 +1,75 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "acb_mat.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_jet_all, state) +{ + slong iter; + + /* Test: agrees with jet_naive_all */ + for (iter = 0; iter < 10 * flint_test_multiplier(); iter++) + { + slong g = 1 + n_randint(state, 2); + slong ord = n_randint(state, 3); + slong nb = acb_theta_jet_nb(ord, g); + slong n2 = 1 << (2 * g); + slong prec = 100 + n_randint(state, 400); + slong bits = n_randint(state, 4); + acb_mat_t tau; + acb_ptr z, dth, test; + slong k; + + acb_mat_init(tau, g, g); + z = _acb_vec_init(g); + dth = _acb_vec_init(nb * n2); + test = _acb_vec_init(nb * n2); + + /* Sample tau not too far from reduced domain */ + acb_siegel_randtest_reduced(tau, state, prec, bits); + acb_mat_scalar_mul_2exp_si(tau, tau, -1); + acb_siegel_randtest_vec(z, state, g, prec); + + /* Sometimes phony input too */ + if (n_randint(state, 20) == 0) + { + k = n_randint(state, g); + arb_zero(acb_imagref(acb_mat_entry(tau, k, k))); + } + + acb_theta_jet_all(dth, z, tau, ord, prec); + acb_theta_jet_naive_all(test, z, tau, ord, prec); + + if (!_acb_vec_overlaps(dth, test, nb * n2)) + { + flint_printf("FAIL\n"); + flint_printf("g = %wd, prec = %wd, ord = %wd, tau, z:\n", g, prec, ord); + _acb_vec_printd(z, g, 5); + acb_mat_printd(tau, 5); + flint_printf("th, test:\n"); + _acb_vec_printd(dth, nb * n2, 5); + _acb_vec_printd(test, nb * n2, 5); + flint_printf("difference:\n"); + _acb_vec_sub(test, dth, test, nb * n2, prec); + _acb_vec_printd(test, nb * n2, 5); + flint_abort(); + } + + acb_mat_clear(tau); + _acb_vec_clear(z, g); + _acb_vec_clear(dth, nb * n2); + _acb_vec_clear(test, nb * n2); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-jet_compose.c b/src/acb_theta/test/t-jet_compose.c new file mode 100644 index 0000000000..57da53e4aa --- /dev/null +++ b/src/acb_theta/test/t-jet_compose.c @@ -0,0 +1,70 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "acb_mat.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_jet_compose, state) +{ + slong iter; + + /* Test: chain rule */ + for (iter = 0; iter < 100 * flint_test_multiplier(); iter++) + { + slong g = 1 + n_randint(state, 4); + slong ord = n_randint(state, 4); + slong nb = acb_theta_jet_nb(ord, g); + slong prec = 200; + slong mag_bits = 2; + acb_mat_t N1, N2, N3; + acb_ptr v1, v2, v3, test; + slong k; + + acb_mat_init(N1, g, g); + acb_mat_init(N2, g, g); + acb_mat_init(N3, g, g); + v1 = _acb_vec_init(nb); + v2 = _acb_vec_init(nb); + v3 = _acb_vec_init(nb); + test = _acb_vec_init(nb); + + for (k = 0; k < nb; k++) + { + acb_randtest_precise(&v3[k], state, prec, mag_bits); + } + acb_mat_randtest(N1, state, prec, mag_bits); + acb_mat_randtest(N2, state, prec, mag_bits); + acb_mat_mul(N3, N2, N1, prec); + + acb_theta_jet_compose(v2, v3, N2, ord, prec); + acb_theta_jet_compose(v1, v2, N1, ord, prec); + acb_theta_jet_compose(test, v3, N3, ord, prec); + + if (!_acb_vec_overlaps(test, v1, nb)) + { + flint_printf("FAIL (g = %wd, ord = %wd)\n", g, ord); + _acb_vec_printd(v3, nb, 5); + _acb_vec_printd(test, nb, 5); + flint_abort(); + } + + acb_mat_clear(N1); + acb_mat_clear(N2); + acb_mat_clear(N3); + _acb_vec_clear(v1, nb); + _acb_vec_clear(v2, nb); + _acb_vec_clear(v3, nb); + _acb_vec_clear(test, nb); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-jet_error_bounds.c b/src/acb_theta/test/t-jet_error_bounds.c new file mode 100644 index 0000000000..4c27fff45a --- /dev/null +++ b/src/acb_theta/test/t-jet_error_bounds.c @@ -0,0 +1,133 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "acb_mat.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_jet_error_bounds, state) +{ + slong iter; + + /* Test: compute theta values at two points in a common ball */ + for (iter = 0; iter < 10 * flint_test_multiplier(); iter++) + { + slong g = 1 + n_randint(state, 3); + slong n = 1 << (2 * g); + slong ord = n_randint(state, 2); + slong bits = 2; + slong nb = acb_theta_jet_nb(ord, g); + slong nb_der = acb_theta_jet_nb(ord + 2, g); + slong lprec = ACB_THETA_LOW_PREC; + slong mprec = ACB_THETA_LOW_PREC + n_randint(state, 50); + slong hprec = mprec + n_randint(state, 50); + acb_mat_t tau1, tau2, tau3; + acb_ptr z1, z2, z3, dth; + arb_ptr err; + acb_ptr d1, d2, test; + acb_t x; + slong j, k; + + acb_mat_init(tau1, g, g); + acb_mat_init(tau2, g, g); + acb_mat_init(tau3, g, g); + z1 = _acb_vec_init(g); + z2 = _acb_vec_init(g); + z3 = _acb_vec_init(g); + dth = _acb_vec_init(n * nb_der); + err = _arb_vec_init(n * nb); + d1 = _acb_vec_init(n * nb); + d2 = _acb_vec_init(n * nb); + test = _acb_vec_init(n * nb); + acb_init(x); + + acb_siegel_randtest_reduced(tau1, state, hprec, bits); + acb_siegel_randtest_vec(z1, state, g, hprec); + + for (j = 0; j < g; j++) + { + for (k = j; k < g; k++) + { + acb_set(acb_mat_entry(tau1, k, j), acb_mat_entry(tau1, j, k)); + acb_urandom(x, state, hprec); + acb_mul_2exp_si(x, x, -mprec); + acb_add(acb_mat_entry(tau2, j, k), acb_mat_entry(tau1, j, k), x, hprec); + acb_set(acb_mat_entry(tau2, k, j), acb_mat_entry(tau2, j, k)); + acb_union(acb_mat_entry(tau3, j, k), acb_mat_entry(tau1, j, k), + acb_mat_entry(tau2, j, k), hprec); + acb_set(acb_mat_entry(tau3, k, j), acb_mat_entry(tau3, j, k)); + } + acb_urandom(x, state, hprec); + acb_mul_2exp_si(x, x, -mprec); + acb_add(&z2[j], &z1[j], x, hprec); + acb_union(&z3[j], &z1[j], &z2[j], hprec); + } + + if (!acb_mat_contains(tau3, tau2) || !acb_mat_contains(tau3, tau1) + || !_acb_vec_contains(z3, z1, g) || !_acb_vec_contains(z3, z1, g)) + { + flint_printf("FAIL (input)\n"); + flint_printf("mprec = %wd, hprec = %wd\n", mprec, hprec); + acb_mat_printd(tau1, 5); + acb_mat_printd(tau2, 5); + acb_mat_printd(tau3, 5); + _acb_vec_printd(z1, g, 5); + _acb_vec_printd(z2, g, 5); + _acb_vec_printd(z3, g, 5); + flint_abort(); + } + + acb_theta_jet_naive_all(d1, z1, tau1, ord, hprec); + acb_theta_jet_naive_all(d2, z2, tau2, ord, hprec); + acb_theta_jet_naive_all(dth, z3, tau3, ord + 2, lprec); + for (k = 0; k < n; k++) + { + acb_theta_jet_error_bounds(err + k * nb, z3, tau3, dth + k * nb_der, ord, lprec); + } + /* Errors are wrt midpoint, so multiply by 2 */ + _arb_vec_scalar_mul_2exp_si(err, err, n * nb, 1); + + _acb_vec_set(test, d1, n * nb); + for (k = 0; k < n * nb; k++) + { + acb_add_error_arb(&test[k], &err[k]); + } + + if (!_acb_vec_overlaps(test, d2, n * nb)) + { + flint_printf("FAIL (bounds)\n"); + flint_printf("values:\n"); + _acb_vec_printd(d1, n * nb, 5); + _acb_vec_printd(d2, n * nb, 5); + flint_printf("bounds:\n"); + _arb_vec_printd(err, n * nb, 5); + flint_printf("difference:\n"); + _acb_vec_sub(d1, d1, d2, n * nb, hprec); + _acb_vec_printd(d1, n * nb, 5); + flint_abort(); + } + + acb_mat_clear(tau1); + acb_mat_clear(tau2); + acb_mat_clear(tau3); + _acb_vec_clear(z1, g); + _acb_vec_clear(z2, g); + _acb_vec_clear(z3, g); + _acb_vec_clear(dth, n * nb_der); + _arb_vec_clear(err, n * nb); + _acb_vec_clear(d1, n * nb); + _acb_vec_clear(d2, n * nb); + _acb_vec_clear(test, n * nb); + acb_clear(x); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-jet_mul.c b/src/acb_theta/test/t-jet_mul.c new file mode 100644 index 0000000000..4172dbf87b --- /dev/null +++ b/src/acb_theta/test/t-jet_mul.c @@ -0,0 +1,90 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "fmpz_mpoly.h" +#include "acb.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_jet_mul, state) +{ + slong iter; + + /* Test: matches multiplication of fmpz_mpoly_t */ + for (iter = 0; iter < 100 * flint_test_multiplier(); iter++) + { + slong g = 1 + n_randint(state, 4); + slong ord = n_randint(state, 10); + slong nb = acb_theta_jet_nb(ord, g); + slong prec = 100; + slong * tups; + fmpz_mpoly_ctx_t ctx; + fmpz_mpoly_t p1, p2, p3; + fmpz_t c; + acb_ptr v1, v2, v3; + acb_t x; + slong k, t; + + tups = flint_malloc(g * nb * sizeof(slong)); + fmpz_mpoly_ctx_init(ctx, g, ORD_LEX); + fmpz_mpoly_init(p1, ctx); + fmpz_mpoly_init(p2, ctx); + fmpz_mpoly_init(p3, ctx); + fmpz_init(c); + v1 = _acb_vec_init(nb); + v2 = _acb_vec_init(nb); + v3 = _acb_vec_init(nb); + acb_init(x); + + acb_theta_jet_tuples(tups, ord, g); + for (k = 0; k < nb; k++) + { + t = n_randint(state, 100); + acb_set_si(&v1[k], t); + fmpz_mpoly_set_coeff_si_ui(p1, t, (ulong *) tups + k * g, ctx); + + t = n_randint(state, 100); + acb_set_si(&v2[k], t); + fmpz_mpoly_set_coeff_si_ui(p2, t, (ulong *) tups + k * g, ctx); + } + + acb_theta_jet_mul(v3, v1, v2, ord, g, prec); + fmpz_mpoly_mul(p3, p1, p2, ctx); + + for (k = 0; k < nb; k++) + { + fmpz_mpoly_get_coeff_fmpz_ui(c, p3, (ulong *) tups + k * g, ctx); + acb_set_fmpz(x, c); + if (!acb_eq(x, &v3[k])) + { + flint_printf("FAIL\n"); + flint_printf("g = %wd, ord = %wd, k = %wd, vectors:\n", g, ord, k); + _acb_vec_printd(v1, nb, 5); + _acb_vec_printd(v2, nb, 5); + _acb_vec_printd(v3, nb, 5); + flint_abort(); + } + } + + flint_free(tups); + fmpz_mpoly_clear(p1, ctx); + fmpz_mpoly_clear(p2, ctx); + fmpz_mpoly_clear(p3, ctx); + fmpz_mpoly_ctx_clear(ctx); + fmpz_clear(c); + _acb_vec_clear(v1, nb); + _acb_vec_clear(v2, nb); + _acb_vec_clear(v3, nb); + acb_clear(x); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-jet_naive_00.c b/src/acb_theta/test/t-jet_naive_00.c new file mode 100644 index 0000000000..e9fb6e6581 --- /dev/null +++ b/src/acb_theta/test/t-jet_naive_00.c @@ -0,0 +1,63 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "acb_mat.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_jet_naive_00, state) +{ + slong iter; + + /* Test: values match jet_naive_all */ + for (iter = 0; iter < 10 * flint_test_multiplier(); iter++) + { + slong prec = ACB_THETA_LOW_PREC + n_randint(state, 100); + slong bits = n_randint(state, 4); + slong ord = n_randint(state, 3); + slong g = 1 + n_randint(state, 3); + slong n2 = 1 << (2 * g); + slong nb = acb_theta_jet_nb(ord, g); + acb_mat_t tau; + acb_ptr z, dth, test; + + acb_mat_init(tau, g, g); + z = _acb_vec_init(g); + dth = _acb_vec_init(nb); + test = _acb_vec_init(nb * n2); + + acb_siegel_randtest_reduced(tau, state, prec, bits); + acb_siegel_randtest_vec(z, state, g, prec); + + acb_theta_jet_naive_00(dth, z, tau, ord, prec); + acb_theta_jet_naive_all(test, z, tau, ord, prec); + + if (!_acb_vec_overlaps(dth, test, nb)) + { + flint_printf("FAIL (overlap)\n"); + flint_printf("g = %wd, prec = %wd, ord = %wd\n", g, prec, ord); + acb_mat_printd(tau, 5); + _acb_vec_printd(z, g, 5); + flint_printf("jet_naive_00:\n"); + _acb_vec_printd(dth, nb, 5); + flint_printf("jet_naive_all:\n"); + _acb_vec_printd(test, nb, 5); + flint_abort(); + } + + acb_mat_clear(tau); + _acb_vec_clear(z, g); + _acb_vec_clear(dth, nb); + _acb_vec_clear(test, nb * n2); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-jet_naive_all.c b/src/acb_theta/test/t-jet_naive_all.c new file mode 100644 index 0000000000..30c4a21a92 --- /dev/null +++ b/src/acb_theta/test/t-jet_naive_all.c @@ -0,0 +1,102 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "acb_mat.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_jet_naive_all, state) +{ + slong iter; + + /* Test: values match acb_modular_theta_jet on diagonal matrices */ + for (iter = 0; iter < 10 * flint_test_multiplier(); iter++) + { + slong g = 2 + n_randint(state, 2); + slong mprec = ACB_THETA_LOW_PREC + n_randint(state, 100); + slong prec = mprec + 50; + slong bits = n_randint(state, 4); + slong ord = n_randint(state, 3); + slong n2 = 1 << (2 * g); + slong nb = acb_theta_jet_nb(ord, g); + acb_mat_t tau, tau11; + acb_ptr z, dth, dth_g1, test; + acb_t prod, t; + slong * tups; + slong k, j, l, ab; + + acb_mat_init(tau, g, g); + acb_mat_init(tau11, 1, 1); + z = _acb_vec_init(g); + dth = _acb_vec_init(nb * n2); + dth_g1 = _acb_vec_init((ord + 1) * g * 4); + test = _acb_vec_init(nb * n2); + acb_init(prod); + acb_init(t); + tups = flint_malloc(nb * g * sizeof(slong)); + + for (k = 0; k < g; k++) + { + acb_siegel_randtest_reduced(tau11, state, prec, bits); + acb_set(acb_mat_entry(tau, k, k), acb_mat_entry(tau11, 0, 0)); + } + acb_siegel_randtest_vec(z, state, g, prec); + + acb_theta_jet_naive_all(dth, z, tau, ord, mprec); + for (k = 0; k < g; k++) + { + acb_set(acb_mat_entry(tau11, 0, 0), acb_mat_entry(tau, k, k)); + acb_theta_jet_naive_all(dth_g1 + k * (ord + 1) * 4, &z[k], tau11, ord, prec); + } + + /* Make test vector using products of derivatives wrt each variable */ + acb_theta_jet_tuples(tups, ord, g); + for (j = 0; j < nb; j++) + { + for (k = 0; k < n2; k++) + { + acb_one(prod); + for (l = 0; l < g; l++) + { + ab = 2 * ((k >> (2 * g - l - 1)) % 2) + ((k >> (g - l - 1)) % 2); + acb_mul(prod, prod, + &dth_g1[l * (ord + 1) * 4 + ab * (ord + 1) + tups[j * g + l]], prec); + } + acb_set(&test[k * nb + j], prod); + } + } + + if (!_acb_vec_overlaps(dth, test, n2 * nb)) + { + flint_printf("FAIL (overlap)\n"); + flint_printf("g = %wd, prec = %wd, ord = %wd\n", g, prec, ord); + acb_mat_printd(tau, 5); + _acb_vec_printd(z, g, 5); + flint_printf("jet_naive_all:\n"); + _acb_vec_printd(dth, nb * n2, 5); + flint_printf("test:\n"); + _acb_vec_printd(test, nb * n2, 5); + flint_abort(); + } + + acb_mat_clear(tau); + acb_mat_clear(tau11); + _acb_vec_clear(z, g); + _acb_vec_clear(dth, nb * n2); + _acb_vec_clear(dth_g1, (ord + 1) * g * 4); + _acb_vec_clear(test, nb * n2); + acb_clear(prod); + acb_clear(t); + flint_free(tups); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-jet_naive_fixed_ab.c b/src/acb_theta/test/t-jet_naive_fixed_ab.c new file mode 100644 index 0000000000..a396e4b751 --- /dev/null +++ b/src/acb_theta/test/t-jet_naive_fixed_ab.c @@ -0,0 +1,64 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "acb_mat.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_jet_naive_fixed_ab, state) +{ + slong iter; + + /* Test: values match jet_naive_all */ + for (iter = 0; iter < 10 * flint_test_multiplier(); iter++) + { + slong prec = ACB_THETA_LOW_PREC + n_randint(state, 100); + slong bits = n_randint(state, 4); + slong ord = n_randint(state, 2); + slong g = 1 + n_randint(state, 3); + slong n2 = 1 << (2 * g); + ulong ab = n_randint(state, n2); + slong nb = acb_theta_jet_nb(ord, g); + acb_mat_t tau; + acb_ptr z, dth, test; + + acb_mat_init(tau, g, g); + z = _acb_vec_init(g); + dth = _acb_vec_init(nb); + test = _acb_vec_init(nb * n2); + + acb_siegel_randtest_reduced(tau, state, prec, bits); + acb_siegel_randtest_vec(z, state, g, prec); + + acb_theta_jet_naive_fixed_ab(dth, ab, z, tau, ord, prec); + acb_theta_jet_naive_all(test, z, tau, ord, prec); + + if (!_acb_vec_overlaps(dth, test + ab * nb, nb)) + { + flint_printf("FAIL (overlap)\n"); + flint_printf("g = %wd, prec = %wd, ord = %wd, ab = %wd\n", g, prec, ord, ab); + acb_mat_printd(tau, 5); + _acb_vec_printd(z, g, 5); + flint_printf("jet_naive_fixed_ab:\n"); + _acb_vec_printd(dth, nb, 5); + flint_printf("jet_naive_all:\n"); + _acb_vec_printd(test + ab * nb, nb, 5); + flint_abort(); + } + + acb_mat_clear(tau); + _acb_vec_clear(z, g); + _acb_vec_clear(dth, nb); + _acb_vec_clear(test, nb * n2); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-jet_naive_radius.c b/src/acb_theta/test/t-jet_naive_radius.c new file mode 100644 index 0000000000..4510830deb --- /dev/null +++ b/src/acb_theta/test/t-jet_naive_radius.c @@ -0,0 +1,133 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "arb_mat.h" +#include "acb_mat.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_jet_naive_radius, state) +{ + slong iter; + + /* Test: sum of terms on border of ellipsoid must be less than bound */ + for (iter = 0; iter < 20 * flint_test_multiplier(); iter++) + { + slong g = 1 + n_randint(state, 3); + slong mprec = 50 + n_randint(state, 100); + slong prec = mprec + 50; + slong bits = n_randint(state, 4); + slong ord = n_randint(state, 4); + slong nb = acb_theta_jet_nb(ord, g); + acb_theta_eld_t E; + acb_mat_t tau; + arb_mat_t C; + arf_t R2, eps; + acb_ptr z, new_z; + arb_ptr v, a; + acb_t c, term; + arb_t u, abs, sum; + slong nb_pts; + slong * pts; + slong * tups; + slong j, k; + int res; + + acb_mat_init(tau, g, g); + arb_mat_init(C, g, g); + arf_init(R2); + arf_init(eps); + acb_theta_eld_init(E, g, g); + z = _acb_vec_init(g); + new_z = _acb_vec_init(g); + v = _arb_vec_init(g); + a = _arb_vec_init(g); + acb_init(c); + acb_init(term); + arb_init(u); + arb_init(abs); + arb_init(sum); + tups = flint_malloc(g * nb * sizeof(slong)); + + acb_siegel_randtest_reduced(tau, state, prec, bits); + for (k = 0; k < g; k++) + { + acb_randtest_precise(&z[k], state, prec, bits); + } + acb_siegel_cho(C, tau, prec); + acb_theta_naive_reduce(v, new_z, a, c, u, z, 1, tau, prec); + + acb_theta_jet_naive_radius(R2, eps, C, v, ord, mprec); + arb_mul_arf(u, u, eps, prec); + + /* Test: sum of terms on the border of ellipsoid is less than u */ + res = acb_theta_eld_set(E, C, R2, v); + if (!res) + { + flint_printf("FAIL (ellipsoid)\n"); + flint_abort(); + } + + nb_pts = acb_theta_eld_nb_border(E); + pts = flint_malloc(g * nb_pts * sizeof(slong)); + acb_theta_eld_border(pts, E); + acb_theta_jet_tuples(tups, ord, g); + + for (j = 0; j < nb; j++) + { + arb_zero(sum); + for (k = 0; k < nb_pts; k++) + { + acb_theta_naive_term(term, new_z, tau, tups + j * g, pts + k * g, prec); + acb_abs(abs, term, prec); + arb_add(sum, sum, abs, prec); + } + + acb_abs(abs, c, prec); + arb_mul(sum, sum, abs, prec); + arb_sub(abs, sum, u, prec); + + if (arb_is_positive(abs)) + { + flint_printf("FAIL\n"); + flint_printf("sum, bound:\n"); + arb_printd(sum, 10); + flint_printf("\n"); + arb_printd(u, 10); + flint_printf("\ntau:\n"); + acb_mat_printd(tau, 5); + flint_printf("z:\n"); + _acb_vec_printd(z, g, 10); + acb_theta_eld_print(E); + flint_abort(); + } + } + + acb_mat_clear(tau); + arb_mat_clear(C); + arf_clear(R2); + arf_clear(eps); + acb_theta_eld_clear(E); + _acb_vec_clear(z, g); + _acb_vec_clear(new_z, g); + _arb_vec_clear(v, g); + _arb_vec_clear(a, g); + acb_clear(c); + acb_clear(term); + arb_clear(u); + arb_clear(abs); + arb_clear(sum); + flint_free(pts); + flint_free(tups); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-jet_ql_all.c b/src/acb_theta/test/t-jet_ql_all.c new file mode 100644 index 0000000000..9f619ea784 --- /dev/null +++ b/src/acb_theta/test/t-jet_ql_all.c @@ -0,0 +1,63 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "acb_mat.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_jet_ql_all, state) +{ + slong iter; + + /* Test: matches jet_naive_all */ + for (iter = 0; iter < 20 * flint_test_multiplier(); iter++) + { + slong prec = ACB_THETA_LOW_PREC + n_randint(state, 100); + slong ord = n_randint(state, 3); + slong g = 1 + n_randint(state, 2); + slong n2 = 1 << (2 * g); + slong nb = acb_theta_jet_nb(ord, g); + slong bits = 6; + acb_mat_t tau; + acb_ptr z, dth, test; + + acb_mat_init(tau, g, g); + z = _acb_vec_init(g); + dth = _acb_vec_init(nb * n2); + test = _acb_vec_init(nb * n2); + + acb_siegel_randtest_reduced(tau, state, prec, bits); + acb_siegel_randtest_vec(z, state, g, prec); + + acb_theta_jet_ql_all(dth, z, tau, ord, prec); + acb_theta_jet_naive_all(test, z, tau, ord, prec); + + if (!_acb_vec_overlaps(dth, test, nb * n2)) + { + flint_printf("FAIL (overlap)\n"); + flint_printf("g = %wd, prec = %wd, ord = %wd\n", g, prec, ord); + acb_mat_printd(tau, 5); + _acb_vec_printd(z, g, 5); + flint_printf("dth:\n"); + _acb_vec_printd(dth, nb * n2, 5); + flint_printf("test:\n"); + _acb_vec_printd(test, nb * n2, 5); + flint_abort(); + } + + acb_mat_clear(tau); + _acb_vec_clear(z, g); + _acb_vec_clear(dth, nb * n2); + _acb_vec_clear(test, nb * n2); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-jet_ql_bounds.c b/src/acb_theta/test/t-jet_ql_bounds.c new file mode 100644 index 0000000000..2b495fe9be --- /dev/null +++ b/src/acb_theta/test/t-jet_ql_bounds.c @@ -0,0 +1,106 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "acb_mat.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_jet_ql_bounds, state) +{ + slong iter; + + /* Test: bounds are finite, theta values correctly bounded */ + for (iter = 0; iter < 20 * flint_test_multiplier(); iter++) + { + slong lp = ACB_THETA_LOW_PREC; + slong prec = lp + 100; + slong bits = n_randint(state, 4); + slong ord = 1 + n_randint(state, 10); + slong g = 1 + n_randint(state, 3); + slong n2 = 1 << (2 * g); + acb_mat_t tau; + acb_ptr z, x, th; + arb_t c, rho, abs, t; + slong k; + + acb_mat_init(tau, g, g); + z = _acb_vec_init(g); + x = _acb_vec_init(g); + th = _acb_vec_init(n2); + arb_init(c); + arb_init(rho); + arb_init(abs); + arb_init(t); + + acb_siegel_randtest_reduced(tau, state, prec, bits); + acb_mat_scalar_mul_2exp_si(tau, tau, -2); + for (k = 0; k < g; k++) + { + acb_urandom(&z[k], state, prec); + } + + acb_theta_jet_ql_bounds(c, rho, z, tau, ord); + + if (!arb_is_finite(rho) || !arb_is_finite(c)) + { + flint_printf("FAIL (infinite)\n"); + acb_mat_printd(tau, 5); + _acb_vec_printd(z, g, 5); + flint_printf("c, rho:\n"); + arb_printd(c, 10); + flint_printf("\n"); + arb_printd(rho, 10); + flint_printf("\n"); + flint_abort(); + } + + for (k = 0; k < g; k++) + { + acb_urandom(&x[k], state, prec); + } + _acb_vec_scalar_mul_arb(x, x, g, rho, prec); + _acb_vec_add(x, x, z, g, prec); + acb_theta_naive_all(th, x, 1, tau, lp); + + arb_zero(abs); + for (k = 0; k < n2; k++) + { + acb_abs(t, &th[k], lp); + arb_max(abs, abs, t, lp); + } + + if (arb_gt(abs, c)) + { + flint_printf("FAIL (bound)\n"); + acb_mat_printd(tau, 5); + _acb_vec_printd(z, g, 5); + flint_printf("rho, c, abs:\n"); + arb_printd(rho, 10); + flint_printf("\n"); + arb_printd(c, 10); + flint_printf("\n"); + arb_printd(abs, 10); + flint_printf("\n"); + flint_abort(); + } + + acb_mat_clear(tau); + _acb_vec_clear(z, g); + _acb_vec_clear(x, g); + _acb_vec_clear(th, n2); + arb_clear(c); + arb_clear(rho); + arb_clear(abs); + arb_clear(t); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-jet_ql_finite_diff.c b/src/acb_theta/test/t-jet_ql_finite_diff.c new file mode 100644 index 0000000000..31dab902e7 --- /dev/null +++ b/src/acb_theta/test/t-jet_ql_finite_diff.c @@ -0,0 +1,117 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "ulong_extras.h" +#include "acb.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_jet_ql_finite_diff, state) +{ + slong iter; + + /* Test: find correct coefficients for exp function */ + for (iter = 0; iter < 100 * flint_test_multiplier(); iter++) + { + slong prec = 100 + n_randint(state, 1000); + slong ord = n_randint(state, 4); + slong g = 1 + n_randint(state, 4); + slong b = ord + 1; + slong nb_val = n_pow(b, g); + slong nb_fd = acb_theta_jet_nb(ord, g); + slong * tups; + arb_t c, rho; + arf_t eps, err; + acb_ptr val, df, test; + acb_t x, t; + fmpz_t m; + slong k, kk, j, i; + + tups = flint_malloc(g * nb_fd * sizeof(slong)); + arb_init(c); + arb_init(rho); + arf_init(eps); + arf_init(err); + val = _acb_vec_init(nb_val); + df = _acb_vec_init(nb_fd); + test = _acb_vec_init(nb_fd); + acb_init(x); + acb_init(t); + fmpz_init(m); + + /* Get c, rho, eps, err */ + arb_one(rho); + arb_set_si(c, g); + arb_exp(c, c, prec); + acb_theta_jet_ql_radius(eps, err, c, rho, ord, g, prec); + + /* Fill in values, apply jet_fd at 2*prec */ + for (k = 0; k < nb_val; k++) + { + acb_zero(x); + kk = k; + for (j = 0; j < g; j++) + { + acb_unit_root(t, b, 2 * prec); + acb_pow_ui(t, t, (kk % b), 2 * prec); + acb_add(x, x, t, 2 * prec); + kk = kk / b; + } + acb_zero(t); + arb_set_arf(acb_realref(t), eps); + acb_mul(x, x, t, 2 * prec); + acb_exp(&val[k], x, 2 * prec); + } + acb_theta_jet_ql_finite_diff(df, eps, err, rho, val, ord, g, 2 * prec); + + /* Fill in test */ + acb_theta_jet_tuples(tups, ord, g); + for (j = 0; j < nb_fd; j++) + { + acb_one(x); + for (i = 0; i < g; i++) + { + fmpz_fac_ui(m, tups[j * g + i]); + acb_div_fmpz(x, x, m, prec); + } + acb_set(&test[j], x); + } + + if (!_acb_vec_overlaps(df, test, nb_fd)) + { + flint_printf("FAIL\n"); + flint_printf("g = %wd, ord = %wd, values:\n", g, ord); + _acb_vec_printd(val, nb_val, 5); + flint_printf("taylor coeffs:\n"); + _acb_vec_printd(df, nb_fd, 5); + flint_printf("test:\n"); + _acb_vec_printd(test, nb_fd, 5); + flint_printf("difference:\n"); + _acb_vec_sub(test, test, df, nb_fd, prec); + _acb_vec_printd(test, nb_fd, 5); + flint_abort(); + } + + flint_free(tups); + arb_clear(c); + arb_clear(rho); + arf_clear(eps); + arf_clear(err); + _acb_vec_clear(val, nb_val); + _acb_vec_clear(df, nb_fd); + _acb_vec_clear(test, nb_fd); + acb_clear(x); + acb_clear(t); + fmpz_clear(m); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-jet_ql_radius.c b/src/acb_theta/test/t-jet_ql_radius.c new file mode 100644 index 0000000000..4664090821 --- /dev/null +++ b/src/acb_theta/test/t-jet_ql_radius.c @@ -0,0 +1,90 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "arb.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_jet_ql_radius, state) +{ + slong iter; + + /* Test: inequalities are satisfied */ + for (iter = 0; iter < 100 * flint_test_multiplier(); iter++) + { + slong prec = 100 + n_randint(state, 500); + slong mag_bits = n_randint(state, 10); + slong ord = n_randint(state, 10); + slong g = n_randint(state, 10); + arb_t c, rho, t, u; + arf_t eps, err; + + arb_init(c); + arb_init(rho); + arb_init(t); + arb_init(u); + arf_init(eps); + arf_init(err); + + arb_randtest_positive(c, state, prec, mag_bits); + arb_randtest_positive(rho, state, prec, mag_bits); + + acb_theta_jet_ql_radius(eps, err, c, rho, ord, g, prec); + + arb_set_si(t, 2 * g); + arb_root_ui(t, t, ord + 1, prec); + arb_mul_arf(t, t, eps, prec); + + if (arb_gt(t, rho)) + { + flint_printf("FAIL (1st bound)\n"); + flint_printf("c, rho, eps, err:\n"); + arb_printd(c, 10); + flint_printf("\n"); + arb_printd(rho, 10); + flint_printf("\n"); + arf_printd(eps, 10); + flint_printf("\n"); + flint_abort(); + } + + arb_set_arf(t, eps); + arb_pow_ui(t, t, ord + 1, prec); + arb_pow_ui(u, rho, 2 * ord + 1, prec); + arb_div(t, t, u, prec); + arb_mul(t, t, c, prec); + arb_mul_si(t, t, 2 * g, prec); + arb_sub_arf(t, t, err, prec); + + if (arb_is_positive(t)) + { + flint_printf("FAIL (2nd bound)\n"); + flint_printf("c, rho, eps, err:\n"); + arb_printd(c, 10); + flint_printf("\n"); + arb_printd(rho, 10); + flint_printf("\n"); + arf_printd(eps, 10); + flint_printf("\n"); + arf_printd(err, 10); + flint_abort(); + } + + arb_clear(c); + arb_clear(rho); + arb_clear(t); + arb_clear(u); + arf_clear(eps); + arf_clear(err); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-jet_tuples.c b/src/acb_theta/test/t-jet_tuples.c new file mode 100644 index 0000000000..a15513d501 --- /dev/null +++ b/src/acb_theta/test/t-jet_tuples.c @@ -0,0 +1,56 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_jet_tuples, state) +{ + slong iter; + + /* Test: get the right index */ + for (iter = 0; iter < 1000 * flint_test_multiplier(); iter++) + { + slong g = 1 + n_randint(state, 6); + slong ord = n_randint(state, 6); + slong nb = acb_theta_jet_nb(ord, g); + slong * tups; + slong i = n_randint(state, nb); + slong test; + slong j, k; + + tups = flint_malloc(nb * g * sizeof(slong)); + + acb_theta_jet_tuples(tups, ord, g); + test = acb_theta_jet_index(tups + i * g, g); + + if (test != i) + { + flint_printf("FAIL\n"); + flint_printf("g = %wd, ord = %wd, nb = %wd\n", g, ord, nb); + flint_printf("tups:\n"); + for (j = 0; j < nb; j++) + { + for (k = 0; k < g; k++) + { + flint_printf("%wd ", tups[j * g + k]); + } + flint_printf("\n"); + } + flint_printf("i = %wd, test = %wd\n", i, test); + flint_abort(); + } + + flint_free(tups); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-naive_00.c b/src/acb_theta/test/t-naive_00.c new file mode 100644 index 0000000000..74138b514c --- /dev/null +++ b/src/acb_theta/test/t-naive_00.c @@ -0,0 +1,72 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "acb_mat.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_naive_00, state) +{ + slong iter; + + /* Test: agrees with first entry of naive_0b */ + for (iter = 0; iter < 20 * flint_test_multiplier(); iter++) + { + slong g = 1 + n_randint(state, 3); + slong n = 1 << g; + acb_mat_t tau; + acb_ptr z; + slong nbz = 1 + n_randint(state, 4); + acb_ptr th, th_0b, test; + slong prec1 = 100 + n_randint(state, 200); + slong prec = prec1 + n_randint(state, 200); + slong mag_bits = n_randint(state, 10); + slong k; + + acb_mat_init(tau, g, g); + z = _acb_vec_init(g * nbz); + th = _acb_vec_init(nbz); + th_0b = _acb_vec_init(n * nbz); + test = _acb_vec_init(nbz); + + acb_siegel_randtest_reduced(tau, state, prec, mag_bits); + acb_siegel_randtest_vec(z, state, g * nbz, prec); + + acb_theta_naive_00(th, z, nbz, tau, prec1); + acb_theta_naive_0b(th_0b, z, nbz, tau, prec); + for (k = 0; k < nbz; k++) + { + acb_set(&test[k], &th_0b[k * n]); + } + + if (!_acb_vec_overlaps(th, test, nbz)) + { + flint_printf("FAIL: overlap\n"); + flint_printf("g = %wd, prec1 = %wd, prec = %wd, nbz = %wd, tau:\n", + g, prec1, prec, nbz); + acb_mat_printd(tau, 5); + flint_printf("z:\n"); + _acb_vec_printd(z, g * nbz, 5); + flint_printf("th, test:\n"); + _acb_vec_printd(th, nbz, 5); + _acb_vec_printd(test, nbz, 5); + flint_abort(); + } + + acb_mat_clear(tau); + _acb_vec_clear(z, g * nbz); + _acb_vec_clear(th, nbz); + _acb_vec_clear(th_0b, n * nbz); + _acb_vec_clear(test, nbz); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-naive_all.c b/src/acb_theta/test/t-naive_all.c new file mode 100644 index 0000000000..f0a925cbc6 --- /dev/null +++ b/src/acb_theta/test/t-naive_all.c @@ -0,0 +1,98 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "acb_mat.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_naive_all, state) +{ + slong iter; + + /* Test: agrees with built-in genus 1 on diagonal matrices */ + for (iter = 0; iter < 20 * flint_test_multiplier(); iter++) + { + slong g = 2 + n_randint(state, 2); + slong nb = n_pow(2, 2 * g); + slong prec1 = ACB_THETA_LOW_PREC + n_randint(state, 200); + slong prec = prec1 + n_randint(state, 100); + slong mag_bits = n_randint(state, 2); + slong nbz = 1 + n_randint(state, 4); + acb_mat_t tau, tau11; + acb_ptr z, th, th_test, th_g1; + slong k, j; + ulong ab, a, b; + + acb_mat_init(tau, g, g); + acb_mat_init(tau11, 1, 1); + z = _acb_vec_init(g * nbz); + th = _acb_vec_init(nb * nbz); + th_test = _acb_vec_init(nb * nbz); + th_g1 = _acb_vec_init(4 * g); + + for (k = 0; k < g; k++) + { + acb_siegel_randtest_reduced(tau11, state, prec, mag_bits); + acb_set(acb_mat_entry(tau, k, k), acb_mat_entry(tau11, 0, 0)); + } + acb_siegel_randtest_vec(z, state, g * nbz, prec); + + acb_theta_naive_all(th, z, nbz, tau, prec1); + + for (j = 0; j < nbz; j++) + { + for (k = 0; k < g; k++) + { + acb_set(acb_mat_entry(tau11, 0, 0), acb_mat_entry(tau, k, k)); + acb_theta_naive_all(&th_g1[4 * k], &z[j * g + k], 1, tau11, prec); + } + /* Could use a more efficient recursive algorithm here */ + for (ab = 0; ab < n_pow(2, 2 * g); ab++) + { + a = ab >> g; + b = ab; + acb_one(&th_test[j * nb + ab]); + for (k = g - 1; k >= 0; k--) + { + acb_mul(&th_test[j * nb + ab], &th_test[j * nb + ab], + &th_g1[4 * k + 2 * (a % 2) + (b % 2)], prec); + a = a >> 1; + b = b >> 1; + } + } + } + + if (!_acb_vec_overlaps(th, th_test, nb * nbz)) + { + flint_printf("FAIL: overlap\n"); + flint_printf("g = %wd, prec = %wd, nbz = %wd, tau:\n", g, prec, nbz); + acb_mat_printd(tau, 10); + flint_printf("z:\n"); + _acb_vec_printd(z, g * nbz, 10); + flint_printf("th, th_test:\n"); + _acb_vec_printd(th, nb * nbz, 10); + _acb_vec_printd(th_test, nb * nbz, 10); + flint_printf("difference:\n"); + _acb_vec_sub(th_test, th_test, th, nb * nbz, prec); + _acb_vec_printd(th_test, nb * nbz, 10); + flint_abort(); + } + + acb_mat_clear(tau); + acb_mat_clear(tau11); + _acb_vec_clear(z, g * nbz); + _acb_vec_clear(th, nb * nbz); + _acb_vec_clear(th_test, nb * nbz); + _acb_vec_clear(th_g1, 4 * g); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-naive_fixed_a.c b/src/acb_theta/test/t-naive_fixed_a.c new file mode 100644 index 0000000000..da64cefe0e --- /dev/null +++ b/src/acb_theta/test/t-naive_fixed_a.c @@ -0,0 +1,76 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "acb_mat.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_naive_fixed_a, state) +{ + slong iter; + + /* Test: agrees with naive_all */ + for (iter = 0; iter < 20 * flint_test_multiplier(); iter++) + { + slong g = 1 + n_randint(state, 3); + slong n = 1 << g; + slong nbz = 1 + n_randint(state, 2); + acb_mat_t tau; + acb_ptr z; + acb_ptr th, th_all, th_test; + slong prec = 20 + n_randint(state, 100); + slong mag_bits = n_randint(state, 2); + slong k, a; + + acb_mat_init(tau, g, g); + z = _acb_vec_init(g * nbz); + th = _acb_vec_init(n * nbz); + th_all = _acb_vec_init(n * n * nbz); + th_test = _acb_vec_init(n * nbz); + + acb_siegel_randtest_reduced(tau, state, prec, mag_bits); + acb_siegel_randtest_vec(z, state, g * nbz, prec); + + acb_theta_naive_all(th_all, z, nbz, tau, prec); + + for (a = 0; a < n; a++) + { + acb_theta_naive_fixed_a(th, a, z, nbz, tau, prec); + for (k = 0; k < nbz; k++) + { + _acb_vec_set(th_test + k * n, th_all + k * n * n + a * n, n); + } + if (!_acb_vec_overlaps(th, th_test, n * nbz)) + { + flint_printf("FAIL\n"); + flint_printf("g = %wd, prec = %wd, nbz = %wd, a = %wd, tau:\n", + g, prec, nbz, a); + acb_mat_printd(tau, 5); + flint_printf("z:\n"); + _acb_vec_printd(z, g * nbz, 10); + flint_printf("th, th_test:\n"); + _acb_vec_printd(th, n * nbz, 10); + _acb_vec_printd(th_test, n * nbz, 10); + flint_printf("th_all:\n"); + _acb_vec_printd(th_all, n * n * nbz, 10); + flint_abort(); + } + } + + acb_mat_clear(tau); + _acb_vec_clear(z, g * nbz); + _acb_vec_clear(th, n * nbz); + _acb_vec_clear(th_all, n * n * nbz); + _acb_vec_clear(th_test, n * nbz); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-naive_fixed_ab.c b/src/acb_theta/test/t-naive_fixed_ab.c new file mode 100644 index 0000000000..5cd9a3fc3b --- /dev/null +++ b/src/acb_theta/test/t-naive_fixed_ab.c @@ -0,0 +1,77 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "acb_mat.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_naive_fixed_ab, state) +{ + slong iter; + + /* Test: agrees with naive_all */ + for (iter = 0; iter < 20 * flint_test_multiplier(); iter++) + { + slong g = 1 + n_randint(state, 3); + slong nb = n_pow(2, g); + slong nbz = 1 + n_randint(state, 2); + acb_mat_t tau; + acb_ptr z; + acb_ptr th, th_all, th_test; + slong prec = 20 + n_randint(state, 100); + slong mag_bits = n_randint(state, 2); + ulong ab; + slong k; + + acb_mat_init(tau, g, g); + z = _acb_vec_init(g * nbz); + th = _acb_vec_init(nbz); + th_all = _acb_vec_init(nb * nb * nbz); + th_test = _acb_vec_init(nbz); + + acb_siegel_randtest_reduced(tau, state, prec, mag_bits); + acb_siegel_randtest_vec(z, state, g * nbz, prec); + + acb_theta_naive_all(th_all, z, nbz, tau, prec); + + for (ab = 0; ab < nb * nb; ab++) + { + acb_theta_naive_fixed_ab(th, ab, z, nbz, tau, prec); + for (k = 0; k < nbz; k++) + { + acb_set(&th_test[k], &th_all[k * nb * nb + ab]); + } + if (!_acb_vec_overlaps(th, th_test, nbz)) + { + flint_printf("FAIL\n"); + flint_printf("g = %wd, prec = %wd, nbz = %wd, ab = %wd, tau:\n", + g, prec, nbz, ab); + acb_mat_printd(tau, 5); + flint_printf("z:\n"); + _acb_vec_printd(z, g * nbz, 10); + flint_printf("th, th_test:\n"); + _acb_vec_printd(th, nbz, 10); + _acb_vec_printd(th_test, nbz, 10); + flint_printf("th_all:\n"); + _acb_vec_printd(th_all, nb * nb * nbz, 10); + flint_abort(); + } + } + + acb_mat_clear(tau); + _acb_vec_clear(z, g * nbz); + _acb_vec_clear(th, nbz); + _acb_vec_clear(th_all, nb * nb * nbz); + _acb_vec_clear(th_test, nbz); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-naive_radius.c b/src/acb_theta/test/t-naive_radius.c new file mode 100644 index 0000000000..7c8d660010 --- /dev/null +++ b/src/acb_theta/test/t-naive_radius.c @@ -0,0 +1,127 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "arb_mat.h" +#include "acb_mat.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_naive_radius, state) +{ + slong iter; + + /* Test: sum of terms on border of ellipsoid must be less than bound */ + for (iter = 0; iter < 100 * flint_test_multiplier(); iter++) + { + slong g = 1 + n_randint(state, 3); + slong mprec = 50 + n_randint(state, 100); + slong prec = mprec + 50; + slong bits = n_randint(state, 4); + acb_theta_eld_t E; + acb_mat_t tau; + arb_mat_t C; + arf_t R2, eps; + acb_ptr z, new_z; + arb_ptr v, a; + acb_t c, term; + arb_t u, abs, sum; + slong nb_pts; + slong * pts; + slong k; + int res; + + acb_mat_init(tau, g, g); + arb_mat_init(C, g, g); + arf_init(R2); + arf_init(eps); + acb_theta_eld_init(E, g, g); + z = _acb_vec_init(g); + new_z = _acb_vec_init(g); + v = _arb_vec_init(g); + a = _arb_vec_init(g); + acb_init(c); + arb_init(u); + acb_init(term); + arb_init(abs); + arb_init(sum); + + acb_siegel_randtest_reduced(tau, state, prec, bits); + for (k = 0; k < g; k++) + { + acb_randtest_precise(&z[k], state, prec, bits); + } + acb_siegel_cho(C, tau, prec); + acb_theta_naive_reduce(v, new_z, a, c, u, z, 1, tau, prec); + + acb_theta_naive_radius(R2, eps, C, 0, mprec); + arb_mul_arf(u, u, eps, prec); + + /* Test: sum of terms on the border of ellipsoid is less than u */ + res = acb_theta_eld_set(E, C, R2, v); + if (!res) + { + flint_printf("FAIL (ellipsoid)\n"); + acb_mat_printd(tau, 5); + arb_mat_printd(C, 5); + _acb_vec_printd(z, g, 5); + _arb_vec_printd(v, g, 5); + flint_abort(); + } + + nb_pts = acb_theta_eld_nb_border(E); + pts = flint_malloc(g * nb_pts * sizeof(slong)); + acb_theta_eld_border(pts, E); + + arb_zero(sum); + for (k = 0; k < nb_pts; k++) + { + acb_theta_naive_term(term, new_z, tau, NULL, pts + k * g, prec); + acb_abs(abs, term, prec); + arb_add(sum, sum, abs, prec); + } + acb_abs(abs, c, prec); + arb_mul(sum, sum, abs, prec); + arb_sub(abs, sum, u, prec); + + if (arb_is_positive(abs)) + { + flint_printf("FAIL\n"); + flint_printf("sum, bound:\n"); + arb_printd(sum, 10); + flint_printf("\n"); + arb_printd(u, 10); + flint_printf("\ntau:\n"); + acb_mat_printd(tau, 5); + flint_printf("new_z:\n"); + _acb_vec_printd(new_z, g, 10); + acb_theta_eld_print(E); + flint_abort(); + } + + acb_mat_clear(tau); + arb_mat_clear(C); + arf_clear(R2); + arf_clear(eps); + acb_theta_eld_clear(E); + _acb_vec_clear(z, g); + _acb_vec_clear(new_z, g); + _arb_vec_clear(v, g); + _arb_vec_clear(a, g); + acb_clear(c); + arb_clear(u); + acb_clear(term); + arb_clear(abs); + arb_clear(sum); + flint_free(pts); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-naive_reduce.c b/src/acb_theta/test/t-naive_reduce.c new file mode 100644 index 0000000000..91aafe189f --- /dev/null +++ b/src/acb_theta/test/t-naive_reduce.c @@ -0,0 +1,170 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "arb_mat.h" +#include "acb_mat.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_naive_reduce, state) +{ + slong iter; + + /* Test: special values of z */ + for (iter = 0; iter < 10 * flint_test_multiplier(); iter++) + { + slong g = 1 + n_randint(state, 5); + slong nbz = n_randint(state, 5); + slong bits = n_randint(state, 5); + slong prec = 100 + n_randint(state, 200); + acb_mat_t tau; + arb_mat_t Y, C; + acb_ptr z, new_z, c; + arb_ptr u, v, w, a; + acb_t t, x; + slong *n, *zero; + slong err_exp = - 10 - n_randint(state, 20); + slong k, j; + int res; + + acb_mat_init(tau, g, g); + arb_mat_init(Y, g, g); + arb_mat_init(C, g, g); + z = _acb_vec_init(g * nbz); + new_z = _acb_vec_init(g * nbz); + c = _acb_vec_init(nbz); + u = _arb_vec_init(nbz); + v = _arb_vec_init(g); + w = _arb_vec_init(g * nbz); + a = _arb_vec_init(g * nbz); + acb_init(t); + acb_init(x); + n = flint_malloc(g * nbz * sizeof(slong)); + zero = flint_malloc(g * sizeof(slong)); + + /* Set tau, cho, Y */ + acb_siegel_randtest_reduced(tau, state, prec, bits); + acb_siegel_cho(C, tau, prec); + acb_mat_get_imag(Y, tau); + + /* Test: if z is real, c = 1, u = 1 and v = 0 */ + for (k = 0; k < g * nbz; k++) + { + arb_randtest_precise(acb_realref(&z[k]), state, prec, bits); + } + acb_theta_naive_reduce(v, new_z, a, c, u, z, nbz, tau, prec); + + res = 1; + for (k = 0; k < nbz; k++) + { + res = res && acb_is_one(&c[k]); + res = res && arb_is_one(&u[k]); + } + + if (!_arb_vec_is_zero(v, g) || !res) + { + flint_printf("FAIL\n"); + flint_abort(); + } + + /* Test: if im(z) = - Y . (even integral vector n) + small error, + then terms for n and 0 correspond and v is small */ + for (j = 0; j < g; j++) + { + zero[j] = 0; + } + for (k = 0; k < nbz; k++) + { + for (j = k * g; j < (k + 1) * g; j++) + { + n[j] = 2 * n_randint(state, 10); + arb_set_si(&w[j], n[j]); + } + arb_mat_vector_mul_col(w + k * g, Y, w + k * g, prec); + for (j = k * g; j < (k + 1) * g; j++) + { + arb_urandom(acb_imagref(&z[j]), state, prec); + arb_mul_2exp_si(acb_imagref(&z[j]), acb_imagref(&z[j]), err_exp); + arb_sub(acb_imagref(&z[j]), acb_imagref(&z[j]), &w[j], prec); + } + } + acb_theta_naive_reduce(v, new_z, a, c, u, z, nbz, tau, prec); + + for (k = 0; k < g * nbz; k++) + { + if (!arb_equal_si(&a[k], -n[k])) + { + flint_printf("FAIL (integral vector)\n"); + _arb_vec_printd(a, g * nbz, 5); + flint_printf("k = %wd, n[k] = %wd\n", k, n[k]); + flint_abort(); + } + } + + for (k = 0; k < nbz; k++) + { + acb_theta_naive_term(x, z + k * g, tau, NULL, n + k * g, prec); + acb_theta_naive_term(t, new_z + k * g, tau, NULL, zero, prec); + acb_mul(t, t, &c[k], prec); + + if (!acb_overlaps(x, t)) + { + flint_printf("FAIL (value, k = %wd)\n", k); + flint_printf("tau:\n"); + acb_mat_printd(tau, 10); + flint_printf("z:\n"); + _acb_vec_printd(z + k * g, g, 10); + flint_printf("values:\n"); + acb_printd(x, 10); + flint_printf("\n"); + acb_printd(t, 10); + flint_printf("\n"); + acb_printd(&c[k], 10); + flint_printf("\nNew z:\n"); + _acb_vec_printd(new_z + k * g, g, 10); + flint_abort(); + } + } + + arb_mat_inv(C, C, prec); + arb_mat_vector_mul_col(v, C, v, prec); + for (k = 0; k < g; k++) + { + arb_mul_2exp_si(&v[k], &v[k], - err_exp - 2); + arb_sub_si(&v[k], &v[k], 1, prec); + if (!arb_is_negative(&v[k])) + { + flint_printf("FAIL (offset)\n"); + arb_printd(&v[k], 10); + flint_printf("\n"); + flint_abort(); + } + } + + acb_mat_clear(tau); + arb_mat_clear(Y); + arb_mat_clear(C); + _acb_vec_clear(z, g * nbz); + _acb_vec_clear(new_z, g * nbz); + _acb_vec_clear(c, nbz); + _arb_vec_clear(u, nbz); + _arb_vec_clear(v, g); + _arb_vec_clear(w, g * nbz); + _arb_vec_clear(a, g * nbz); + acb_clear(t); + acb_clear(x); + flint_free(n); + flint_free(zero); + } + + TEST_FUNCTION_END(state); +} + diff --git a/src/acb_theta/test/t-naive_term.c b/src/acb_theta/test/t-naive_term.c new file mode 100644 index 0000000000..e06d6e554b --- /dev/null +++ b/src/acb_theta/test/t-naive_term.c @@ -0,0 +1,67 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "fmpz.h" +#include "acb_mat.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_naive_term, state) +{ + slong iter; + + /* Test: agrees with genus 1 */ + for (iter = 0; iter < 1000 * flint_test_multiplier(); iter++) + { + slong g = 1; + slong prec = 100 + n_randint(state, 200); + slong bits = n_randint(state, 5); + slong n = n_randint(state, 100); + slong k = n_randint(state, 10); + acb_mat_t tau; + acb_t z; + acb_t x, t; + fmpz_t m; + + acb_mat_init(tau, g, g); + acb_init(z); + acb_init(x); + acb_init(t); + fmpz_init(m); + + acb_siegel_randtest(tau, state, prec, bits); + acb_randtest_precise(z, state, prec, bits); + + acb_theta_naive_term(x, z, tau, &k, &n, prec); + acb_mul_si(t, acb_mat_entry(tau, 0, 0), n, prec); + acb_addmul_si(t, z, 2, prec); + acb_mul_si(t, t, n, prec); + acb_exp_pi_i(t, t, prec); + + fmpz_set_si(m, n); + fmpz_pow_ui(m, m, k); + acb_mul_fmpz(t, t, m, prec); + + if (!acb_overlaps(x, t)) + { + flint_printf("FAIL\n"); + flint_abort(); + } + + acb_mat_clear(tau); + acb_clear(z); + acb_clear(x); + acb_clear(t); + fmpz_clear(m); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-ql_a0.c b/src/acb_theta/test/t-ql_a0.c new file mode 100644 index 0000000000..8ae74b11d6 --- /dev/null +++ b/src/acb_theta/test/t-ql_a0.c @@ -0,0 +1,102 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "acb_mat.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_ql_a0, state) +{ + slong iter; + + /* Test: agrees with ql_a0_naive */ + for (iter = 0; iter < 10 * flint_test_multiplier(); iter++) + { + slong g = 1 + n_randint(state, 3); + slong n = 1 << g; + slong prec = (g > 1 ? 200 : 500) + n_randint(state, 500); + slong bits = n_randint(state, 5); + slong hprec = prec + 100; + int hast = iter % 2; + int hasz = (iter % 4) / 2; + slong nbt = (hast ? 3 : 1); + slong nbz = (hasz ? 2 : 1); + slong guard = ACB_THETA_LOW_PREC; + slong lp = ACB_THETA_LOW_PREC; + acb_mat_t tau; + acb_ptr z, t, r, test; + arb_ptr d, d0; + arb_t y; + slong k; + int res; + + acb_mat_init(tau, g, g); + z = _acb_vec_init(g); + t = _acb_vec_init(g); + r = _acb_vec_init(nbz * nbt * n); + test = _acb_vec_init(nbz * nbt * n); + d = _arb_vec_init(n); + d0 = _arb_vec_init(n); + arb_init(y); + + res = 0; + while(!res) + { + acb_siegel_randtest_reduced(tau, state, prec, bits); + arb_sub_si(y, acb_imagref(acb_mat_entry(tau, g - 1, g - 1)), 200, prec); + res = arb_is_negative(y); + } + + if (hast) + { + for (k = 0; k < g; k++) + { + arb_urandom(acb_realref(&t[k]), state, hprec); + } + } + + acb_theta_dist_a0(d0, z, tau, lp); + if (hasz) + { + acb_siegel_randtest_vec(z, state, g, prec); + } + acb_theta_dist_a0(d, z, tau, lp); + + res = acb_theta_ql_a0(r, t, z, d0, d, tau, guard, prec); + acb_theta_ql_a0_naive(test, t, z, d0, d, tau, guard, hprec); + + if (res && !_acb_vec_overlaps(r, test, nbz * nbt * n)) + { + flint_printf("FAIL\n"); + flint_printf("g = %wd, prec = %wd, hprec = %wd, hasz = %wd, hast = %wd, tau:\n", + g, prec, hprec, hasz, hast); + acb_mat_printd(tau, 5); + flint_printf("output:\n"); + _acb_vec_printd(r, nbz * nbt * n, 5); + _acb_vec_printd(test, nbz * nbt * n, 5); + flint_printf("difference:\n"); + _acb_vec_sub(r, r, test, nbz * nbt * n, hprec); + _acb_vec_printd(r, nbz * nbt * n, 5); + flint_abort(); + } + + acb_mat_clear(tau); + _acb_vec_clear(z, g); + _acb_vec_clear(t, g); + _acb_vec_clear(r, nbz * nbt * n); + _acb_vec_clear(test, nbz * nbt * n); + _arb_vec_clear(d, n); + _arb_vec_clear(d0, n); + arb_clear(y); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-ql_a0_split.c b/src/acb_theta/test/t-ql_a0_split.c new file mode 100644 index 0000000000..2173d6aef1 --- /dev/null +++ b/src/acb_theta/test/t-ql_a0_split.c @@ -0,0 +1,94 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "acb_mat.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_ql_a0_split, state) +{ + slong iter; + + /* Test: agrees with ql_a0_naive using ql_a0_naive as worker */ + for (iter = 0; iter < 10 * flint_test_multiplier(); iter++) + { + slong g = 2 + n_randint(state, 2); + slong n = 1 << g; + slong s = 1 + n_randint(state, g - 1); + int hast = iter % 2; + slong nbt = (hast ? 3 : 1); + slong prec = 50 + n_randint(state, 50); + slong hprec = prec + 25; + slong guard = 0; + slong lp = ACB_THETA_LOW_PREC; + slong bits = n_randint(state, 4); + acb_mat_t tau; + acb_ptr z, t, r, test; + arb_ptr d, d0; + slong k; + int res; + + acb_mat_init(tau, g, g); + z = _acb_vec_init(g); + t = _acb_vec_init(g); + r = _acb_vec_init(nbt * n); + test = _acb_vec_init(2 * nbt * n); + d = _arb_vec_init(n); + d0 = _arb_vec_init(n); + + acb_siegel_randtest_reduced(tau, state, hprec, bits); + acb_siegel_randtest_vec(z, state, g, hprec); + if (hast) + { + for (k = 0; k < g; k++) + { + arb_urandom(acb_realref(&t[k]), state, hprec); + } + } + + acb_theta_dist_a0(d0, t, tau, lp); + acb_theta_dist_a0(d, z, tau, lp); + + res = acb_theta_ql_a0_split(r, t, z, d, tau, s, guard, prec, + &acb_theta_ql_a0_naive); + acb_theta_ql_a0_naive(test, t, z, d0, d, tau, guard, hprec); + + if (!_acb_vec_is_zero(z, g)) + { + _acb_vec_set(test, test + nbt * n, nbt * n); + } + + if (res && !_acb_vec_overlaps(r, test, nbt * n)) + { + flint_printf("FAIL\n"); + flint_printf("g = %wd, s = %wd, prec = %wd, tau, z:\n", g, s, prec); + acb_mat_printd(tau, 5); + _acb_vec_printd(z, g, 5); + flint_printf("output:\n"); + _acb_vec_printd(r, nbt * n, 5); + _acb_vec_printd(test, nbt * n, 5); + flint_printf("difference:\n"); + _acb_vec_sub(test, test, r, nbt * n, prec); + _acb_vec_printd(test, nbt * n, 5); + flint_abort(); + } + + acb_mat_clear(tau); + _acb_vec_clear(z, g); + _acb_vec_clear(t, g); + _acb_vec_clear(r, nbt * n); + _acb_vec_clear(test, 2 * nbt * n); + _arb_vec_clear(d, n); + _arb_vec_clear(d0, n); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-ql_a0_steps.c b/src/acb_theta/test/t-ql_a0_steps.c new file mode 100644 index 0000000000..cd4fcbe889 --- /dev/null +++ b/src/acb_theta/test/t-ql_a0_steps.c @@ -0,0 +1,92 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "acb_mat.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_ql_a0_steps, state) +{ + slong iter; + + /* Test: agrees with ql_a0_naive using ql_a0_naive as worker */ + for (iter = 0; iter < 10 * flint_test_multiplier(); iter++) + { + slong g = 2 + n_randint(state, 2); + slong n = 1 << g; + slong s = 1 + n_randint(state, g - 1); + slong nb_steps = n_randint(state, 5); + slong bits = n_randint(state, 4); + int hast = iter % 2; + int hasz = (iter % 4) / 2; + slong nbt = (hast ? 3 : 1); + slong nbz = (hasz ? 2 : 1); + slong prec = 200 + n_randint(state, 500); + slong hprec = prec + 50; + slong guard = ACB_THETA_LOW_PREC; + slong lp = ACB_THETA_LOW_PREC; + acb_mat_t tau; + acb_ptr z, t, r, test; + arb_ptr d, d0; + slong k; + int res; + + acb_mat_init(tau, g, g); + z = _acb_vec_init(g); + t = _acb_vec_init(g); + r = _acb_vec_init(nbz * nbt * n); + test = _acb_vec_init(nbz * nbt * n); + d = _arb_vec_init(n); + d0 = _arb_vec_init(n); + + acb_siegel_randtest_reduced(tau, state, hprec, bits); + if (hast) + { + for (k = 0; k < g; k++) + { + arb_urandom(acb_realref(&t[k]), state, hprec); + } + } + if (hasz) + { + acb_siegel_randtest_vec(z, state, g, prec); + } + + acb_theta_dist_a0(d0, t, tau, lp); + acb_theta_dist_a0(d, z, tau, lp); + + res = acb_theta_ql_a0_steps(r, t, z, d0, d, tau, nb_steps, s, + guard, prec, &acb_theta_ql_a0_naive); + acb_theta_ql_a0_naive(test, t, z, d0, d, tau, guard, hprec); + + if (res && !_acb_vec_overlaps(r, test, nbz * nbt * n)) + { + flint_printf("FAIL\n"); + flint_printf("g = %wd, prec = %wd, s = %wd, hasz = %wd, hast = %wd, tau:\n", + g, prec, s, hasz, hast); + acb_mat_printd(tau, 5); + flint_printf("output:\n"); + _acb_vec_printd(r, nbz * nbt * n, 5); + _acb_vec_printd(test, nbz * nbt * n, 5); + flint_abort(); + } + + acb_mat_clear(tau); + _acb_vec_clear(z, g); + _acb_vec_clear(t, g); + _acb_vec_clear(r, nbz * nbt * n); + _acb_vec_clear(test, nbz * nbt * n); + _arb_vec_clear(d, n); + _arb_vec_clear(d0, n); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-ql_all.c b/src/acb_theta/test/t-ql_all.c new file mode 100644 index 0000000000..4767ed8577 --- /dev/null +++ b/src/acb_theta/test/t-ql_all.c @@ -0,0 +1,72 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "acb_mat.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_ql_all, state) +{ + slong iter; + + /* Test: agrees with naive_all */ + for (iter = 0; iter < 20 * flint_test_multiplier(); iter++) + { + slong g = 1 + n_randint(state, 3); + slong n = 1 << g; + int hasz = iter % 2; + int sqr = iter % 3; + slong prec = (g > 1 ? 100 : 1000) + n_randint(state, 200); + slong hprec = prec + 25; + slong bits = 6; + acb_mat_t tau; + acb_ptr z, th, test; + + acb_mat_init(tau, g, g); + z = _acb_vec_init(g); + th = _acb_vec_init(n * n); + test = _acb_vec_init(n * n); + + acb_siegel_randtest_reduced(tau, state, hprec, bits); + if (hasz) + { + acb_siegel_randtest_vec(z, state, g, prec); + } + + acb_theta_ql_all(th, z, tau, sqr, prec); + acb_theta_naive_all(test, z, 1, tau, hprec); + if (sqr) + { + _acb_vec_sqr(test, test, n * n, prec); + } + + if (!_acb_vec_overlaps(th, test, n * n)) + { + flint_printf("FAIL\n"); + flint_printf("g = %wd, prec = %wd, hasz = %wd, tau, z:\n", + g, prec, hasz); + acb_mat_printd(tau, 5); + _acb_vec_printd(z, g, 5); + flint_printf("output:\n"); + _acb_vec_printd(th, n * n, 5); + _acb_vec_printd(test, n * n, 5); + flint_abort(); + } + + acb_mat_clear(tau); + _acb_vec_clear(z, g); + _acb_vec_clear(th, n * n); + _acb_vec_clear(test, n * n); + } + + TEST_FUNCTION_END(state); +} + diff --git a/src/acb_theta/test/t-ql_reduce.c b/src/acb_theta/test/t-ql_reduce.c new file mode 100644 index 0000000000..f9ee805aec --- /dev/null +++ b/src/acb_theta/test/t-ql_reduce.c @@ -0,0 +1,159 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "arb_mat.h" +#include "acb_mat.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_ql_reduce, state) +{ + slong iter; + + /* Test: agrees with naive algorithms */ + for (iter = 0; iter < 20 * flint_test_multiplier(); iter++) + { + slong g = 2 + n_randint(state, 2); + slong n = 1 << g; + slong prec = ACB_THETA_LOW_PREC + n_randint(state, 100); + slong bits = 6; + acb_mat_t tau, tau0; + arb_mat_t Y; + acb_ptr z, new_z, th, th0, test; + arb_ptr x; + acb_t c; + arb_t u, abs; + ulong a0, a1, b0, b1, fixed_a1; + slong * n1; + slong k, s; + + acb_mat_init(tau, g, g); + arb_mat_init(Y, g, g); + z = _acb_vec_init(g); + new_z = _acb_vec_init(g); + th = _acb_vec_init(n * n); + th0 = _acb_vec_init(n * n); + test = _acb_vec_init(n * n); + x = _arb_vec_init(g); + acb_init(c); + arb_init(u); + arb_init(abs); + n1 = flint_malloc(g * sizeof(slong)); + + acb_siegel_randtest_reduced(tau, state, prec, bits); + + /* Choose z as Y.v + error with v either 0, 1/4 or 1/2 entries, or + random values */ + acb_mat_get_imag(Y, tau); + for (k = 0; k < g; k++) + { + arb_set_si(&x[k], n_randint(state, 3)); + } + _arb_vec_scalar_mul_2exp_si(x, x, g, -2); + arb_mat_vector_mul_col(x, Y, x, prec); + + if (iter % 2 == 0) + { + for (k = 0; k < g; k++) + { + acb_urandom(&z[k], state, prec); + arb_add(acb_imagref(&z[k]), acb_imagref(&z[k]), &x[k], prec); + } + } + else + { + acb_siegel_randtest_vec(z, state, g, prec); + } + + s = acb_theta_ql_reduce(new_z, c, u, n1, z, tau, prec); + acb_theta_naive_all(th, z, 1, tau, prec); + + /* If s == -1, check that theta values are small */ + if (s == -1) + { + for (k = 0; k < n * n; k++) + { + acb_abs(abs, &th[k], prec); + if (arb_gt(abs, u)) + { + flint_printf("FAIL (g = %wd, s = %wd)", g, s); + acb_mat_printd(tau, 5); + flint_printf("values, bound:\n"); + _acb_vec_printd(th, n * n, 5); + arb_printd(u, 5); + flint_printf("\n"); + flint_abort(); + } + } + } + /* Otherwise, construct test vector */ + else + { + fixed_a1 = acb_theta_char_get_a(n1, g - s); + if (s == 0) + { + acb_one(&th0[0]); + } + else + { + acb_mat_window_init(tau0, tau, 0, 0, s, s); + acb_theta_naive_all(th0, new_z, 1, tau0, prec); + acb_mat_window_clear(tau0); + } + + for (k = 0; k < n * n; k++) + { + a0 = k >> (g + g - s); + a1 = (k >> g) % (1 << (g - s)); + b0 = (k >> (g - s)) % (1 << s); + b1 = k % (1 << (g - s)); + if (a1 == fixed_a1) + { + acb_mul(&test[k], c, &th0[(a0 << s) + b0], prec); + acb_mul_i_pow_si(&test[k], &test[k], + acb_theta_char_dot_slong(b1, n1, g - s)); + } + acb_add_error_arb(&test[k], u); + } + + if (!_acb_vec_overlaps(th, test, n * n)) + { + flint_printf("FAIL (g = %wd, s = %wd)\n", g, s); + flint_printf("tau, z:\n"); + acb_mat_printd(tau, 5); + _acb_vec_printd(z, g, 5); + flint_printf("th, test:\n"); + _acb_vec_printd(th, n * n, 5); + _acb_vec_printd(test, n * n, 5); + flint_printf("difference:\n"); + _acb_vec_sub(test, test, th, n * n, prec); + _acb_vec_printd(test, n * n, 5); + flint_abort(); + } + } + + acb_mat_clear(tau); + arb_mat_clear(Y); + _acb_vec_clear(z, g); + _acb_vec_clear(new_z, g); + _acb_vec_clear(th, n * n); + _acb_vec_clear(th0, n * n); + _acb_vec_clear(test, n * n); + _arb_vec_clear(x, g); + acb_clear(c); + arb_clear(u); + arb_clear(abs); + flint_free(n1); + } + + TEST_FUNCTION_END(state); +} + diff --git a/src/acb_theta/test/t-siegel_cocycle.c b/src/acb_theta/test/t-siegel_cocycle.c new file mode 100644 index 0000000000..b7771f8a17 --- /dev/null +++ b/src/acb_theta/test/t-siegel_cocycle.c @@ -0,0 +1,74 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "acb_mat.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_siegel_cocycle, state) +{ + slong iter; + + /* Test: chain rule */ + for (iter = 0; iter < 100 * flint_test_multiplier(); iter++) + { + slong g = 1 + n_randint(state, 6); + slong prec = 100 + n_randint(state, 200); + slong mag_bits = n_randint(state, 10); + fmpz_mat_t m1, m2, m3; + acb_mat_t tau1, tau2; + acb_mat_t c1, c2, c3, t; + + fmpz_mat_init(m1, 2 * g, 2 * g); + fmpz_mat_init(m2, 2 * g, 2 * g); + fmpz_mat_init(m3, 2 * g, 2 * g); + acb_mat_init(tau1, g, g); + acb_mat_init(tau2, g, g); + acb_mat_init(c1, g, g); + acb_mat_init(c2, g, g); + acb_mat_init(c3, g, g); + acb_mat_init(t, g, g); + + acb_siegel_randtest(tau1, state, prec, mag_bits); + sp2gz_randtest(m1, state, mag_bits); + sp2gz_randtest(m2, state, mag_bits); + + /* Test: chain rule */ + acb_siegel_cocycle(c1, m1, tau1, prec); + acb_siegel_transform(tau2, m1, tau1, prec); + acb_siegel_cocycle(c2, m2, tau2, prec); + fmpz_mat_mul(m3, m2, m1); + acb_siegel_cocycle(c3, m3, tau1, prec); + acb_mat_mul(t, c2, c1, prec); + + if (!acb_mat_overlaps(t, c3)) + { + flint_printf("FAIL\n\n"); + acb_mat_printd(c3, 10); + flint_printf("\n"); + acb_mat_printd(t, 10); + flint_printf("\n"); + flint_abort(); + } + + fmpz_mat_clear(m1); + fmpz_mat_clear(m2); + fmpz_mat_clear(m3); + acb_mat_clear(tau1); + acb_mat_clear(tau2); + acb_mat_clear(c1); + acb_mat_clear(c2); + acb_mat_clear(c3); + acb_mat_clear(t); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-siegel_is_reduced.c b/src/acb_theta/test/t-siegel_is_reduced.c new file mode 100644 index 0000000000..3976137f89 --- /dev/null +++ b/src/acb_theta/test/t-siegel_is_reduced.c @@ -0,0 +1,62 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "acb_mat.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_siegel_is_reduced, state) +{ + slong iter; + + /* Test: correct values on some matrices */ + for (iter = 0; iter < 10 * flint_test_multiplier(); iter++) + { + slong g = 1 + n_randint(state, 4); + slong prec = ACB_THETA_LOW_PREC; + slong tol_exp = -10; + slong j = n_randint(state, g); + slong k = n_randint(state, g); + acb_mat_t tau; + + acb_mat_init(tau, g, g); + + acb_mat_onei(tau); + if (!acb_siegel_is_reduced(tau, tol_exp, prec)) + { + flint_printf("FAIL (1)\n"); + acb_mat_printd(tau, 5); + flint_abort(); + } + + acb_add_si(acb_mat_entry(tau, j, k), acb_mat_entry(tau, j, k), 1, prec); + acb_set(acb_mat_entry(tau, k, j), acb_mat_entry(tau, j, k)); + if (acb_siegel_is_reduced(tau, tol_exp, prec)) + { + flint_printf("FAIL (2)\n"); + acb_mat_printd(tau, 5); + flint_abort(); + } + + acb_mat_onei(tau); + acb_mul_2exp_si(acb_mat_entry(tau, j, j), acb_mat_entry(tau, j, j), -1); + if (acb_siegel_is_reduced(tau, tol_exp, prec)) + { + flint_printf("FAIL (3)\n"); + acb_mat_printd(tau, 5); + flint_abort(); + } + + acb_mat_clear(tau); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-siegel_reduce.c b/src/acb_theta/test/t-siegel_reduce.c new file mode 100644 index 0000000000..784f4b9e49 --- /dev/null +++ b/src/acb_theta/test/t-siegel_reduce.c @@ -0,0 +1,68 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "acb_mat.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_siegel_reduce, state) +{ + slong iter; + + /* Test: mat is symplectic and image passes acb_siegel_is_reduced */ + for (iter = 0; iter < 50 * flint_test_multiplier(); iter++) + { + slong g = 1 + n_randint(state, 4); + slong prec = 100 + n_randint(state, 200); + slong mag_bits = n_randint(state, 5); + slong tol_exp = -10; + int fail = iter % 10; + acb_mat_t tau; + acb_mat_t w; + fmpz_mat_t mat; + + acb_mat_init(tau, g, g); + acb_mat_init(w, g, g); + fmpz_mat_init(mat, 2 * g, 2 * g); + + if (fail) + { + mag_bits = 100; + } + acb_siegel_randtest(tau, state, prec, mag_bits); + acb_siegel_reduce(mat, tau, prec); + + if (!sp2gz_is_correct(mat)) + { + flint_printf("FAIL (symplectic)\n"); + fmpz_mat_print(mat); + flint_abort(); + } + + acb_siegel_transform(w, mat, tau, prec); + + if (!fail && !acb_siegel_is_reduced(w, tol_exp, prec)) + { + flint_printf("FAIL (not reduced)\n"); + acb_mat_printd(tau, 10); + fmpz_mat_print_pretty(mat); + flint_printf("\n"); + acb_mat_printd(w, 10); + flint_abort(); + } + + acb_mat_clear(tau); + acb_mat_clear(w); + fmpz_mat_clear(mat); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-siegel_transform.c b/src/acb_theta/test/t-siegel_transform.c new file mode 100644 index 0000000000..4a2e422160 --- /dev/null +++ b/src/acb_theta/test/t-siegel_transform.c @@ -0,0 +1,66 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "acb_mat.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_siegel_transform, state) +{ + slong iter; + + /* Test: chain rule */ + for (iter = 0; iter < 100 * flint_test_multiplier(); iter++) + { + slong g = 1 + n_randint(state, 6); + slong prec = 100 + n_randint(state, 200); + slong mag_bits = n_randint(state, 10); + fmpz_mat_t m1, m2, m3; + acb_mat_t tau1, tau2, tau3, test; + + fmpz_mat_init(m1, 2 * g, 2 * g); + fmpz_mat_init(m2, 2 * g, 2 * g); + fmpz_mat_init(m3, 2 * g, 2 * g); + acb_mat_init(tau1, g, g); + acb_mat_init(tau2, g, g); + acb_mat_init(tau3, g, g); + acb_mat_init(test, g, g); + + acb_siegel_randtest(tau1, state, prec, mag_bits); + sp2gz_randtest(m1, state, mag_bits); + sp2gz_randtest(m2, state, mag_bits); + + acb_siegel_transform(tau2, m1, tau1, prec); + acb_siegel_transform(tau3, m2, tau2, prec); + fmpz_mat_mul(m3, m2, m1); + acb_siegel_transform(test, m3, tau1, prec); + + if (!acb_mat_overlaps(test, tau3)) + { + flint_printf("FAIL\n\n"); + acb_mat_printd(test, 10); + flint_printf("\n"); + acb_mat_printd(tau3, 10); + flint_printf("\n"); + flint_abort(); + } + + fmpz_mat_clear(m1); + fmpz_mat_clear(m2); + fmpz_mat_clear(m3); + acb_mat_clear(tau1); + acb_mat_clear(tau2); + acb_mat_clear(tau3); + acb_mat_clear(test); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-siegel_transform_z.c b/src/acb_theta/test/t-siegel_transform_z.c new file mode 100644 index 0000000000..a30e392c29 --- /dev/null +++ b/src/acb_theta/test/t-siegel_transform_z.c @@ -0,0 +1,83 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "acb_mat.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_siegel_transform_z, state) +{ + slong iter; + + /* Test: matches siegel_transform, inverse matrix gives inverse transformation */ + for (iter = 0; iter < 100 * flint_test_multiplier(); iter++) + { + slong g = 1 + n_randint(state, 6); + slong prec = 100 + n_randint(state, 200); + slong bits = n_randint(state, 10); + acb_mat_t tau1, w, tau2; + acb_ptr z1, r, z2; + fmpz_mat_t m; + + acb_mat_init(tau1, g, g); + acb_mat_init(w, g, g); + acb_mat_init(tau2, g, g); + z1 = _acb_vec_init(g); + r = _acb_vec_init(g); + z2 = _acb_vec_init(g); + fmpz_mat_init(m, 2 * g, 2 * g); + + acb_siegel_randtest(tau1, state, prec, bits); + acb_siegel_randtest_vec(z1, state, g, prec); + + sp2gz_randtest(m, state, bits); + acb_siegel_transform_z(r, w, m, z1, tau1, prec); + + /* Test: agrees with transform */ + acb_siegel_transform(tau2, m, tau1, prec); + if (!acb_mat_overlaps(tau2, w)) + { + flint_printf("FAIL (transform)\n\n"); + acb_mat_printd(w, 10); + flint_printf("\n"); + acb_mat_printd(tau2, 10); + flint_printf("\n"); + flint_abort(); + } + + /* Test: inverse transformation */ + sp2gz_inv(m, m); + acb_siegel_transform_z(z2, tau2, m, r, w, prec); + if (!acb_mat_contains(tau2, tau1) || !_acb_vec_contains(z2, z1, g)) + { + flint_printf("FAIL (inverse)\n\n"); + acb_mat_printd(tau1, 10); + flint_printf("\n"); + acb_mat_printd(tau2, 10); + flint_printf("\n\n"); + _acb_vec_printd(z1, g, 10); + flint_printf("\n\n"); + _acb_vec_printd(z2, g, 10); + flint_printf("\n"); + flint_abort(); + } + + acb_mat_clear(tau1); + acb_mat_clear(w); + acb_mat_clear(tau2); + _acb_vec_clear(z1, g); + _acb_vec_clear(r, g); + _acb_vec_clear(z2, g); + fmpz_mat_clear(m); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-sp2gz_decompose.c b/src/acb_theta/test/t-sp2gz_decompose.c new file mode 100644 index 0000000000..381e228b5c --- /dev/null +++ b/src/acb_theta/test/t-sp2gz_decompose.c @@ -0,0 +1,152 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "acb_theta.h" + +static int +sp2gz_comes_from_g1(const fmpz_mat_t mat) +{ + slong g = sp2gz_dim(mat); + fmpz_mat_t x, y; + slong k, l; + int res = 0; + + fmpz_mat_init(x, 2, 2); + fmpz_mat_init(y, 2 * g, 2 * g); + + for (k = 0; k < 2; k++) + { + for (l = 0; l < 2; l++) + { + fmpz_set(fmpz_mat_entry(x, k, l), fmpz_mat_entry(mat, k * g, l * g)); + } + } + + if (sp2gz_is_correct(x)) + { + sp2gz_embed(y, x); + res = fmpz_mat_equal(mat, y); + } + + fmpz_mat_clear(x); + fmpz_mat_clear(y); + return res; +} + +static int +sp2gz_is_allowed_in_dec(const fmpz_mat_t mat) +{ + slong g = sp2gz_dim(mat); + fmpz_mat_t alpha, beta, gamma, x, y; + slong r; + int res; + + if (g == 1 || sp2gz_comes_from_g1(mat)) + { + return 1; + } + + fmpz_mat_window_init(alpha, mat, 0, 0, g, g); + fmpz_mat_window_init(beta, mat, 0, g, g, 2 * g); + fmpz_mat_window_init(gamma, mat, g, 0, 2 * g, g); + fmpz_mat_init(x, 2 * g, 2 * g); + + if (!fmpz_mat_is_zero(gamma)) + { + r = fmpz_mat_rank(gamma); + + fmpz_mat_init(y, 2 * r, 2 * r); + sp2gz_j(y); + sp2gz_embed(x, y); + fmpz_mat_clear(y); + } + else if (!fmpz_mat_is_zero(beta)) + { + sp2gz_trig(x, beta); + } + else + { + sp2gz_block_diag(x, alpha); + } + + res = fmpz_mat_equal(mat, x); + fmpz_mat_window_clear(alpha); + fmpz_mat_window_clear(beta); + fmpz_mat_window_clear(gamma); + fmpz_mat_clear(x); + return res; +} + +TEST_FUNCTION_START(acb_theta_sp2gz_decompose, state) +{ + slong iter; + + /* Test: decomposition consists of elementary matrices and product is the + original matrix */ + for (iter = 0; iter < 100 * flint_test_multiplier(); iter++) + { + slong g = 2 + n_randint(state, 5); + slong bits = n_randint(state, 20); + fmpz_mat_t m, x; + fmpz_mat_struct * dec = NULL; + slong nb_dec = 0; + slong k; + + fmpz_mat_init(m, 2 * g, 2 * g); + fmpz_mat_init(x, 2 * g, 2 * g); + + sp2gz_randtest(m, state, bits); + dec = sp2gz_decompose(&nb_dec, m); + + for (k = 0; k < nb_dec; k++) + { + if (!sp2gz_is_allowed_in_dec(&dec[k])) + { + flint_printf("FAIL (not elementary)\n"); + fmpz_mat_print_pretty(&dec[k]); + flint_printf("\n"); + flint_abort(); + } + } + + fmpz_mat_one(x); + for (k = 0; k < nb_dec; k++) + { + fmpz_mat_mul(x, x, &dec[k]); + } + if (!fmpz_mat_equal(m, x)) + { + flint_printf("FAIL (product)\n"); + fmpz_mat_print_pretty(x); + flint_printf("\n"); + fmpz_mat_print_pretty(m); + flint_printf("\ndecomposition in %wd matrices:\n", nb_dec); + for (k = 0; k < nb_dec; k++) + { + fmpz_mat_print_pretty(&dec[k]); + flint_printf("\n"); + } + flint_abort(); + } + + fmpz_mat_clear(m); + fmpz_mat_clear(x); + for (k = 0; k < nb_dec; k++) + { + fmpz_mat_clear(&dec[k]); + } + flint_free(dec); + } + + TEST_FUNCTION_END(state); +} + diff --git a/src/acb_theta/test/t-sp2gz_inv.c b/src/acb_theta/test/t-sp2gz_inv.c new file mode 100644 index 0000000000..6b3719fe84 --- /dev/null +++ b/src/acb_theta/test/t-sp2gz_inv.c @@ -0,0 +1,48 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "fmpz.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_sp2gz_inv, state) +{ + slong iter; + + /* Test: matches fmpz_mat_inv */ + for (iter = 0; iter < 500 * flint_test_multiplier(); iter++) + { + slong g = 1 + n_randint(state, 10); + slong bits = n_randint(state, 10); + fmpz_mat_t m1, m2; + fmpz_t den; + + fmpz_mat_init(m1, 2 * g, 2 * g); + fmpz_mat_init(m2, 2 * g, 2 * g); + fmpz_init(den); + + sp2gz_randtest(m1, state, bits); + sp2gz_inv(m2, m1); + fmpz_mat_inv(m1, den, m1); + + if (!fmpz_mat_equal(m1, m2) || !fmpz_is_one(den)) + { + flint_printf("FAIL\n\n"); + flint_abort(); + } + + fmpz_mat_clear(m1); + fmpz_mat_clear(m2); + fmpz_clear(den); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-sp2gz_is_correct.c b/src/acb_theta/test/t-sp2gz_is_correct.c new file mode 100644 index 0000000000..2f5b68a066 --- /dev/null +++ b/src/acb_theta/test/t-sp2gz_is_correct.c @@ -0,0 +1,81 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_sp2gz_is_correct, state) +{ + slong iter; + + /* Test: return 1 on various kinds of symplectic matrices; return 0 on + non-square of even size */ + for (iter = 0; iter < 500 * flint_test_multiplier(); iter++) + { + slong g = 1 + n_randint(state, 10); + fmpz_mat_t a, b, m, n; + slong r = n_randint(state, 10); + slong c = n_randint(state, 10); + slong bits = n_randint(state, 100); + + fmpz_mat_init(a, g, g); + fmpz_mat_init(b, g, g); + fmpz_mat_init(m, 2 * g, 2 * g); + fmpz_mat_init(n, r, c); + + if (iter == 0) + { + sp2gz_j(m); + } + else if (iter <= sp2gz_nb_fundamental(g)) + { + sp2gz_fundamental(m, iter - 1); + } + else if (iter % 2 == 0) + { + fmpz_mat_one(a); + fmpz_mat_randops(a, state, bits); + sp2gz_block_diag(m, a); + } + else + { + fmpz_mat_randtest(a, state, bits); + fmpz_mat_transpose(b, a); + fmpz_mat_add(a, a, b); + sp2gz_trig(m, a); + } + + if (!sp2gz_is_correct(m)) + { + flint_printf("FAIL\n"); + fmpz_mat_print_pretty(m); + flint_printf("\n"); + flint_abort(); + } + + fmpz_mat_one(n); + if ((r != c || r % 2 == 1) && sp2gz_is_correct(n)) + { + flint_printf("FAIL\n"); + fmpz_mat_print_pretty(n); + flint_printf("\n"); + flint_abort(); + } + + fmpz_mat_clear(a); + fmpz_mat_clear(b); + fmpz_mat_clear(m); + fmpz_mat_clear(n); + } + + TEST_FUNCTION_END(state); +} + diff --git a/src/acb_theta/test/t-sp2gz_set_blocks.c b/src/acb_theta/test/t-sp2gz_set_blocks.c new file mode 100644 index 0000000000..70cb181299 --- /dev/null +++ b/src/acb_theta/test/t-sp2gz_set_blocks.c @@ -0,0 +1,57 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_sp2gz_set_blocks, state) +{ + slong iter; + + /* Test: set_abcd is inverse of get_abcd */ + for (iter = 0; iter < 500 * flint_test_multiplier(); iter++) + { + slong g = 1 + n_randint(state, 10); + fmpz_mat_t a, b, c, d; + fmpz_mat_t m, n; + slong bits = n_randint(state, 10); + + fmpz_mat_init(m, 2 * g, 2 * g); + fmpz_mat_init(n, 2 * g, 2 * g); + sp2gz_randtest(m, state, bits); + + fmpz_mat_window_init(a, m, 0, 0, g, g); + fmpz_mat_window_init(b, m, 0, g, g, 2 * g); + fmpz_mat_window_init(c, m, g, 0, 2 * g, g); + fmpz_mat_window_init(d, m, g, g, 2 * g, 2 * g); + + sp2gz_set_blocks(n, a, b, c, d); + + if (!fmpz_mat_equal(m, n)) + { + flint_printf("FAIL\n\n"); + fmpz_mat_print_pretty(m); + flint_printf("\n\n"); + fmpz_mat_print_pretty(n); + flint_abort(); + flint_printf("\n\n"); + } + + fmpz_mat_window_clear(a); + fmpz_mat_window_clear(b); + fmpz_mat_window_clear(c); + fmpz_mat_window_clear(d); + fmpz_mat_clear(m); + fmpz_mat_clear(n); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-transform_char.c b/src/acb_theta/test/t-transform_char.c new file mode 100644 index 0000000000..eca71e975a --- /dev/null +++ b/src/acb_theta/test/t-transform_char.c @@ -0,0 +1,56 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_transform_char, state) +{ + slong iter; + + /* Test: on trigonal symplectic matrices, a remains the same */ + for (iter = 0; iter < 100 * flint_test_multiplier(); iter++) + { + slong g = 1 + n_randint(state, 10); + slong bits = 8; + fmpz_mat_t mat; + slong e; + ulong ab = n_randint(state, 1 << (2 * g)); + ulong test; + slong j, k; + + fmpz_mat_init(mat, 2 * g, 2 * g); + + for (j = 0; j < g; j++) + { + for (k = j; k < g; k++) + { + fmpz_randtest(fmpz_mat_entry(mat, j, k), state, bits); + fmpz_set(fmpz_mat_entry(mat, k, j), fmpz_mat_entry(mat, j, k)); + } + } + sp2gz_trig(mat, mat); + + test = acb_theta_transform_char(&e, mat, ab); + + if ((test >> g) != (ab >> g)) + { + flint_printf("FAIL\n"); + flint_printf("ab = %wd, test = %wd, matrix:\n", ab, test); + fmpz_mat_print_pretty(mat); + flint_abort(); + } + + fmpz_mat_clear(mat); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-transform_kappa.c b/src/acb_theta/test/t-transform_kappa.c new file mode 100644 index 0000000000..f1d3f5a7ac --- /dev/null +++ b/src/acb_theta/test/t-transform_kappa.c @@ -0,0 +1,62 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_transform_kappa, state) +{ + slong iter; + + /* Test: kappa and kappa2 agree */ + for (iter = 0; iter < 200 * flint_test_multiplier(); iter++) + { + slong g = 1 + n_randint(state, 3); + slong bits = n_randint(state, 4); + slong prec = 200; + fmpz_mat_t mat; + fmpz_mat_t x; + acb_mat_t tau; + acb_t sqrtdet; + slong kappa, kappa2; + + fmpz_mat_init(mat, 2 * g, 2 * g); + fmpz_mat_init(x, 2, 2); + acb_mat_init(tau, g, g); + acb_init(sqrtdet); + + sp2gz_randtest(mat, state, bits); + acb_siegel_randtest_reduced(tau, state, prec, bits); + + kappa = acb_theta_transform_kappa(sqrtdet, mat, tau, prec); + kappa2 = acb_theta_transform_kappa2(mat); + + if (kappa % 4 != kappa2) + { + flint_printf("FAIL\n"); + flint_printf("tau, mat:\n"); + acb_mat_printd(tau, 5); + fmpz_mat_print_pretty(mat); + flint_printf("kappa = %wd, kappa2 = %wd, sqrtdet:\n", kappa, kappa2); + acb_printd(sqrtdet, 5); + flint_printf("\n"); + flint_abort(); + } + + fmpz_mat_clear(mat); + fmpz_mat_clear(x); + acb_mat_clear(tau); + acb_clear(sqrtdet); + } + + TEST_FUNCTION_END(state); +} + diff --git a/src/acb_theta/test/t-transform_proj.c b/src/acb_theta/test/t-transform_proj.c new file mode 100644 index 0000000000..6c7adc7f10 --- /dev/null +++ b/src/acb_theta/test/t-transform_proj.c @@ -0,0 +1,69 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_transform_proj, state) +{ + slong iter; + + /* Test: inverse matrix gives back the same projective point */ + for (iter = 0; iter < 100 * flint_test_multiplier(); iter++) + { + slong g = 1 + n_randint(state, 3); + slong n2 = 1 << (2 * g); + slong prec = 100; + slong bits = n_randint(state, 5); + int sqr = iter % 2; + fmpz_mat_t mat, inv; + acb_ptr th, aux, test; + acb_t scal; + slong k; + + fmpz_mat_init(mat, 2 * g, 2 * g); + fmpz_mat_init(inv, 2 * g, 2 * g); + th = _acb_vec_init(n2); + aux = _acb_vec_init(n2); + test = _acb_vec_init(n2); + acb_init(scal); + + sp2gz_randtest(mat, state, bits); + sp2gz_inv(inv, mat); + for (k = 0; k < n2; k++) + { + acb_urandom(&test[k], state, prec); + } + + acb_theta_transform_proj(aux, mat, test, sqr, prec); + acb_theta_transform_proj(th, inv, aux, sqr, prec); + acb_div(scal, &test[0], &th[0], prec); + _acb_vec_scalar_mul(th, th, n2, scal, prec); + + if (!_acb_vec_overlaps(th, test, n2)) + { + flint_printf("FAIL (sqr = %wd)\n", sqr); + flint_printf("test, th:\n"); + _acb_vec_printd(test, n2, 5); + _acb_vec_printd(th, n2, 5); + flint_abort(); + } + + fmpz_mat_clear(mat); + fmpz_mat_clear(inv); + _acb_vec_clear(th, n2); + _acb_vec_clear(aux, n2); + _acb_vec_clear(test, n2); + acb_clear(scal); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-transform_sqrtdet.c b/src/acb_theta/test/t-transform_sqrtdet.c new file mode 100644 index 0000000000..0925283d92 --- /dev/null +++ b/src/acb_theta/test/t-transform_sqrtdet.c @@ -0,0 +1,53 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_transform_sqrtdet, state) +{ + slong iter; + + /* Test: square of sqrtdet is det */ + for (iter = 0; iter < 100 * flint_test_multiplier(); iter++) + { + slong g = 1 + n_randint(state, 4); + acb_mat_t tau; + acb_t r, t; + slong prec = 2 + n_randint(state, 200); + slong mag_bits = n_randint(state, 4); + + acb_mat_init(tau, g, g); + acb_init(r); + acb_init(t); + + acb_siegel_randtest(tau, state, prec, mag_bits); + acb_theta_transform_sqrtdet(r, tau, prec); + acb_sqr(r, r, prec); + acb_mat_det(t, tau, prec); + + if (!acb_overlaps(r, t)) + { + flint_printf("FAIL\n"); + acb_printd(r, 10); + flint_printf("\n"); + acb_printd(t, 10); + flint_printf("\n"); + flint_abort(); + } + + acb_mat_clear(tau); + acb_clear(r); + acb_clear(t); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/transform_char.c b/src/acb_theta/transform_char.c new file mode 100644 index 0000000000..6baa90cc6f --- /dev/null +++ b/src/acb_theta/transform_char.c @@ -0,0 +1,142 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "fmpz.h" +#include "acb_theta.h" + +ulong +acb_theta_transform_char(slong * e, const fmpz_mat_t mat, ulong ab) +{ + slong g = sp2gz_dim(mat); + fmpz_mat_t a, b, c, d; + fmpz_mat_t mat_tp; + fmpz_mat_t block; /* CD^t or AB^t */ + fmpz_mat_t alphabeta, alpha, beta; + fmpz_mat_t Cvec_1, Cvec_2, Lvec; + fmpz_mat_t coef; + fmpz_t eps, x; + ulong res = 0; + slong i; + + fmpz_mat_window_init(a, mat, 0, 0, g, g); + fmpz_mat_window_init(b, mat, 0, g, g, 2 * g); + fmpz_mat_window_init(c, mat, g, 0, 2 * g, g); + fmpz_mat_window_init(d, mat, g, g, 2 * g, 2 * g); + fmpz_mat_init(mat_tp, 2 * g, 2 * g); + fmpz_mat_init(block, g, g); + fmpz_mat_init(alphabeta, 2 * g, 1); + fmpz_mat_init(Cvec_1, g, 1); + fmpz_mat_init(Cvec_2, g, 1); + fmpz_mat_init(Lvec, 1, g); + fmpz_mat_init(coef, 1, 1); + fmpz_init(eps); + fmpz_init(x); + + fmpz_mat_transpose(mat_tp, mat); + + /* Compute blocks and substract diagonals in alphabeta */ + fmpz_mat_transpose(block, d); + fmpz_mat_mul(block, c, block); + for (i = 0; i < g; i++) + { + fmpz_sub(fmpz_mat_entry(alphabeta, i, 0), + fmpz_mat_entry(alphabeta, i, 0), fmpz_mat_entry(block, i, i)); + } + fmpz_mat_transpose(block, b); + fmpz_mat_mul(block, a, block); + for (i = 0; i < g; i++) + { + fmpz_sub(fmpz_mat_entry(alphabeta, g + i, 0), + fmpz_mat_entry(alphabeta, g + i, 0), fmpz_mat_entry(block, i, i)); + } + + /* Turn ab into a 2g x 1 fmpz matrix, and update alphabeta */ + for (i = 0; i < 2 * g; i++) + { + /* Least significant bits first */ + fmpz_add_si(fmpz_mat_entry(alphabeta, 2 * g - 1 - i, 0), + fmpz_mat_entry(alphabeta, 2 * g - 1 - i, 0), ab & 1); + ab = ab >> 1; + } + + /* Perform matrix-vector multiplication */ + fmpz_mat_mul(alphabeta, mat_tp, alphabeta); + + /* Compute eps */ + fmpz_mat_window_init(alpha, alphabeta, 0, 0, g, 1); + fmpz_mat_window_init(beta, alphabeta, g, 0, 2 * g, 1); + + fmpz_zero(eps); + + fmpz_mat_mul(Cvec_1, c, beta); + fmpz_mat_mul(Cvec_2, b, alpha); + fmpz_mat_transpose(Lvec, Cvec_2); + fmpz_mat_mul(coef, Lvec, Cvec_1); + fmpz_addmul_ui(eps, fmpz_mat_entry(coef, 0, 0), 2); + + fmpz_mat_mul(Cvec_1, b, alpha); + fmpz_mat_mul(Cvec_2, d, alpha); + fmpz_mat_transpose(Lvec, Cvec_2); + fmpz_mat_mul(coef, Lvec, Cvec_1); + fmpz_sub(eps, eps, fmpz_mat_entry(coef, 0, 0)); + + fmpz_mat_mul(Cvec_1, a, beta); + fmpz_mat_mul(Cvec_2, c, beta); + fmpz_mat_transpose(Lvec, Cvec_2); + fmpz_mat_mul(coef, Lvec, Cvec_1); + fmpz_sub(eps, eps, fmpz_mat_entry(coef, 0, 0)); + + fmpz_mat_transpose(block, b); + fmpz_mat_mul(block, a, block); + for (i = 0; i < g; i++) + { + fmpz_set(fmpz_mat_entry(Lvec, 0, i), fmpz_mat_entry(block, i, i)); + } + fmpz_mat_mul(Cvec_1, d, alpha); + fmpz_mat_mul(Cvec_2, c, beta); + fmpz_mat_sub(Cvec_1, Cvec_1, Cvec_2); + fmpz_mat_mul(coef, Lvec, Cvec_1); + fmpz_addmul_ui(eps, fmpz_mat_entry(coef, 0, 0), 2); + + /* Convert alphabeta mod 2 to ulong */ + for (i = 0; i < 2 * g; i++) + { + res = res << 1; + res += fmpz_tstbit(fmpz_mat_entry(alphabeta, i, 0), 0); + } + /* Adjust sign of eps and reduce mod 8 */ + for (i = 0; i < g; i++) + { + if (fmpz_mod_ui(x, fmpz_mat_entry(alphabeta, i, 0), 2) == 1 + && fmpz_mod_ui(x, fmpz_mat_entry(alphabeta, i + g, 0), 4) > 1) + { + fmpz_add_ui(eps, eps, 4); + } + } + *e = fmpz_mod_ui(eps, eps, 8); + + fmpz_mat_window_clear(a); + fmpz_mat_window_clear(b); + fmpz_mat_window_clear(c); + fmpz_mat_window_clear(d); + fmpz_mat_clear(mat_tp); + fmpz_mat_clear(block); + fmpz_mat_clear(alphabeta); + fmpz_mat_window_clear(alpha); + fmpz_mat_window_clear(beta); + fmpz_mat_clear(Cvec_1); + fmpz_mat_clear(Cvec_2); + fmpz_mat_clear(Lvec); + fmpz_mat_clear(coef); + fmpz_clear(eps); + fmpz_clear(x); + return res; +} diff --git a/src/acb_theta/transform_kappa.c b/src/acb_theta/transform_kappa.c new file mode 100644 index 0000000000..435ce05598 --- /dev/null +++ b/src/acb_theta/transform_kappa.c @@ -0,0 +1,185 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb_mat.h" +#include "acb_modular.h" +#include "acb_theta.h" + +static slong +transform_kappa_g1(acb_t sqrtdet, const fmpz_mat_t mat, const fmpz_mat_t x, + const acb_mat_t tau, slong prec) +{ + slong g = acb_mat_nrows(tau); + psl2z_t y; + int R[4]; + int S[4]; + int C; + ulong ab; + slong e, res; + + psl2z_init(y); + + /* set y to corresponding psl2z_t and use acb_modular_theta_transform */ + fmpz_set(&y->a, fmpz_mat_entry(x, 0, 0)); + fmpz_set(&y->b, fmpz_mat_entry(x, 0, 1)); + fmpz_set(&y->c, fmpz_mat_entry(x, 1, 0)); + fmpz_set(&y->d, fmpz_mat_entry(x, 1, 1)); + + acb_modular_theta_transform(R, S, &C, y); + + acb_mul_fmpz(sqrtdet, acb_mat_entry(tau, 0, 0), &y->c, prec); + acb_add_fmpz(sqrtdet, sqrtdet, &y->d, prec); + acb_sqrt(sqrtdet, sqrtdet, prec); + + /* find out where theta_00 is going */ + if (S[2] == 1) /* theta_2 */ + { + ab = 1 << (2 * g - 1); + } + else if (S[2] == 2) /* theta_0 */ + { + ab = 0; + } + else /* theta_1, since -theta_3 cannot happen (odd) */ + { + ab = 1 << (g - 1); + } + acb_theta_transform_char(&e, mat, ab); + + /* adjust root of unity based on R */ + if (fmpz_is_zero(&y->c)) + { + res = -R[2] - e; + } + else + { + res = -R[2] - 1 - e; + } + + psl2z_clear(y); + return res; +} + +static slong +transform_kappa_j(acb_t sqrtdet, const fmpz_mat_t mat, const acb_mat_t tau, slong prec) +{ + slong g = sp2gz_dim(mat); + fmpz_mat_t gamma; + acb_mat_t tau0; + slong r, res; + + fmpz_mat_window_init(gamma, mat, g, 0, 2 * g, g); + r = fmpz_mat_rank(gamma); + fmpz_mat_window_clear(gamma); + + /* Mumford: theta_00(mtau) = det(tau0/i)^{1/2} theta_00(tau), and + transform_sqrtdet(tau0) = i^{r/2} det(tau0/i)^{1/2} */ + acb_mat_window_init(tau0, tau, 0, 0, r, r); + acb_theta_transform_sqrtdet(sqrtdet, tau0, prec); + acb_mat_window_clear(tau0); + + res = -r; + if (r % 2 == 1) + { + acb_mul_onei(sqrtdet, sqrtdet); + res -= 2; + } + return res; +} + +slong +acb_theta_transform_kappa(acb_t sqrtdet, const fmpz_mat_t mat, + const acb_mat_t tau, slong prec) +{ + slong g = acb_mat_nrows(tau); + fmpz_mat_struct * dec; + fmpz_mat_t delta; + fmpz_t det; + slong nb_dec; + fmpz_mat_t x; + acb_mat_t w; + acb_t c; + slong k, res, e; + ulong ab; + + fmpz_mat_init(x, 2, 2); + acb_mat_init(w, g, g); + acb_init(c); + fmpz_init(det); + dec = sp2gz_decompose(&nb_dec, mat); + + acb_one(sqrtdet); + acb_mat_set(w, tau); + res = 0; + + for (k = nb_dec - 1; k >= 0; k--) + { + if (sp2gz_is_trig(&dec[k]) || sp2gz_is_block_diag(&dec[k])) + { + /* theta_00(mtau) = theta_ab(tau) */ + fmpz_mat_window_init(delta, &dec[k], g, g, 2 * g, 2 * g); + fmpz_mat_det(det, delta); + fmpz_mat_window_clear(delta); + + if (fmpz_is_one(det)) + { + acb_one(c); + } + else + { + acb_onei(c); + res -= 2; + } + } + else if (sp2gz_is_embedded(x, &dec[k])) + { + if (fmpz_cmp_si(fmpz_mat_entry(x, 1, 0), 0) < 0 + || (fmpz_is_zero(fmpz_mat_entry(x, 1, 0)) + && fmpz_cmp_si(fmpz_mat_entry(x, 1, 1), 0) < 0)) + { + fmpz_mat_neg(x, x); + res += transform_kappa_g1(c, &dec[k], x, w, prec); + acb_div_onei(c, c); + res += 2; + } + else + { + res += transform_kappa_g1(c, &dec[k], x, w, prec); + } + } + else /* embedded j */ + { + res += transform_kappa_j(c, &dec[k], w, prec); + } + acb_siegel_transform(w, &dec[k], w, prec); + acb_mul(sqrtdet, sqrtdet, c, prec); + } + + /* Adjust final sign based on transformation of coordinates */ + acb_theta_transform_char(&e, mat, 0); + res -= e; + ab = 0; + for (k = 0; k < nb_dec; k++) + { + ab = acb_theta_transform_char(&e, &dec[k], ab); + res += e; + } + + fmpz_mat_clear(x); + acb_mat_clear(w); + acb_clear(c); + for (k = 0; k < nb_dec; k++) + { + fmpz_mat_clear(&dec[k]); + } + flint_free(dec); + return res & 7; +} diff --git a/src/acb_theta/transform_kappa2.c b/src/acb_theta/transform_kappa2.c new file mode 100644 index 0000000000..4d76d5ce59 --- /dev/null +++ b/src/acb_theta/transform_kappa2.c @@ -0,0 +1,154 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb_modular.h" +#include "acb_theta.h" + +static slong +transform_kappa2_g1(const fmpz_mat_t mat, const fmpz_mat_t x) +{ + slong g = sp2gz_dim(mat); + psl2z_t y; + int R[4]; + int S[4]; + int C; + ulong ab; + slong e, res; + + psl2z_init(y); + + /* set y to corresponding psl2z_t and use acb_modular_theta_transform */ + fmpz_set(&y->a, fmpz_mat_entry(x, 0, 0)); + fmpz_set(&y->b, fmpz_mat_entry(x, 0, 1)); + fmpz_set(&y->c, fmpz_mat_entry(x, 1, 0)); + fmpz_set(&y->d, fmpz_mat_entry(x, 1, 1)); + + acb_modular_theta_transform(R, S, &C, y); + + /* find out where theta_00 is going */ + if (S[2] == 1) /* theta_2 */ + { + ab = 1 << (2 * g - 1); + } + else if (S[2] == 2) /* theta_0 */ + { + ab = 0; + } + else /* theta_1, since -theta_3 cannot happen (odd) */ + { + ab = 1 << (g - 1); + } + acb_theta_transform_char(&e, mat, ab); + + /* adjust root of unity based on R */ + if (fmpz_is_zero(&y->c)) + { + res = -R[2] - e; + } + else + { + res = -R[2] - 1 - e; + } + + psl2z_clear(y); + return res; +} + +static slong +transform_kappa2_j(const fmpz_mat_t mat) +{ + slong g = sp2gz_dim(mat); + fmpz_mat_t gamma; + slong r, res; + + fmpz_mat_window_init(gamma, mat, g, 0, 2 * g, g); + r = fmpz_mat_rank(gamma); + fmpz_mat_window_clear(gamma); + + res = -r; + if (r % 2 == 1) + { + res -= 2; + } + return res; +} + +slong +acb_theta_transform_kappa2(const fmpz_mat_t mat) +{ + slong g = sp2gz_dim(mat); + fmpz_mat_struct * dec; + fmpz_mat_t delta; + fmpz_t det; + slong nb_dec; + fmpz_mat_t x; + slong k, res, e; + ulong ab; + + fmpz_mat_init(x, 2, 2); + fmpz_init(det); + dec = sp2gz_decompose(&nb_dec, mat); + + res = 0; + for (k = nb_dec - 1; k >= 0; k--) + { + if (sp2gz_is_trig(&dec[k]) || sp2gz_is_block_diag(&dec[k])) + { + /* theta_00(mtau) = theta_ab(tau) */ + fmpz_mat_window_init(delta, &dec[k], g, g, 2 * g, 2 * g); + fmpz_mat_det(det, delta); + fmpz_mat_window_clear(delta); + + if (!fmpz_is_one(det)) + { + res += 2; + } + } + else if (sp2gz_is_embedded(x, &dec[k])) + { + if (fmpz_cmp_si(fmpz_mat_entry(x, 1, 0), 0) < 0 + || (fmpz_is_zero(fmpz_mat_entry(x, 1, 0)) + && fmpz_cmp_si(fmpz_mat_entry(x, 1, 1), 0) < 0)) + { + fmpz_mat_neg(x, x); + res += transform_kappa2_g1(&dec[k], x); + res += 2; + } + else + { + res += transform_kappa2_g1(&dec[k], x); + } + } + else /* embedded j */ + { + res += transform_kappa2_j(&dec[k]); + } + } + + /* Adjust final sign based on transformation of coordinates */ + acb_theta_transform_char(&e, mat, 0); + res -= e; + ab = 0; + for (k = 0; k < nb_dec; k++) + { + ab = acb_theta_transform_char(&e, &dec[k], ab); + res += e; + } + + fmpz_mat_clear(x); + for (k = 0; k < nb_dec; k++) + { + fmpz_mat_clear(&dec[k]); + } + flint_free(dec); + return res & 3; +} + diff --git a/src/acb_theta/transform_proj.c b/src/acb_theta/transform_proj.c new file mode 100644 index 0000000000..8146ad6912 --- /dev/null +++ b/src/acb_theta/transform_proj.c @@ -0,0 +1,41 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb.h" +#include "acb_theta.h" + +void +acb_theta_transform_proj(acb_ptr res, const fmpz_mat_t mat, acb_srcptr th, int sqr, slong prec) +{ + slong g = sp2gz_dim(mat); + ulong n2 = 1 << (2 * g); + slong k = (sqr ? 4 : 8); + acb_ptr aux; + ulong ab, image_ab; + slong e; + acb_t c; + + aux = _acb_vec_init(n2); + acb_init(c); + + for (ab = 0; ab < n2; ab++) + { + image_ab = acb_theta_transform_char(&e, mat, ab); + acb_unit_root(c, k, prec); + acb_pow_ui(c, c, e, prec); + acb_mul(c, c, &th[image_ab], prec); + acb_set(&aux[ab], c); + } + _acb_vec_set(res, aux, n2); + + _acb_vec_clear(aux, n2); + acb_clear(c); +} diff --git a/src/acb_theta/transform_sqrtdet.c b/src/acb_theta/transform_sqrtdet.c new file mode 100644 index 0000000000..77ae933f02 --- /dev/null +++ b/src/acb_theta/transform_sqrtdet.c @@ -0,0 +1,185 @@ +/* + Copyright (C) 2023 Jean Kieffer + + This file is part of FLINT. + + FLINT is free software: you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License (LGPL) as published + by the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. See . +*/ + +#include "acb_poly.h" +#include "acb_mat.h" +#include "acb_theta.h" + +static void +acb_theta_sqrt_branch(acb_t res, const acb_t x, acb_srcptr rts_neg, slong nb_neg, + acb_srcptr rts_pos, slong nb_pos, const acb_t sqrt_lead, slong prec) +{ + acb_t s, t; + slong k; + + acb_init(s); + acb_init(t); + + acb_set(s, sqrt_lead); + for (k = 0; k < nb_neg; k++) + { + acb_sub(t, x, &rts_neg[k], prec); + acb_sqrt_analytic(t, t, 1, prec); + acb_mul(s, s, t, prec); + } + for (k = 0; k < nb_pos; k++) + { + acb_sub(t, &rts_pos[k], x, prec); + acb_sqrt_analytic(t, t, 1, prec); + acb_mul(s, s, t, prec); + } + + acb_set(res, s); + + acb_clear(s); + acb_clear(t); +} + +void +acb_theta_transform_sqrtdet(acb_t res, const acb_mat_t tau, slong prec) +{ + slong g = acb_mat_nrows(tau); + flint_rand_t state; + acb_mat_t A, B, C; + acb_poly_t pol, h; + acb_ptr rts, rts_neg, rts_pos; + acb_t z, rt, mu; + arb_t x; + slong k, j, nb_neg, nb_pos; + int success = 0; + + flint_randinit(state); + acb_mat_init(A, g, g); + acb_mat_init(B, g, g); + acb_mat_init(C, g, g); + acb_poly_init(pol); + acb_poly_init(h); + rts = _acb_vec_init(g); + rts_neg = _acb_vec_init(g); + rts_pos = _acb_vec_init(g); + acb_init(z); + acb_init(rt); + acb_init(mu); + arb_init(x); + + /* Choose a purely imaginary matrix A and compute pol s.t. pol(-1) = det(A) + and pol(1) = det(tau): pol(t) is + + det(A + (t+1)/2 (tau - A)) = det(A) det(I - (t+1)/2 (I - A^{-1}tau)) + + We want to get the g roots of this polynomial to compute the branch + cuts. This can fail e.g. when det(tau - A) = 0, so pick A at random + until the roots can be found */ + for (k = 0; (k < 100) && !success; k++) + { + acb_mat_onei(A); + for (j = 0; j < g; j++) + { + arb_urandom(x, state, prec); + arb_add(acb_imagref(acb_mat_entry(A, j, j)), + acb_imagref(acb_mat_entry(A, j, j)), x, prec); + } + acb_mat_inv(B, A, prec); + acb_mat_mul(B, B, tau, prec); + acb_mat_one(C); + acb_mat_sub(C, C, B, prec); + + /* Get reverse of charpoly */ + acb_mat_charpoly(h, C, prec); + acb_poly_zero(pol); + for (j = 0; j <= g; j++) + { + acb_poly_get_coeff_acb(z, h, j); + acb_poly_set_coeff_acb(pol, g - j, z); + } + acb_poly_one(h); + acb_poly_set_coeff_si(h, 1, 1); + acb_poly_scalar_mul_2exp_si(h, h, -1); + acb_poly_compose(pol, pol, h, prec); + + success = (acb_poly_find_roots(rts, pol, NULL, 0, prec) == g); + + /* Check that no root intersects the [-1,1] segment */ + for (j = 0; (j < g) && success; j++) + { + if (arb_contains_zero(acb_imagref(&rts[j]))) + { + arb_abs(x, acb_realref(&rts[j])); + arb_sub_si(x, x, 1, prec); + success = arb_is_positive(x); + } + } + } + + if (success) + { + /* Partition the roots between positive & negative real parts to + compute branch for sqrt(pol) */ + nb_neg = 0; + nb_pos = 0; + for (k = 0; k < g; k++) + { + if (arb_is_negative(acb_realref(&rts[k]))) + { + acb_set(&rts_neg[nb_neg], &rts[k]); + nb_neg++; + } + else + { + acb_set(&rts_pos[nb_pos], &rts[k]); + nb_pos++; + } + } + acb_mat_det(rt, A, prec); + acb_mul(rt, rt, acb_poly_get_coeff_ptr(pol, g), prec); + acb_sqrts(rt, z, rt, prec); + + /* Set mu to +-1 such that mu*sqrt_branch gives the correct value at A, + i.e. i^(g/2) * something positive */ + acb_mat_det(mu, A, prec); + acb_mul_i_pow_si(mu, mu, -g); + acb_sqrt(mu, mu, prec); + acb_set_si(z, g); + acb_mul_2exp_si(z, z, -2); + acb_exp_pi_i(z, z, prec); + acb_mul(mu, mu, z, prec); + acb_set_si(z, -1); + acb_theta_sqrt_branch(z, z, rts_neg, nb_neg, rts_pos, nb_pos, rt, prec); + acb_div(mu, mu, z, prec); + + /* Compute square root branch at z=1 to get sqrtdet */ + acb_set_si(z, 1); + acb_theta_sqrt_branch(rt, z, rts_neg, nb_neg, rts_pos, nb_pos, rt, prec); + acb_mul(rt, rt, mu, prec); + acb_mat_det(res, tau, prec); + acb_theta_agm_sqrt(res, res, rt, 1, prec); + } + else + { + acb_mat_det(res, tau, prec); + acb_sqrts(res, z, res, prec); + acb_union(res, res, z, prec); + } + + flint_randclear(state); + acb_mat_clear(A); + acb_mat_clear(B); + acb_mat_clear(C); + acb_poly_clear(pol); + acb_poly_clear(h); + _acb_vec_clear(rts, g); + _acb_vec_clear(rts_pos, g); + _acb_vec_clear(rts_neg, g); + acb_clear(z); + acb_clear(rt); + acb_clear(mu); + arb_clear(x); +} diff --git a/src/arb_mat.h b/src/arb_mat.h index 3261bb2fc1..a135bd879b 100644 --- a/src/arb_mat.h +++ b/src/arb_mat.h @@ -321,8 +321,6 @@ void arb_mat_vector_mul_row(arb_ptr res, arb_srcptr v, const arb_mat_t A, slong void arb_mat_vector_mul_col(arb_ptr res, const arb_mat_t A, arb_srcptr v, slong prec); -void arb_mat_bilinear_form(arb_t res, const arb_mat_t A, arb_srcptr v1, arb_srcptr v2, slong prec); - /* Solving */ ARB_MAT_INLINE void diff --git a/src/arb_mat/bilinear_form.c b/src/arb_mat/bilinear_form.c deleted file mode 100644 index e18d02372e..0000000000 --- a/src/arb_mat/bilinear_form.c +++ /dev/null @@ -1,43 +0,0 @@ -/* - Copyright (C) 2023 Jean Kieffer - - This file is part of FLINT. - - FLINT is free software: you can redistribute it and/or modify it under - the terms of the GNU Lesser General Public License (LGPL) as published - by the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. See . -*/ - -#include "arb_mat.h" - -void -arb_mat_bilinear_form(arb_t res, const arb_mat_t A, arb_srcptr v1, arb_srcptr v2, slong prec) -{ - slong nrow = arb_mat_nrows(A); - slong ncol = arb_mat_ncols(A); - arb_mat_t col, row, prod, scal; - slong k; - - arb_mat_init(col, ncol, 1); - arb_mat_init(row, 1, nrow); - arb_mat_init(prod, nrow, 1); - arb_mat_init(scal, 1, 1); - - for (k = 0; k < nrow; k++) - { - arb_set(arb_mat_entry(row, 0, k), &v1[k]); - } - for (k = 0; k < ncol; k++) - { - arb_set(arb_mat_entry(col, k, 0), &v2[k]); - } - arb_mat_mul(prod, A, col, prec); - arb_mat_mul(scal, row, prod, prec); - arb_set(res, arb_mat_entry(scal, 0, 0)); - - arb_mat_clear(col); - arb_mat_clear(row); - arb_mat_clear(prod); - arb_mat_clear(scal); -} diff --git a/src/arb_mat/spd_is_lll_reduced.c b/src/arb_mat/spd_is_lll_reduced.c index 8a589640ed..6bb5a9fec2 100644 --- a/src/arb_mat/spd_is_lll_reduced.c +++ b/src/arb_mat/spd_is_lll_reduced.c @@ -13,45 +13,74 @@ #include "fmpz_lll.h" #include "arb_mat.h" -int arb_mat_spd_is_lll_reduced(const arb_mat_t A, slong tol_exp, slong prec) +/* Adapted from fmpz_mat_is_reduced_gram */ +int +arb_mat_spd_is_lll_reduced(const arb_mat_t A, slong tol_exp, slong prec) { - slong g = arb_mat_nrows(A); - arb_mat_t B; - fmpz_mat_t N; - arb_t c; + slong d = arb_mat_nrows(A); + arb_mat_t r, mu; + arb_ptr s; + arb_t delta, eta, t; + slong i, j, k; int res = 1; - slong j, k; - arb_mat_init(B, g, g); - fmpz_mat_init(N, g, g); - arb_init(c); + if (d <= 1) + { + return 1; + } + + arb_mat_init(r, d, d); + arb_mat_init(mu, d, d); + s = _arb_vec_init(d); + arb_init(delta); + arb_init(eta); + arb_init(t); - /* Set B, check error bounds on coefficients */ - for (j = 0; (j < g) && res; j++) + arb_one(t); + arb_mul_2exp_si(t, t, tol_exp); + arb_set_si(delta, 99); + arb_div_si(delta, delta, 100, prec); + arb_sub(delta, delta, t, prec); + arb_set_si(eta, 51); + arb_div_si(eta, eta, 100, prec); + arb_add(eta, eta, t, prec); + + arb_set(arb_mat_entry(r, 0, 0), arb_mat_entry(A, 0, 0)); + + for (i = 1; (i < d) && res; i++) { - for (k = 0; (k < g) && res; k++) + arb_set(&s[0], arb_mat_entry(A, i, i)); + for (j = 0; (j < i) && res; j++) { - if (mag_cmp_2exp_si(arb_radref(arb_mat_entry(A, j, k)), tol_exp - 4) > 0) + arb_set(arb_mat_entry(r, i, j), arb_mat_entry(A, i, j)); + for (k = 0; k < j; k++) + { + arb_submul(arb_mat_entry(r, i, j), arb_mat_entry(mu, j, k), + arb_mat_entry(r, i, k), prec); + } + arb_div(arb_mat_entry(mu, i, j), arb_mat_entry(r, i, j), + arb_mat_entry(r, j, j), prec); + arb_abs(t, arb_mat_entry(mu, i, j)); + if (!arb_le(t, eta)) { res = 0; } - arb_one(c); - arb_mul_2exp_si(c, c, tol_exp); - arb_add_si(c, c, 1, prec); - arb_pow_ui(c, c, j + k, prec); - arb_mul(arb_mat_entry(B, j, k), c, arb_mat_entry(A, j, k), prec); + arb_set(&s[j + 1], &s[j]); + arb_submul(&s[j + 1], arb_mat_entry(mu, i, j), arb_mat_entry(r, i, j), prec); + } + arb_set(arb_mat_entry(r, i, i), &s[i]); + arb_mul(t, delta, arb_mat_entry(r, i - 1, i - 1), prec); + if (!arb_le(t, &s[i - 1])) + { + res = 0; } } - res = res && arb_mat_spd_get_fmpz_mat(N, B, prec); - if (res) - { - /* Default Flint LLL values, except Gram */ - res = fmpz_mat_is_reduced_gram(N, 0.99, 0.51); - } - - arb_mat_clear(B); - fmpz_mat_clear(N); - arb_clear(c); + arb_mat_clear(r); + arb_mat_clear(mu); + _arb_vec_clear(s, d); + arb_clear(delta); + arb_clear(eta); + arb_clear(t); return res; } diff --git a/src/arb_mat/test/main.c b/src/arb_mat/test/main.c index 6c38eb75af..6fba56e93e 100644 --- a/src/arb_mat/test/main.c +++ b/src/arb_mat/test/main.c @@ -15,7 +15,6 @@ /* Include functions *********************************************************/ #include "t-addmul_rad_mag_fast.c" -#include "t-bilinear_form.c" #include "t-charpoly.c" #include "t-cho.c" #include "t-companion.c" @@ -59,7 +58,6 @@ test_struct tests[] = { TEST_FUNCTION(arb_mat_addmul_rad_mag_fast), - TEST_FUNCTION(arb_mat_bilinear_form), TEST_FUNCTION(arb_mat_charpoly), TEST_FUNCTION(arb_mat_cho), TEST_FUNCTION(arb_mat_companion), diff --git a/src/arb_mat/test/t-bilinear_form.c b/src/arb_mat/test/t-bilinear_form.c deleted file mode 100644 index cb828fd121..0000000000 --- a/src/arb_mat/test/t-bilinear_form.c +++ /dev/null @@ -1,67 +0,0 @@ -/* - Copyright (C) 2023 Jean Kieffer - - This file is part of FLINT. - - FLINT is free software: you can redistribute it and/or modify it under - the terms of the GNU Lesser General Public License (LGPL) as published - by the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. See . -*/ - -#include "test_helpers.h" -#include "arb_mat.h" - -TEST_FUNCTION_START(arb_mat_bilinear_form, state) -{ - slong iter; - - for (iter = 0; iter < 1000 * flint_test_multiplier(); iter++) - { - slong nrow = n_randint(state, 10); - slong ncol = n_randint(state, 10); - slong bits = n_randint(state, 10); - slong prec = 100 + n_randint(state, 200); - arb_mat_t A, B; - arb_ptr v1, v2; - arb_t x, t; - slong k; - - arb_mat_init(A, nrow, ncol); - arb_mat_init(B, ncol, nrow); - v1 = _arb_vec_init(nrow); - v2 = _arb_vec_init(ncol); - arb_init(x); - arb_init(t); - - arb_mat_randtest(A, state, prec, bits); - for (k = 0; k < nrow; k++) - { - arb_randtest_precise(&v1[k], state, prec, bits); - } - for (k = 0; k < ncol; k++) - { - arb_randtest_precise(&v2[k], state, prec, bits); - } - - /* Test: should be equal for transpose */ - arb_mat_bilinear_form(x, A, v1, v2, prec); - arb_mat_transpose(B, A); - arb_mat_bilinear_form(t, B, v2, v1, prec); - - if (!arb_overlaps(x, t)) - { - flint_printf("FAIL\n"); - flint_abort(); - } - - arb_mat_clear(A); - arb_mat_clear(B); - _arb_vec_clear(v1, nrow); - _arb_vec_clear(v2, ncol); - arb_clear(x); - arb_clear(t); - } - - TEST_FUNCTION_END(state); -} diff --git a/src/arb_mat/test/t-spd_lll_reduce.c b/src/arb_mat/test/t-spd_lll_reduce.c index 8f09ae8ac1..61d6f43dac 100644 --- a/src/arb_mat/test/t-spd_lll_reduce.c +++ b/src/arb_mat/test/t-spd_lll_reduce.c @@ -17,8 +17,8 @@ TEST_FUNCTION_START(arb_mat_spd_lll_reduce, state) { slong iter; - /* Test: result satisfies arb_mat_spd_is_lll_reduced and - arb_mat_spd_is_lll_reduced returns 0 on more imprecise result */ + /* Test: result satisfies arb_mat_spd_is_lll_reduced; U is I if starting + matrix was reduced */ for (iter = 0; iter < 500 * flint_test_multiplier(); iter++) { slong g = 1 + n_randint(state, 4); @@ -29,13 +29,11 @@ TEST_FUNCTION_START(arb_mat_spd_lll_reduce, state) arb_mat_t R; arb_mat_t T; fmpz_mat_t U; - mag_t eps; arb_mat_init(M, g, g); arb_mat_init(R, g, g); arb_mat_init(T, g, g); fmpz_mat_init(U, g, g); - mag_init(eps); arb_mat_randtest_spd(M, state, prec, mag_bits); arb_mat_spd_lll_reduce(U, M, prec); @@ -55,22 +53,19 @@ TEST_FUNCTION_START(arb_mat_spd_lll_reduce, state) flint_abort(); } - mag_one(eps); - mag_mul_2exp_si(eps, eps, tol_exp); - arb_mat_add_error_mag(R, eps); - - if (arb_mat_spd_is_lll_reduced(R, tol_exp, prec)) + if (arb_mat_spd_is_lll_reduced(M, tol_exp, prec) + && !fmpz_mat_is_one(U)) { - flint_printf("FAIL (error bounds)\n"); - arb_mat_printd(R, 10); - flint_abort(); + flint_printf("FAIL (identity)\n"); + arb_mat_printd(M, 10); + fmpz_mat_print_pretty(U); + flint_printf("\n"); } arb_mat_clear(M); arb_mat_clear(R); arb_mat_clear(T); fmpz_mat_clear(U); - mag_clear(eps); } TEST_FUNCTION_END(state);