diff --git a/doc/source/acb_theta.rst b/doc/source/acb_theta.rst index 1f4700352e..55b2d67e7c 100644 --- a/doc/source/acb_theta.rst +++ b/doc/source/acb_theta.rst @@ -4,16 +4,20 @@ =============================================================================== 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`: +any dimension `g\geq 1`. The algorithms will be detailed in the forthcoming +paper [EK2025]_. In the case `g=1`, we rely on, but also improve on +functionality from :ref:`acb_modular.h `. We also provide +functionality to evaluate derivatives of theta functions, and also to evaluate +Siegel modular forms in terms of theta functions when `g=2`. + +In this module, *tau* or `\tau` always denotes an element of the Siegel upper +half-space `\mathcal{H}_g`, i.e. `\tau` is a symmetric `g\times g` complex +matrix with positive definite imaginary part, encoded as an :type:`acb_mat_t`, +while *z* or *zs* denotes a vector (resp. a tuple of vectors) in +`\mathbb{C}^g`, encoded as an :type:`acb_ptr` of length `g` (resp. *nb* times +`g`). For each `a,b\in \{0,1\}^g`, the Riemann theta function (of level 2) of +characteristic `(a,b)` is the following analytic function in `\tau\in +\mathcal{H}_g` and `z\in \mathbb{C}^g`: .. math:: @@ -21,78 +25,105 @@ function in `\tau\in \mathbb{H}_g` and `z\in \mathbb{C}^g`: 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 `4`. 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.) +Throughout, we order vectors of theta values by associating to each +characteristic `(a,b)` the :type:`ulong` between 0 and `2^{2g}-1` whose `g` +most (resp. least) significant bits are given by `a` (resp. `b`): thus `(a,b)` +where `a = (1,0)` and `b = (0,0)` in dimension `2` is numbered `8`. With these +conventions, the output of :func:`acb_modular_theta` in dimension 1 is +`(-\theta_3,\theta_2,\theta_0,\theta_1)`. When manipulating `a` or `b` +individually, we map them to integers between 0 and `2^g-1`. + +The numerical functions in this module always compute certified error bounds: +for instance, if `\tau` is represented by an :type:`acb_mat_t` whose imaginary +part is not certainly positive definite at the chosen working precision, then +the output theta values will have an infinite radius. Main user functions ------------------------------------------------------------------------------- +The main method to evaluate theta functions is + .. 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). +The output, stored in *th*, is a vector of length `2^{2g}`. If *sqr* is zero +(false), this function computes `\theta_{a,b}(z,\tau)` for all `a,b\in +\{0,1\}^g`; if *sqr* is nonzero (true), it computes `\theta_{a,b}(z_j,\tau)^2` +instead using a faster algorithm. -.. function:: void acb_theta_naive_fixed_ab(acb_ptr th, ulong ab, acb_srcptr zs, slong nb, const acb_mat_t tau, slong prec) +We handle the final argument *prec* as follows. Barring unexpected +cancellations, the absolute value of `\theta_{a,b}(z,\tau)` should be roughly -.. function:: void acb_theta_naive_all(acb_ptr th, acb_srcptr zs, slong nb, const acb_mat_t tau, slong prec) + .. math:: - 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. + \left|\theta_{a,b}(z,\tau)\right| \approx e^{\pi y^T Y^{-1} y} e^{- d^2} -.. function:: void acb_theta_jet_all(acb_ptr dth, acb_srcptr z, const acb_mat_t tau, slong ord, slong prec) +where - 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.) +- `Y` and `y` denote the imaginary parts of `\tau` and `z` respectively (we + keep this notation throughout); +- `d` denotes the distance between the point `v = -Y^{-1}y \in \mathbb{R}^g` and the + shifted lattice `\mathbb{Z}^g + \tfrac{a}{2} \subset \mathbb{R}^g` for the Euclidean norm + given by the Gram matrix `\pi Y`. -.. 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) +This leads us to define the normalized functions -.. function:: void acb_theta_jet_naive_all(acb_ptr dth, acb_srcptr z, const acb_mat_t tau, slong ord, slong prec) + .. math:: - 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. + \widetilde{\theta}_{a,b}(z,\tau) = e^{-\pi y^T Y^{-1} y} \theta_{a,b}(z,\tau) + +which are no longer holomorphic, but are uniformly bounded on `\mathbb{C}^g` +for a fixed `\tau`. We use those internally for easier precision management: an +argument *prec* means that `\widetilde{\theta}_{a,b}(z,\tau)` is computed with +an absolute error bound of roughly `2^{-\mathit{prec}}`. The expected error +bound on the output of :func:`acb_theta_all` and similar functions will be of +the order of `\exp(\pi y^T Y^{-1} y) \cdot 2^{-\mathit{prec}}` to avoid +unreasonable computations when `y` is very far from zero. Some internal +functions also take the factor `\exp(-d^2)` into account, and are documented as +such. + +The function :func:`acb_theta_all` is in fact an interface to the more complete +method + +.. function:: void acb_theta_jet(acb_ptr th, acb_srcptr zs, slong nb, const acb_mat_t tau, slong ord, int all, int sqr, slong prec) + +which allows for several input vectors *z* (for the same matrix *tau*) for more +efficiency, and also evaluates derivatives of theta functions. Its parameters +are as follows: + +- *nb* is the number of vectors `z` that we consider, and the input vector *zs* + should have length *nb* times `g`. The output vector *th* will be the + concatenation of the *nb* individual outputs for each `z`. +- *ord* is the order of derivatives to be computed: we will compute all the + partial derivatives of theta functions `\theta_{a,b}` with respect to the + entries of `z` of total order up to *ord*. (Partial derivatives with respect + to the entries of `\tau` are then accounted for by the heat equation.) We + refer to the documentation below for conventions on the normalization and + ordering of those derivatives. +- *all* is a boolean: if false (zero), then we compute (partial derivatives of) + `\theta_{0,0}` only, rather than `\theta_{a,b}` for all characteristics, + using a slightly faster algorithm. +- *sqr* is as in :func:`acb_theta_all`, but is ignored if *ord* is positive. + +Behind the scenes, :func:`acb_theta_jet` works as follows: it first reduces the +inputs `(z,\tau)` using the action of the Siegel modular group +`\mathrm{Sp}_{2g}(\mathbb{Z})` (the symplectic group) on `\mathbb{C}^g\times +\mathcal{H}_g`, then evaluates (partial derivatives of) theta functions on the +reduced arguments, and finally applies the transformation formula for theta +functions under `\mathrm{Sp}_{2g}(\mathbb{Z})`. The second step (evaluating +theta functions) uses an advanced algorithm based on duplication formulas that +has a uniform, quasi-linear complexity in terms of the required precision. 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. +2`, computes the associated theta values at `z = 0` at 100000 bits of precision +in less than 10 ms, and prints them. .. code-block:: c + #include "acb.h" + #include "acb_mat.h" #include "acb_theta.h" int main() @@ -118,8 +149,7 @@ in roughly 0.1s, and prints them. :: - (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) - + (1.1803 + 0j) +/- (3.34e-3010, 2.34e-3010j), (0.99254 + 0j) +/- (3.28e-3010, 2.78e-3010j), (0.99254 + 0j) +/- (2.37e-3010, 1.87e-3010j), (0.83463 + 0j) +/- (2.73e-3010, 2.23e-3010j), (0.99254 + 0j) +/- (1.08e-3010, 5.79e-3011j), (0 + 0j) +/- (1.35e-3009, 1.35e-3009j), (0.83463 + 0j) +/- (9.64e-3011, 4.63e-3011j), (0 + 0j) +/- (1.13e-3009, 1.13e-3009j), (0.99254 + 0j) +/- (3.20e-3009, 3.15e-3009j), (0.83463 + 0j) +/- (3.79e-3009, 3.74e-3009j), (0 + 0j) +/- (4.04e-3011, 4.04e-3011j), (0 + 0j) +/- (4.80e-3011, 4.80e-3011j), (0.83463 + 0j) +/- (8.30e-3010, 7.80e-3010j), (0 + 0j) +/- (5.18e-3009, 5.18e-3009j), (0 + 0j) +/- (1.00e-3011, 1.00e-3011j), (-6.1135e-3020 + 0j) +/- (2.80e-3010, 2.80e-3010j) The Siegel modular group ------------------------------------------------------------------------------- @@ -141,8 +171,7 @@ 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*. This is - an inline function only. + 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) @@ -182,12 +211,12 @@ where `\alpha,\beta,\gamma,\delta` are `g\times g` blocks. 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. + symplectic matrix in general. .. 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) + algorithm on `\mathcal{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 @@ -198,7 +227,7 @@ where `\alpha,\beta,\gamma,\delta` are `g\times g` blocks. .. function:: void sp2gz_fundamental(fmpz_mat_t mat, slong j) - Sets *mat* to the `j^{\text{th}}` fundamental symplectic matrix as defined + Sets *mat* to the `j`-th fundamental symplectic matrix as defined above. .. function:: int sp2gz_is_correct(const fmpz_mat_t mat) @@ -229,7 +258,8 @@ where `\alpha,\beta,\gamma,\delta` are `g\times g` blocks. .. function:: void sp2gz_inv(fmpz_mat_t inv, const fmpz_mat_t mat) - Sets *inv* to the inverse of the symplectic matrix *mat*. + Sets *inv* to the inverse of the symplectic matrix *mat*. In contrast with + :func:`fmpz_mat_inv`, this involves no computation. .. function:: fmpz_mat_struct * sp2gz_decompose(slong * nb, const fmpz_mat_t mat) @@ -275,31 +305,22 @@ We continue to denote by `\alpha,\beta,\gamma,\delta` the `g\times g` blocks of 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_yinv(arb_mat_t cho, arb_mat_t yinv, const acb_mat_t tau, slong prec) -.. 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. + Sets *yinv* to the inverse of the imaginary part `Y` of *tau*, and sets + *cho* to an upper-triangular Cholesky matrix for `\pi Y`, i.e. to the + upper-triangular matrix `C` with positive diagonal entries such that `\pi Y + = C^T C`. If one cannot determine that `Y` is positive definite at the + current working precision, *yinv* and *cho* are set to indeterminate + matrices. .. 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. + `\tau` do not have a reasonable size or if `\det Y` 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) @@ -312,31 +333,94 @@ We continue to denote by `\alpha,\beta,\gamma,\delta` the `g\times g` blocks of :func:`sp2gz_fundamental`, the determinant of the corresponding cocycle is at least `1-\varepsilon`. +.. function:: slong acb_siegel_kappa(acb_t sqrtdet, const fmpz_mat_t mat, const acb_mat_t tau, int sqr, slong prec) + + If *sqr* is zero (false), returns `0\leq r < 8` such that + `\kappa(\mathit{mat}) = \zeta_8^r` and sets *sqrtdet* to the corresponding + square root of `\det(\gamma\tau + \delta)` in the theta transformation + formula. If *sqr* is nonzero (true), returns instead `0\leq r < 4` such + that `\kappa(\mathit{mat})^2 = i^r` and sets *sqrtdet* to + `\det(\gamma\tau + \delta)`. + + By [Igu1972]_, p. 176 and [Mum1983]_, p. 189, for any symplectic matrix + `m`, any `(z,\tau)\in \mathbb{C}^g\times \mathcal{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 8th root of unity, only well-defined up to sign unless + we choose a particular branch of `\det(\gamma\tau + \delta)^{1/2}` on + `\mathcal{H}_g`. Hence `\kappa(m)^2` is a well-defined 4th root of unity. + + We proceed as follows. 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 `2\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 branch of the square root is chosen + such that the result is `i^{g/2}\det(Y)` when `\tau = iY` is purely + imaginary. + + To compute `\det(\tau_0)^{1/2}` (assuming that *sqr* is false), we pick a + purely imaginary matrix *A* and consider the polynomial `P(t) = \det(A + + \tfrac{t+1}{2} (\tau_0 - 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_siegel_kappa2(const fmpz_mat_t mat) + + Returns `0\leq r < 3` such that `\kappa(\mathit{mat})^2 = i^r`, which makes + sense without reference to a branch of `\det(\gamma\tau + + \delta)^{1/2}`. + + This is a simpler interface to :func:`acb_siegel_kappa` when *sqr* is true. + .. 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 + Sets *tau* to a random matrix in `\mathcal{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. + Sets *tau* to a random reduced matrix in `\mathcal{H}_g` whose imaginary + part possibly has large entries. + +.. function:: void acb_siegel_randtest_compact(acb_mat_t tau, flint_rand_t state, int exact, slong prec) + + Sets *tau* to a random reduced matrix in `\mathcal{H}_g` whose imaginary + part has bounded entries. If *exact* is nonzero (true), then the entries of + *tau* are set to exact (dyadic) complex numbers. .. 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. + Sets *z* to a random vector of length *g*, possibly with large entries. -Theta characteristics -------------------------------------------------------------------------------- +.. function:: void acb_siegel_randtest_vec_reduced(acb_ptr zs, flint_rand_t state, slong nb, const acb_mat_t tau, int exact, slong prec) -.. function:: void acb_theta_char_get_slong(slong * n, ulong a, slong g) + Sets *zs* to the concatenation of *nb* random vectors *z* sampled from + `[-1,1]^g + \tau[-1,1]^g`, i.e. close to being reduced with respect to + `\tau`. If *exact* is nonzero (true), then the entries of *zs* are set to + exact (dyadic) complex numbers. - Sets each entry of *n* to the corresponding bit of *a*. +Theta characteristics +------------------------------------------------------------------------------- -.. function:: ulong acb_theta_char_get_a(const slong * n, slong g) +.. function:: int acb_theta_char_bit(ulong ch, slong j, slong n) - Returns the unique characteristic *a* such that `n\in 2\mathbb{Z}^g + a`. + Returns the `j`-th bit of *ch* seen as an element of `\{0,1\}^{n}` with the + above conventions, counting from `j=0` to `n-1`. .. function:: void acb_theta_char_get_arb(arb_ptr v, ulong a, slong g) @@ -355,117 +439,152 @@ Theta characteristics 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. + Returns true iff the characteristic `(a,b)` is even, i.e. `a^Tb` is + divisible by 2. Odd characteristics `(a,b)` have the property that + `\theta_{a,b}(0,\tau)` is identically zero. -.. function:: int acb_theta_char_is_goepel(ulong ch1, ulong ch2, ulong ch3, ulong ch4, slong g) +.. function:: void acb_theta_char_table(ulong * ch, slong * e, const fmpz_mat_t mat, slong ab) - 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`. + If *ab* encodes a valid characteristic, sets *ch* to the theta + characteristic `(a',b')` and sets *e* to `e(\mathit{mat},a,b)` as in the + transformation formula (see :func:`acb_siegel_kappa`). If *ab* is negative, + then sets *ch* and *e* to vectors of length `2^{2g}` containing this output + for all characteristics from 0 to `2^{2g}-1`. -.. function:: int acb_theta_char_is_syzygous(ulong ch1, ulong ch2, ulong ch3, slong g) +.. function:: void acb_theta_char_shuffle(acb_ptr res, const fmpz_mat_t mat, acb_srcptr th, int sqr, slong prec) - Returns true iff the given characteristics define a syzygous triple, - i.e. they can be completed into a Göpel quadruple. + Partially applies the theta transformation formula to the given vector *th* + for the symplectic matrix *mat* and stores the output in *res*. This omits + the `\kappa`, determinant, and exponential factors from the formula. If + *sqr* is nonzero (true), then replaces `\zeta_8` in the formula by `i` to + mimic the transformation formula on squared theta values. This is only used + for testing. -Ellipsoids: types and macros +Toolbox for derivatives ------------------------------------------------------------------------------- -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. +In this module, we only consider the successive partial derivatives of +`\theta_{a,b}(z,\tau)` with respect to the `g` coordinates of `z`, because +derivatives with respect to `\tau` are accounted for by the heat equation -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. + .. math:: -.. type:: acb_theta_eld_struct + \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}. -.. type:: acb_theta_eld_t +where `\delta` is the Kronecker symbol. 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 - 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. + .. 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. + +This sections gathers methods to work with partial derivatives of holomorphic +functions in general. + +.. 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*. -The following macros are available after *E* of type :type:`acb_theta_eld_t` -has been initialized using :func:`acb_theta_eld_init` below. +.. function:: void acb_theta_jet_tuples(slong * tups, slong ord, slong g) -.. macro:: acb_theta_eld_dim(E) + Sets *tups* to the concatenation of all derivation tuples up to total order + *ord*. - Macro returning `d`. +.. function:: slong acb_theta_jet_index(const slong * tup, slong g) -.. macro:: acb_theta_eld_ambient_dim(E) + Returns *n* such that *tup* is the `n`-th derivation tuple of + length *g*. - Macro returning `g`. +.. function:: void acb_theta_jet_mul(acb_ptr res, acb_srcptr v1, acb_srcptr v2, slong ord, slong g, slong prec) -The following macros are available after *E* has been initialized and then -computed using :func:`acb_theta_eld_set` below. + Sets *res* to the vector of derivatives of the product `fg`, assuming that + *v1* and *v2* contains the derivatives of `f` and `g` respectively. -.. macro:: acb_theta_eld_coord(E, k) +.. function:: void acb_theta_jet_compose(acb_ptr res, acb_srcptr v, const acb_mat_t N, slong ord, slong prec) - Macro returning the common coordinate `n_k` of the points in `E`. This - requires `d \leq k < g`. + Sets *res* to the vector of derivatives of the composition `f(Nz)`, + assuming that *v* contains the derivatives of *f* at the point `Nz`. The + dimension `g` is obtained as the size of the square matrix `N`. -.. macro:: acb_theta_eld_min(E) +.. function:: void acb_theta_jet_exp_pi_i(acb_ptr res, arb_srcptr a, slong ord, slong g, slong prec) -.. macro:: acb_theta_eld_mid(E) + 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*. -.. macro:: acb_theta_eld_max(E) +.. function:: void acb_theta_jet_exp_qf(acb_ptr res, acb_srcptr z, const acb_mat_t N, slong ord, slong prec) - Macros returning the minimum, midpoint, and maximum of `n_{d-1}` in `E` - respectively. + Sets *res* to the vector of derivatives of the function `\exp(\pi i z^T N + z)` at the chosen point `z`. The dimension `g` is obtained as the size of + the square matrix `N`. -.. macro:: acb_theta_eld_nr(E) +Ellipsoids +------------------------------------------------------------------------------- -.. macro:: acb_theta_eld_nl(E) +The most direct way of evaluating Riemann theta functions consists in +evaluating a partial sum of the exponential series defining them, then adding +an error bound coming from the tail of the series. We refer to this strategy as +the *summation algorithms*. - Macros returning the number of right and left children of `E` - respectively. +The upper bound on the tail will be obtained from the triangle inequality. First, we use the relation -.. macro:: acb_theta_eld_rchild(E, k) + .. math:: -.. macro:: acb_theta_eld_lchild(E, k) + \theta_{a,b}(z,\tau) = \exp(\pi i a^T (z + \tfrac b2) + \pi i a^T\tau a/4) \theta_{0,b}(z + \tau\tfrac{a}{2},\tau) - Macros returning a pointer to the `k^{\text{th}}` right (resp. left) child - of `E` as an :type:`acb_theta_eld_t`. +to avoid summing over `\mathbb{Z}^g + \tfrac{a}{2}` with a nonzero `a`. Next, +to analyze the absolute value of each term in the sum defining +`\theta_{0,b}(z,\tau)`, we write: -.. macro:: acb_theta_eld_nb_pts(E) + .. math:: - Macro returning the number of points contained in `E`. + \bigl| \exp(i\pi n^T\tau n + 2n^T (z + \tfrac{b}{2}) \bigr| = \exp(\pi y^T Y^{-1} y) \exp (-\lVert n + Y^{-1}y \rVert_\tau^2) -.. macro:: acb_theta_eld_nb_border(E) +(notation as in the introduction). Thus, the exponential terms whose absolute +values are less than a given threshold correspond to lattice points `n\in +\mathbb{Z}^g` lying outside a certain ball centered in `v = -Y^{-1}y` for +`\lVert\cdot\rVert_\tau`; in other words, we should be computing partial sums +over points `n\in \mathbb{Z}^g` lying in certain ellipsoids, as in +[DHBHS2004]_. This section gathers methods to manipulate such ellipsoids +directly. - 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. +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. 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. -.. macro:: acb_theta_eld_box(E, k) +.. type:: acb_theta_eld_struct - 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`. +.. type:: acb_theta_eld_t -Ellipsoids: memory management and computations -------------------------------------------------------------------------------- + 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, + alllowing it to be passed by reference. .. function:: void acb_theta_eld_init(acb_theta_eld_t E, slong d, slong g) @@ -484,17 +603,35 @@ Ellipsoids: memory management and computations 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. +The following functions are available after *E* has been initialized and then +computed using :func:`acb_theta_eld_init` and :func:`acb_theta_eld_set`. + +.. function:: slong acb_theta_eld_nb_pts(acb_theta_eld_t E) + + Returns the number of points contained in `E`, which is stored in the data + structure. .. 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*. + vectors of length *g*. The vector *pts* must be pre-allocated to the + correct length. + +.. function:: slong acb_theta_eld_box(const acb_theta_eld_t E, slong j) + + Returns an upper bound on the absolute value of the `j`-th coordinate of any + point stored in *E*. We require `0\leq j < g`. + +.. function:: slong acb_theta_eld_nb_border(acb_theta_eld_t E) + + Returns the number of points in the "border" of `E`, a certain set of + points lying just outside `E`. This number is stored in the data structure. .. 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`. + Sets *pts* to the list of all the points in the border of `E`. The vector + *pts* must be pre-allocated to the correct length. This is only used for + testing. .. function:: int acb_theta_eld_contains(const acb_theta_eld_t E, slong * pt) @@ -506,837 +643,912 @@ called successfully. 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}) +.. function:: void acb_theta_eld_distances(arb_ptr ds, acb_srcptr zs, slong nb, const acb_mat_t tau, slong prec) -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]_. + Sets *ds* to the concatenation of the following *nb* vectors of length + `2^g`: for each input vector `z`, we compute `\mathrm{Dist}_\tau(-Y^{-1}y, + \mathbb{Z}^g + \tfrac a2)^2` for all `a\in \{0,1\}^g`, where + `\mathrm{Dist}_\tau` denotes the distance attached to `\lVert \cdot + \rVert_\tau`. -.. 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. + We first round the coordinates of `-Y^{-1}y` to obtain an element of + `\mathbb{Z}^g + \tfrac{a}{2}` providing an upper bound on the distance, + then enumerate all the points in the ellipsoid of that radius to find all + the closer points, if any. -Naive algorithms: main functions +Error bounds in summation algorithms ------------------------------------------------------------------------------- -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 +To compute the correct ellipsoids in summation algorithms for a target working +precision, we use the following upper bound on the tail of the series: by +[EK2025]_, for any `v\in \mathbb{R}^g`, any upper-triangular Cholesky matrix +`C`, any nonnegative *ord*, and any `R\geq 0`, we have .. 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:: + \sum_{n\in C\mathbb{Z}^g + Cv,\ \lVert n\rVert^2 \geq R^2} \lVert n\rVert^{\mathit{ord}} e^{-\lVert n\rVert^2} + \leq (1 + \sqrt{\tfrac{8}{\pi}}) \max\{2,R\}^{g-1} R^{p} e^{-R^2} \prod_{j=0}^{g-1} (1 + \tfrac{\sqrt{2\pi}}{\gamma_j}) - 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}). +where `\gamma_0,\ldots, \gamma_{g-1}` are the diagonal coefficients of +`C`. -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. +.. function:: void acb_theta_sum_radius(arf_t R2, arf_t eps, const arb_mat_t cho, slong ord, slong prec) -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. + Sets *eps* to `2^{-\mathit{prec}}` and chooses *R2* such that the above + upper bound for *R2* and the given *ord* is at most *eps*, where `C` is + *cho*. When *ord = 0*, the square root of *R2* is a suitable ellipsoid + radius for a partial sum of the theta series, and *eps* is an upper bound + on the absolute value of the tail of the series defining + `\widetilde{\theta}_{a,b}`. -.. type:: acb_theta_naive_worker_t +.. function:: void acb_theta_sum_jet_radius(arf_t R2, arf_t eps, const arb_mat_t cho, arb_srcptr v, slong ord, slong prec) - A function pointer type. A function *worker* of this type has the - following signature: + Computes a suitable squared radius *R2* and error bound *eps* on the tail + of the theta series as in :func:`acb_theta_sum_radius`, but in the context + of evaluating partial derivatives of theta functions up to order *ord*. The + input vector *v* should be `-C Y^{-1}y`, where `C` is the Cholesky matrix + for `\pi Y`. - .. 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) + We can rewrite the the differentiated series as - where: + .. math:: - - *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*. + \begin{aligned} + \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)}\\ + &= (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}} \xi_n e^{-\pi (n + Y^{-1}y)^T Y (n + Y^{-1}y)}. + \end{aligned} -.. 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) + where `|\xi_n| = 1`. We ignore the leading multiplicative factor. Writing `m = C n + v`, we have - 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. + .. math:: -.. function:: void acb_theta_naive_00(acb_ptr th, acb_srcptr zs, slong nb, const acb_mat_t tau, slong prec) + 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|}. -.. function:: void acb_theta_naive_0b(acb_ptr th, acb_srcptr zs, slong nb, const acb_mat_t tau, slong prec) + Using the upper bound from :func:`acb_theta_sum_radius`, we see that the + absolute value of the tail of the series, when summing outside the + ellipsoid centered in `v` of radius `R`, is bounded above by - 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. + .. math:: - The associated worker performs one :func:`acb_dot` operation. + (\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}). -.. function:: void acb_theta_naive_fixed_a(acb_ptr th, ulong a, acb_srcptr zs, slong nb, const acb_mat_t tau, slong prec) + The output values *R2* and *eps* are such that this upper bound is at most + *eps* when `R` is the square root of *R2*. - 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`. + To obtain them, we first compute *R2* and *eps* using + :func:`acb_theta_sum_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_\infty\}^{\mathit{ord}}`. Otherwise, + we compute *R2* and *eps* using :func:`acb_theta_sum_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_\infty\}^{\mathit{ord}}`. - We reduce to calling :func:`acb_theta_naive_0b` - by writing +.. function:: void acb_theta_sum_term(acb_t res, acb_srcptr z, const acb_mat_t tau, slong * tup, slong * n, slong prec) - .. math:: + 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`-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. - \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). +.. function:: slong acb_theta_sum_addprec(const arb_t d) - 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. + Returns an integer that is close to `d/\log(2)` if *d* is + finite and of reasonable size, and otherwise returns 0. -Naive algorithms for derivatives +Context structures in summation algorithms ------------------------------------------------------------------------------- -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:: +After the relevant ellipsoid has been computed, summation algorithms only +involve exponential terms in `\tau` and `z`. Sometimes, especially in the +setting of the quasi-linear algorithms below, these exponentials can be +computed once, and then used for several calls to summation functions. This +section introduces context structures to make these manipulations easier. - \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}. +.. type:: acb_theta_ctx_tau_struct -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 +.. type:: acb_theta_ctx_tau_t - .. math:: + An :type:`acb_theta_ctx_tau_t` is an array of length one of type + :type:`acb_theta_ctx_tau_struct` containing all the necessary data to run + the summation algorithm on a given matrix `\tau\in\mathcal{H}_g`. In + particular, it contains a matrix *exp_tau_div_4* whose `(j,k)` entry (when + `j\leq k`) is `\exp(\pi i (2 - \delta_{j,k}) \tau_{j,k}/4)`, where `\delta` + denotes the Kronecker symbol. It also contains the Cholesky matrix for `\pi + Y` if `g>1`. - \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}}}, +.. type:: acb_theta_ctx_z_struct -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. +.. type:: acb_theta_ctx_z_t -The naive algorithms for derivatives will evaluate a partial sum of the -differentiated series: + An :type:`acb_theta_ctx_z_t` is an array of length one of type + :type:`acb_theta_ctx_z_struct` containing all the necessary data to run the + summation algorithm on a given vector `z` (provided that an element of type + :type:`acb_theta_ctx_tau_t` is also given.) In particular, it contains the + values `\exp(2\pi i z_j)` for all `1\leq j\leq g`. If `g>1`, it also + contains the center of the ellipsoids used in summation algorithms at `z`. - .. math:: +.. function:: void acb_theta_ctx_tau_init(acb_theta_ctx_tau_t ctx, int allow_shift, slong g) - \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)}. + Initializes *ctx* for use in dimension *g*. If *allow_shift* is nonzero + (true), then additional fields in *ctx* are initialized to allow for the + evaluation of theta functions `\theta_{a,0}` for nonzero `a`. -.. function:: slong acb_theta_jet_nb(slong ord, slong g) +.. function:: void acb_theta_ctx_tau_clear(acb_theta_ctx_tau_t ctx) - Returns the number of derivation tuples with total order at most *ord*. The - result will be zero if *ord* is negative. + Clears *ctx*. -.. function:: slong acb_theta_jet_total_order(const slong * tup, slong g) +.. function:: void acb_theta_ctx_z_init(acb_theta_ctx_z_t ctx, slong g) - Returns the total derivation order for the given tuple *tup* of length *g*. + Initializes *ctx* for use in dimension *g*. -.. function:: void acb_theta_jet_tuples(slong * tups, slong ord, slong g) +.. function:: void acb_theta_ctx_z_clear(acb_theta_ctx_z_t ctx) - Sets *tups* to the concatenation of all derivation tuples up to total order - *ord*. + Clears *ctx*. -.. function:: slong acb_theta_jet_index(const slong * tup, slong g) +.. function:: acb_theta_ctx_z_struct * acb_theta_ctx_z_vec_init(slong nb, slong g) - Returns *n* such that *tup* is the `n^{\mathrm{th}}` derivation tuple of - length *g*. + Returns a pointer to a vector of *nb* initialized elements of type + :type:`acb_theta_ctx_z_struct` in dimension `g`. -.. function:: void acb_theta_jet_mul(acb_ptr res, acb_srcptr v1, acb_srcptr v2, slong ord, slong g, slong prec) +.. function:: void acb_theta_ctx_z_vec_clear(acb_theta_ctx_z_struct * vec, slong nb) - Sets *res* to the vector of derivatives of the product `fg`, assuming that - *v1* and *v2* contains the derivatives of `f` and `g` respectively. + Clears the elements of type :type:`acb_theta_ctx_z_struct` pointed to by + *vec* as well as the pointer itself. -.. function:: void acb_theta_jet_compose(acb_ptr res, acb_srcptr v, const acb_mat_t N, slong ord, slong prec) +.. function:: void acb_theta_ctx_exp_inv(acb_t exp_inv, const acb_t exp, const acb_t x, int is_real, 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`. + Given a complex value *x* and given *exp* containing `\exp(\pi i x)`, sets + *exp_inv* to `\exp(-\pi i x)`. -.. function:: void acb_theta_jet_exp_pi_i(acb_ptr res, arb_srcptr a, slong ord, slong g, slong prec) + This is computed by complex conjugation from *exp* if *is_real* is nonzero + (true). Otherwise, it is computed by inverting *exp*, except if the result + is indeterminate, in which case we recompute *exp_inv* from *x* directly. - 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_ctx_sqr_inv(acb_t sqr_inv, const acb_t inv, const acb_t sqr, int is_real, slong prec) -.. 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) + Given *inv* and *sqr* containing complex values `\exp(-\pi i x)` and + `\exp(2\pi i x)` respectively, sets *sqr_inv* to `\exp(-2\pi i x)`. - 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}`. + This uses complex conjugation from *sqr* if *is_real* is nonzero (true), + and otherwise a complex squaring from *inv*. - We can rewrite the above sum as +.. function:: void acb_theta_ctx_tau_set(acb_theta_ctx_tau_t ctx, const acb_mat_t tau, slong prec) - .. math:: + Computes and stores in *ctx* the required data for the input matrix + `\tau`. The dimensions must match. - (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)}. +.. function:: void acb_theta_ctx_tau_dupl(acb_theta_ctx_tau_t ctx, slong prec) - We ignore the leading multiplicative factor. Writing `m = C n + v`, we have + Modifies *ctx* in place to correspond to the matrix `2\tau` instead of + `\tau`. This is much cheaper than calling :func:`acb_theta_ctx_tau_set` + again. - .. math:: +.. function:: int acb_theta_ctx_tau_overlaps(const acb_theta_ctx_tau_t ctx1, const acb_theta_ctx_tau_t ctx2) - 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|}. + Returns true iff the data contained in *ctx1* and *ctx2* overlap in the + sense of :func:`acb_overlaps`. This is only used for testing. - 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 +.. function:: void acb_theta_ctx_z_set(acb_theta_ctx_z_t ctx, acb_srcptr z, const acb_theta_ctx_tau_t ctx_tau, slong prec) - .. math:: + Computes and stores in *ctx* the required data for the complex vector + *z*. Here *ctx_tau* should contain context data for the matrix `\tau`. The + dimensions must match. - (\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}). +.. function:: void acb_theta_ctx_z_dupl(acb_theta_ctx_z_t ctx, slong prec) - 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}}`. + Modifies *ctx* in place to correspond to the pair `(2z,2\tau)` instead of + `(z,\tau)`. This is much cheaper than calling :func:`acb_theta_ctx_z_set` + again. -.. function:: void acb_theta_jet_naive_00(acb_ptr dth, acb_srcptr z, const acb_mat_t tau, slong ord, slong prec) +.. function:: void acb_theta_ctx_z_add_real(acb_theta_ctx_z_t res, const acb_theta_ctx_z_t ctx, const acb_theta_ctx_z_t ctx_real, slong prec) - Sets *dth* to the vector of derivatives of `\theta_{0,0}` at the given - point `(z,\tau)` up to total order *ord*. + Assuming that *ctx* and *ctx_real* correspond to pairs `(z,\tau)` and `(t, + \tau)` respectively where `t` is a real vector, sets *res* to a valid + context for the pair `(z + t,\tau)`. - 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. +.. function:: void acb_theta_ctx_z_common_v(arb_ptr v, const acb_theta_ctx_z_struct * vec, slong nb, slong prec) - 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`. + Given a vector *vec* of valid contexts for pairs + `(z_1,\tau),\ldots,(z_n,\tau)`, sets *v* to a valid ellipsoid center for + use in :func:`acb_theta_eld_set` when running the summation algorithm for + all these pairs, constructed using :func:`arb_union`. -.. 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) +.. function:: int acb_theta_ctx_z_overlaps(const acb_theta_ctx_z_t ctx1, const acb_theta_ctx_z_t ctx2) - 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. + Returns true iff the data contained in *ctx1* and *ctx2* overlap in the + sense of :func:`acb_overlaps`. This is only used for testing. -Quasi-linear algorithms: presentation +Summation algorithms ------------------------------------------------------------------------------- -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`, +In this module, summation algorithms are mainly used for low to moderate +precisions due to their higher asymptotic complexity. Since summations at low +precisions are a key step in the quasi-linear algorithms, the summation +functions below optimized in many ways and should already compare favorably to +other software packages that evaluate theta functions. - .. math:: +We always assume in this section that the inpits `(z,\tau)` have been +reduced. In particular, this allows us to use only one ellipsoid when several +vectors `z` are given. - \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). +After the relevant ellipsoid *E* has been computed, the main worker inside each +version of the summation algorithm will process one line (i.e. 1-dimensional +ellipsoid) in *E*. 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: -In particular, +- 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:: - \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} + x = \exp(2 \pi i z_0) \prod_{k = 1}^{g-1} \exp(2 \pi i n_k \tau_{0,k}), -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 +- the cofactor `c\in \mathbb{C}` given by .. 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). + 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}). -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)`. +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 +summation algorithm will rely on slightly different workers, so introducing a +function pointer type is helpful to avoid code duplication. -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 `g=1`, the code does not rely on ellipsoids and worker functions, and +calls :func:`acb_modular_theta_sum` from :ref:`acb_modular.h ` +instead. -- 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. +.. type:: acb_theta_sum_worker_t -- 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`. + A function pointer type. A function *worker* of this type has the + following signature: -- 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})`. + .. 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*. -Quasi-linear algorithms: distances +.. function:: void acb_theta_sum_sqr_pow(acb_ptr * sqr_pow, const acb_mat_t exp_tau, const acb_theta_eld_t E, slong prec) + + For each `0\leq j\leq g-1`, sets *sqr_pow[j]* to a vector of length `B_j + + 1` containing `e_j^{n^2}` for `0\leq n \leq B_j`, where `B_j` is an upper + bound on the absolute values of `j`-th coordinates of points in *E* + (obtained by :func:`acb_theta_eld_box`) and `e_{j}` is the `j`-th diagonal + entry of *exp_tau*. The entries of *sqr_pow* need to be preallocated to the + correct lengths. + +.. function:: void acb_theta_sum_work(acb_ptr th, slong len, 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, acb_theta_sum_worker_t worker) + + Runs the summation algorithm on the ellipsoid *E*, assuming `g\geq 2`. The input is as follows: + + - for each `1\leq j\leq k\leq g`, the `(j,k)` entries of the matrices *exp_tau* + and *exp_tau_inv* whose should contain `\exp(\pi i (2 - + \delta_{j,k}) \tau_{j,k})` and its inverse, respectively. + - *E* is the ellipsoid we want to sum on. + - *sqr_pow* should be as output by :func:`acb_theta_sum_sqr_pow` on + *exp_tau* and *E*. + - the vectors *exp_zs* and *exp_zs_inv* should have length *nb* times + *g*. For each `z` stored in *zs*, the corresponding pieces of *exp_zs* + and *exp_zs_inv* should contain `\exp(\pi i z_j)` for `1\leq j\leq g` and + their inverses, respectively. + - the parameters *len*, *ord* and the output vector *th* are passed to + *worker* when processing each individual line in *E*. + + The data associated with *zs* and `\tau` is typically stored in contexts of + type :type:`acb_theta_ctx_tau_t` and :type:`acb_theta_ctx_z_t` + respectively. No error bound coming from the tail of the theta series is + added. + +.. function:: void acb_theta_sum(acb_ptr th, const acb_theta_ctx_z_struct * vec, slong nb, const acb_theta_ctx_tau_t ctx_tau, arb_srcptr distances, int all_a, int all_b, int tilde, slong prec) + + Evaluates theta functions at each of the *nb* pairs `(z,\tau)` + corresponding to a context stored in *vec* together with *ctx_tau* using + summation. Precisely what we compute depends on the parameters *all_a*, + *all_b* and *tilde*: + + - if *all_a* is false (zero), we only compute `\theta_{a,b}` for `a=0`, + otherwise `a` varies from `0` to `2^g - 1`. + - if *all_b* is false (zero), we only compute `\theta_{a,b}` for `b=0`, + otherwise `b` varies from `0` to `2^g - 1`. + - if *tilde* is true (nonzero), then we compute `\widetilde{\theta}_{a,b}` + instead of `\theta_{a,b}`. + + In this function, the absolute error radius we add from the tail of the + exponential series depend on `a`. The amount of precision added is + controlled by *distances*, a vector of length `2^g` (the same for all + vectors *z*). One could for instance set *distances* to zero, or compute it + as in :func:`acb_theta_eld_distances`, which makes sense when the different + values of *z* differ by real vectors. + +.. function:: void acb_theta_sum_jet(acb_ptr th, const acb_theta_ctx_z_struct * vec, slong nb, const acb_theta_ctx_tau_t ctx_tau, slong ord, int all_a, int all_b, slong prec) + + Sets *th* to the vector of derivatives of theta functions up to total order + *ord*, at each of the *nb* pairs `(z,\tau)` specified by the contexts, + using summation. Precisely which characteristics `(a,b)` we consider is + controlled by the parameters *all_a* and *all_b* as in + :func:`acb_theta_sum`. + +AGM steps ------------------------------------------------------------------------------- -.. function:: void acb_theta_dist_pt(arb_t d, arb_srcptr v, const arb_mat_t C, slong * n, slong prec) +The quasi-linear algorithm to evaluate theta functions uses the following +*duplication formula*: for all `z,z'\in \mathbb{C}^g` and `\tau\in +\mathcal{H}_g`, - Sets *d* to `\lVert v + Cn\rVert^2` for the usual Euclidean norm. + .. math:: -.. function:: void acb_theta_dist_lat(arb_t d, arb_srcptr v, const arb_mat_t C, slong prec) + \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). - 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. +Applying the duplication formula amounts to taking a step in a (generalized) +AGM sequence. Note that the formula still holds if we replace `\theta_{a,0}` by +the normalized version `\widetilde{\theta}_{a,0}`. -.. function:: void acb_theta_dist_a0(arb_ptr d, acb_srcptr z, const acb_mat_t tau, slong prec) +This section gathers methods to apply duplication formulas efficiently while +minimizing precision losses. In the case `z = z'`, the duplication formula is +typically followed by an extraction of square roots using low-precision +approximations to make the correct choice. - 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:: void acb_theta_agm_sqrt(acb_ptr res, acb_srcptr a, acb_srcptr roots, slong nb, slong prec) -.. function:: slong acb_theta_dist_addprec(const arb_t d) + Sets each of the *nb* entries of *res* to a square root of the + corresponding entry of `a`. The choice of sign is determined by *roots*: + each entry of *res* will overlap the corresponding entry of *roots* but not + its opposite. When this is not possible, we set the corresponding entry of + *res* to the :func:`acb_union` of both square roots (when both overlap + *roots*) or an indeterminate value (when none overlap *roots*). - Returns an integer that is close to *d* divided by `\log(2)` if *d* is - finite and of reasonable size, and otherwise returns 0. +.. function:: void acb_theta_agm_mul(acb_ptr res, acb_srcptr a1, acb_srcptr a2, slong g, int all, slong prec) -Quasi-linear algorithms: AGM steps -------------------------------------------------------------------------------- + For each `0\leq k < 2^g`, sets the `k`-th entry of *res* to -.. function:: void acb_theta_agm_hadamard(acb_ptr res, acb_srcptr a, slong g, slong prec) + .. math:: - 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`. + \sum_{b\in \{0,1\}^g} a_{1,b}\, a_{2, b + k} -.. function:: void acb_theta_agm_sqrt(acb_ptr res, acb_srcptr a, acb_srcptr rts, slong nb, slong prec) + where addition is meant in `(\mathbb{Z}/2\mathbb{Z}^g)` (a bitwise xor). If + *all* is nonzero (true), then we additionally compute, for each `1\leq a + \leq 2^g-1`, the vector of length `2^g` whose `k`-th entry contains - 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 overlaps *rts*, then `r_k` is set to an - indeterminate value. + .. math:: -.. function:: void acb_theta_agm_mul(acb_ptr res, acb_srcptr a1, acb_srcptr a2, slong g, slong prec) + \sum_{b\in \{0,1\}^g} (-1)^{a^T b} a_{1,b} a_{2, b+k}, - 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). + so *res* has total length `2^{2g}` in that case. 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 + values of the entries in *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) +.. function:: void acb_theta_agm_mul_tight(acb_ptr res, acb_srcptr a0, acb_srcptr a, arb_srcptr d0, arb_srcptr d, slong g, int all, 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 + :func:`acb_theta_eld_distances` on pairs `(0,\tau)` and `(z,\tau)` + respectively, performs the same computation as :func:`acb_theta_agm_mul` on + the vectors *a0* and *a* (and the parameter *all*) 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*, for instance when + applying the duplication formula on normalized theta values. + + We first compute `m, \varepsilon` such that the following holds: for each + `0\leq k < \mathit{nb}`, if `d_k` (resp. `a_k`) denotes the `k`-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 + `2^g e^{-d_k} (m_0 \varepsilon + m \varepsilon_0 + + \varepsilon\varepsilon_0)` to the error bound on the `k`-th entry of + *res*. This is valid because of the parallelogram identity: keeping + notation from :func:`acb_theta_eld_distances`, for each `b\in \{0,1\}^g`, + we have .. 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 + \leq \mathrm{Dist}_\tau(-Y^{-1}y, \mathbb{Z}^g + \tfrac{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 +Quasilinear algorithms on reduced input ------------------------------------------------------------------------------- -The functions in this section will work best when `\tau` lies in the reduced -domain, however `\mathrm{Im}(\tau)` may have large eigenvalues. +The general duplication formula specializes to the three following equalities: + + .. math:: -.. type:: acb_theta_ql_worker_t + \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} - A function pointer type. A function *worker* of this type has the - following signature: +Suppose that we wish to compute `\theta_{a,0}(0,\tau)` for all `a\in \{0,1\}^g` +and a reduced matrix `\tau\in \mathcal{H}_g`. Applying the last of the above +duplication formulas `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 `\pi Y`. Provided that `2^n` is roughly *prec*, +we have to sum only `O_g(1)` terms in the summation algorithm to evaluate +`\theta_{a,0}(0,2^n\tau)` at "shifted absolute precision" *prec*, i.e. absolute +precision *prec* `+\ d^2/\log(2)`. - .. 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 absolute 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 +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 make the +correct choices of square roots at each step by computing low-precision +approximations with the summation 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`, +for theta values at `z\neq 0`, and for all characteristics: + +- If we discover 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 (first + formula above), then `\theta_{a,0}(0, 2^k\tau)` using divisions (second + 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. In practice, we choose `t` at random and obtain a probabilistic + algorithm with a negligible failure probability. - .. math:: +- 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 first and third formulas at each step. - 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 +- 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`. + +- To simplify the precision management, we use :func:`acb_theta_agm_mul_tight` + and work with normalized theta values throughout, which also satisfy the + duplication formulas. + +- If the eigenvalues of `Y` have different orders of magnitude, then as we + consider `\tau`, `2\tau`, `4\tau`, etc., the ellipsoids we would consider in + the summation algorithm become very thin in one direction while still being + thick in other directions. In such a case, we can rewrite theta values as a + sum of `O(1)` theta values in lower dimensions. This increases the efficiency + of the algorithm while ensuring that the absolute precisions we consider are + always of the order of *prec*. + +- Finally, we note that the duplication 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). + + We use those generalized formulas for the very last duplication step when + needed. + +We always assume in this section that the inputs `(z,\tau)` have been +reduced. + +.. function:: int acb_theta_ql_nb_steps(slong * pattern, const acb_mat_t tau, int cst, slong prec) + + Determines how many duplication steps we should perform to evaluate theta + functions at `\tau` at precision *prec*, and at which steps we should fall + back to lower dimensions, if any. The flag *cst* should be set to nonzero + (true) iff theta functions at `z\neq 0` are to be computed. + + The output is stored in *pattern*, a vector of length `g`. Roughly + speaking, the `j`-th entry of *pattern* is a nonnegative integer `m` such + that `2^m \gamma_j^2` is of the order of *prec*, where `\gamma_j` denotes + the `j`-th diagonal coefficient of the Cholesky matrix for `\pi Y`. In other + words, the ellipsoid we need to consider in the summation algorithm at + `2^m\tau` has width `O(1)` in the direction of the `j`-th canonical basis + vector in `\mathbb{R}^g`. Because `\tau` is assumed to be reduced, we + expect *pattern* to be a roughly decreasing vector. + + If some entries of the Cholesky matrix are interminate or too extreme for a + reasonable `m` to be computed, then the output is 0, and otherwise 1. + + Modifying this function is the main way to tune the behavior of the + quasi-linear algorithms to evaluate theta functions. + +.. function:: int acb_theta_ql_lower_dim(acb_ptr * new_zs, acb_ptr * cofactors, slong ** pts, slong * nb, arf_t err, slong * fullprec, acb_srcptr z, const acb_mat_t tau, arb_srcptr distances, slong s, ulong a, slong prec) + + Implements the dimension-lowering strategy for evaluating theta functions. The input is as follows: + + - `(z,\tau)` should be an exact element of `\mathbb{C}^g\times + \mathcal{H}_g` (ideally reduced) + - *distances* should be the output of :func:`acb_theta_eld_distances` on + this pair + - *s* should be an integer between `1` and `g-1`; we will reduce the + evaluation of theta functions from dimension `g` to dimension `s` + - *a* should be an integer between `0` and `2^{g-s}-1` included; we will + only decompose `\widetilde{\theta}_{a',0}(z,\tau)` when the last `g - s` + bits of `a'` correspond to those of *a*. + + We then proceed as follows: + + - *fullprec* is set to the binary precision at which those theta values + `\widetilde{\theta}_{a',0}(z,\tau)` should be computed. We take + distances into account, so *fullprec* is *prec* plus additional guard + bits derived from the maximum of the entries in *distances* corresponding + to the possible characteristics *a'*. + - *R2* and *err* are set as in :func:`acb_theta_sum_radius` for this choice + of *fullprec*. (*R2* is not part of the output.) Thus, + `\widetilde{\theta}_{a',0}(z,\tau)` can be obtained by summing over an + ellipsoid of squared radius *R2* and adding an error *err* coming from + the tail. We do *not* compute that possibly huge ellipsoid. + - Let `n\in \mathbb{Z}^g + \tfrac{a'}{2}` be a point in that + ellipsoid. Write `a' = (a_0,a)` and `n = (n_0,n_1)` where `n_0\in + \mathbb{Z}^s + \tfrac{a_0}{2}` and `n_1\in \mathbb{Z}^{g - s} + + \tfrac{a}{2}`. By the Pythagorean theorem, the possible values for `n_1` + all lie in an ellipsoid of radius *R2* in dimension `g-s`, whose Cholesky + matrix is the lower-right part of a Cholesky matrix for `\pi Y`. This new + ellipsoid is meant to contain very few points. We list all possible + values for `n_1 - \tfrac{a}{2}` (which lies in `\mathbb{Z}^g`) in *pts*, + and set *nb* to the number of those points. Note that *pts* will have to + be freed by the user afterwards. + - For each `n_1 - \tfrac{a}{2}` listed in *pts*, then the sum of the + corresponding terms in the theta series is .. math:: - |\theta_{a,b}(z,\tau) - c i^{\,n_1^Tb_1} \theta_{a_0,b_0}(z', \tau_0)| \leq u + e^{\pi i \bigl(n_1^T \tau_1 n_1 + 2 n_1^T z_1 + \pi y_0^T Y_0 y_0 - \pi y^T Y y \bigr)} + \widetilde{\theta}_{a_0,0}(z_0 + x n_1, \tau_0). + + where `\tau = \Bigl(\begin{smallmatrix} \tau_0 & x\\x^T & + \tau_1\end{smallmatrix}\Bigr)` and `z = (z_0,z_1)`. Thus, we allocate + *new_zs* to contain *nb* vectors of length `g` and set the corresponding + entry to `z_0 + x n_1` (which is still exact). We also allocate + *cofactors* to be a vector of length *nb* and set its corresponding entry + to the above exponential factor. Both *new_zs* and *cofactors* will have + to be freed by the user. + +.. function:: void acb_theta_ql_recombine(acb_ptr th, acb_srcptr th0, acb_srcptr cofactors, const slong * pts, slong nb, const arf_t err, slong fullprec, slong s, ulong a, int all, slong g, slong prec) + + Performs the converse to :func:`acb_theta_ql_lower_dim`, namely recovers + normalized theta values `\widetilde{\theta}_{a',0}(z,\tau)` from the output + of :func:`acb_theta_ql_lower_dim` and theta values in dimension `s`. The + input is as follows: + + - *cofactors*, *pts*, *nb*, *err*, *fullprec*, *s*, *a*, *g*, *prec* should + be as output by :func:`acb_theta_ql_lower_dim`. + - If *all* is true (nonzero), then *th0* should be a concatenation of *nb* + vectors of length `2^{2s}` containing + `\widetilde{\theta}_{a_0,b_0}(z_0,\tau_0)` for all characteristics + `(a_0,b_0)` in dimension `s`, where `z_0` runs through *new_zs* as output + by :func:`acb_theta_ql_lower_dim`, and `\tau_0` is defined as above. If + *all* is false (zero), then *th0* should be a concatenation of *nb* + vectors of length `2^{s}` containing + `\widetilde{\theta}_{a_0,0}(z_0,\tau_0)` only. + + The output, stored in *th*, is either the vector containing + `\widetilde{\theta}_{a,b}(z,\tau)` for all `g`-dimensional characteristics + `(a,b)` (if *all* is true) or only `\widetilde{\theta}_{a,0}(z,\tau)` for + all `a` (if *all* is false), where `(z,\tau)` was the initial input to + :func:`acb_theta_ql_lower_dim`. + +.. function:: int acb_theta_ql_setup(acb_ptr rts, acb_ptr rts_all, acb_ptr t, slong * guard, slong * easy_steps, acb_srcptr zs, slong nb, const acb_mat_t tau, arb_srcptr distances, slong nb_steps, int all, slong prec) + + Sets up the structure of AGM steps to evaluate theta functions at the given + *nb* pairs `(z,\tau)` where `z` runs through *zs*, which are assumed to be + exact and reduced, using *nb_steps* duplication steps. The parameters *nb* + and *nb_steps* must be at least one, and *zs* must begin with the zero + vector in `\mathbb{C}^g`. If `(z,\tau)` are not exact, then the output will + still be mathematically correct, but NaN values or unreasonable precision + losses might occur. + + The rest of the input is as follows: + + - *distances* should be the concatenation of *nb* vectors of length `2^g` + computed by :func:`acb_theta_eld_distances` for each pair `(z,\tau)`. + - *nb_steps* should be the number of times we wish to apply the duplication + formulas before falling back to either the summation algorithms or the + dimension-lowering strategy. + - if *all* is nonzero (true), then we will compute + `\widetilde{\theta}_{a,b}(z,\tau)` for all characteristics `(a,b)`, and + otherwise only `\widetilde{\theta}_{a,0}(z,\tau)`. + + The vectors *rts*, *rts_all*, *t*, and *easy_steps* should be + preinitialized with lengths `2^g \times 3\times + \mathit{nb}\times\mathit{nb\_steps}`, `2^{2g}\times\mathit{nb}` (only used + if *all* is true), `g` and *nb* respectively, while *guard* is a pointer to + one :type:`slong`. + + We proceed as follows. Initially, we work at a very low precision such as 8. + + 1. For each `z`, we use the summation algorithms to obtain approximations + of `\widetilde{\theta}_{a,b}(z,\tau)` (if *all* is true) or + `\widetilde{\theta}_{a,0}(z,\tau)` (if *all* is false), and store them + in *rts_all* or *rts* respectively. We adjust the error bounds in terms + of *distances*, so that the computed approximations do not contain zero + with a good probability. If none of the computed approximations contains + zero, it means that we can successfully apply the last (and simplest) + duplication formula for the last step of the quasi-linear algorithm. In + that case, we go on and compute approximations of + `\widetilde{\theta}_{a,0}(2^k z,2^k\tau)`, for `k = 1,2,` etc., up to + *nb_steps*-1 or until one of the approximations we compute contains + zero, taking distances into account at each step. We store the computed + values in *rts*, and set the corresponding entry of *easy_steps* to be + the number of steps for which the simplest duplication formula can be + applied. + 2. At that point, if the entries of *easy_steps* are all equal to *nb_steps*, + we are done. Otherwise, we pick an auxiliary vector `t` at + random. The 1st entry of *easy_steps*, corresponding to `z=0`, is set to + the minimal value in *easy_steps* (this is necessary to be able to apply + the duplication formulas.) + 3. For each `z`, if `m` denotes the corresponding entry of *easy_steps*, we + use the summation algorithms to compute approximations of + `\widetilde{\theta}_{a,0}(2^k (z + t), 2^k\tau)` and + `\widetilde{\theta}_{a,0}(2^k(z + 2t), 2^k\tau)` for each `k` between + `m` and *nb_steps*-1 included at low precision. (We only need the second + vector for `k=0`.) If one of these values contains zero, we restart step + 3 with another `t` (we allow a small number of such retries, such as + 4). We store those approximations in *rts*. If `k=0` and *all* is true, + then the values we need are `\widetilde{\theta}_{a,b}(z+2t,\tau)` for + all `(a,b)` instead; those are stored in *rts_all*. + 4. If no suitable `t` was found in step 4, then we double the working + precision and go back to step 1. We allow this until the working + precision reaches *prec*. After that, if `t` still cannot be found, then + we declare failure and output 0. This should only happen with negligible + probability for well-formed input. The output value if 1 if a suitable + `t` was found. + + Finally, we set *guard* to the total number of bits of precision we expect + to lose when actually performing the suggested duplication steps later on. + +.. function:: void acb_theta_ql_exact(acb_ptr th, acb_srcptr zs, slong nb, const acb_mat_t tau, const slong * pattern, int all, int shifted_prec, slong prec) + + Runs the full quasi-linear algorithm to evaluate theta functions at the + given *nb* pairs `(z,\tau)` where `z` runs through *zs*, which are assumed + to be exact and reduced. If `(z,\tau)` are not exact, then the function + will still be correct, but NaN values or unreasonable precision losses + might occur. + + The output is either the collection of theta values + `\widetilde{\theta}_{a,b}(z,\tau)` for all `a,b` or + `\widetilde{\theta}_{a,0}(z,\tau)` for all `a` (depending on whether *all* + is true or not) for each vector `z` in *zs*, and is stored in *th*. If + *shifted_prec* is nonzero (true), then the precision to which these values + are computed will take distances into account similarly to + :func:`acb_theta_sum`. + + The input *pattern* conditions how many duplication steps will be performed + and when to apply the dimension-lowering strategy (if at all). If zero + duplication steps are needed, we call :func:`acb_theta_sum` + directly. Otherwise, we call :func:`acb_theta_ql_setup`, which we expect to + succeed with overwhelming probability. The initial theta values required + in the duplication formulas are computed either by the + summation algorithms or, if the dimension-lowering strategy is used, by + calling :func:`acb_theta_ql_lower_dim`, making a recursive call to + :func:`acb_theta_ql_exact` in a lower dimension but (possibly) a longer + list of vectors *zs*, and finally recombining the values with + :func:`acb_theta_ql_recombine`. + +.. function:: void acb_theta_ql_local_bound(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}(\cdot,\tau)|` 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* in the context of finite differences (see + :func:`acb_theta_ql_jet_fd` below). We always ensure that *rho* is at most + 1 and *c* is at least 1. - when the last `g-s` coordinates of `a` equal `n_1` modulo 2, or + 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:: - |\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 + c_0 = 2^g \prod_{j=0}^{g-1} (1 + 2\gamma_j^{-1}), .. 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}))} + c_1 = \sqrt{\pi y^T Y^{-1} y}, - 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))`. + .. math:: -.. function:: void acb_theta_ql_all(acb_ptr th, acb_srcptr z, const acb_mat_t tau, int sqr, slong prec) + c_2 = \sup_{\lVert x \rVert_\infty\leq 1} \sqrt{\pi x^T Y^{-1} x}. - 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). + One can easily compute an upper bound on `c_2` from the Cholesky + decomposition of `\pi Y^{-1}`. We then look for a value of `\rho` that + minimizes `\exp((c_1 + c_2\rho)^2)/\rho^{2\mathit{ord}+1}`, i.e. we set + `\rho` to minimum of 1 and the positive root of `2c_2\rho (c_1 + c_2\rho) = + 2\mathit{ord}+1`. - 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. +.. function:: void acb_theta_ql_jet_error(arb_ptr err, acb_srcptr z, const acb_mat_t tau, acb_srcptr dth, slong ord, slong prec) -Quasi-linear algorithms: derivatives -------------------------------------------------------------------------------- + Assuming that *dth* contains (approximations of) the derivatives of a theta + function `\theta_{a,b}` up to total order `\mathit{ord} + 2` at `(z,\tau)`, + 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. This uses the heat equation and + a Lipschitz-type inequality. -We implement an algorithm for derivatives of theta functions on the reduced -domain based on finite differences. Consider the Taylor expansion: +.. function:: void acb_theta_ql_jet_fd(acb_ptr th, acb_srcptr zs, slong nb, const acb_mat_t tau, slong ord, int all, slong prec) - .. math:: + Evaluates partial derivatives of theta functions `\theta_{a,b}` with + respect to `z` at the given *nb* points `(z,\tau)`. The characteristic `a` + varies from `0` to `2^g-1`, and so does *b* if *all* is true (nonzero). If + *all* is false, then we only consider `b=0`. The result is a concatenation + of *nb* times `2^{2g}` (or `2^g`) vectors of derivatives. - \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}}. + We rely on finite differences on the output of :func:`acb_theta_ql_exact`, + as follows. Consider the Taylor expansion: -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:: - .. 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}}. - \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|}. + 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`-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 -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:: - .. 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|}. - |T|\leq 2c g\,\frac{\varepsilon^{|p|+m}}{\rho^m}. + 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 -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. + .. math:: -.. function:: void acb_theta_jet_ql_bounds(arb_t c, arb_t rho, acb_srcptr z, const acb_mat_t tau, slong ord) + |T|\leq 2c g\,\frac{\varepsilon^{|p|+m}}{\rho^m}. - 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*. + 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. - 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 + The algorithm based on finite differences computes `c` and `\rho` using + :func:`acb_theta_ql_local_bound`, chooses a suitable `\varepsilon`, strips + `(z,\tau)` of their error bounds, increases the working precision to + account for division by `\varepsilon^{\mathit{ord}}\cdot + (\mathit{ord}+1)^g`, calls :func:`acb_theta_ql_exact` on all the auxiliary + points for `m = \mathit{ord} + 1` at a higher working precision, performs + the relevant discrete Fourier transforms, and finally restores provably + correct error bounds on the results using :func:`acb_theta_ql_jet_error` + and derivatives to order *ord* + 2 computed at low precision. This + algorithm runs in quasi-linear time in `\mathit{prec}\cdot + \mathit{ord}^{\,g}` for any fixed `g`. - .. math:: +.. function:: void acb_theta_ql_jet(acb_ptr th, acb_srcptr zs, slong nb, const acb_mat_t tau, slong ord, int all, slong prec) - c_0 = 2^g \prod_{j=0}^{g-1} (1 + 2\gamma_j^{-1}), + Same as :func:`acb_theta_ql_jet_fd`, but makes an automatic choice of + algorithm between finite differences and direct summation depending on the + working precision. - .. math:: +Reduction and main functions +------------------------------------------------------------------------------- - c_1 = \sqrt{\pi y^T Y^{-1} y}, +.. function:: void acb_theta_jet_notransform(acb_ptr th, acb_srcptr zs, slong nb, const acb_mat_t tau, slong ord, ulong ab, int all, int sqr, slong prec) + + Same as :func:`acb_theta_jet`, but does not attempt to reduce the input + pairs `(z,\tau)`. When *all* is false, the parameter *ab* can be used to + specify an individual characteristic `(a,b)` so that we will compute + (derivatives of) `\theta_{a,b}` instead of `\theta_{0,0}`. This function + should only be used when the input is already known to be reduced. + + Depending on the cases, we use the following formulas before calling + :func:`acb_theta_ql_jet`: + + - if *ord* is zero and *all* and *sqr* are both true, we use the + duplication formula .. math:: - c_2 = \sup_{\lVert x \rVert_\infty\leq 1} - \sqrt{\pi x^T \mathrm{Im}(\tau)^{-1} x}. + \theta_{a,b}(z,\tau)^2 = \sum_{a'\in (\mathbb{Z}/2\mathbb{Z})^g} + \theta_{a',0}(0,2\tau) \theta_{a+a',0}(2z,2\tau). - 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 -------------------------------------------------------------------------------- + - if *all* is false and *ab* is zero, we use the 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:: - .. math:: + \theta_{0,0}(z,\tau) = \sum_{a \in (\mathbb{Z}/2\mathbb{Z})^g} + \theta_{a,0}(2z, 4\tau). - \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) + - if *all* is false and *ab* is not zero, we use the formula -where + .. math:: -- `\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`. + \theta_{a,b}(z,\tau) = e^{\pi i (a^T \tau a/4 + a^T b + 2 a^T z)} + \theta_{0,0}(z + \tau\tfrac{a}{2} + \tfrac{b}{2}, \tau). -.. function:: ulong acb_theta_transform_char(slong * e, const fmpz_mat_t mat, ulong ab) + to fall back to the case where *ab* is zero. - Returns the theta characteristic `(a',b')` and sets *e* to `e(\mathit{mat},a,b)`. +.. function:: int acb_theta_reduce_tau(acb_ptr new_zs, acb_mat_t new_tau, fmpz_mat_t mat, acb_mat_t N, acb_mat_t ct, acb_ptr exps, acb_srcptr zs, slong nb, const acb_mat_t tau, slong prec) -.. function:: void acb_theta_transform_sqrtdet(acb_t res, const acb_mat_t tau, slong prec) + Reduces the input matrix `\tau` under the action of the modular group + `\mathrm{Sp}_{2g}(\mathbb{Z})` and modifies the *nb* input vectors stored + in *zs* according to the theta transformation formula. - 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. + The output is as follows: - 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]_. + - *new_tau* is the reduced matrix, + - *mat* is the symplectic matrix such that *new_tau* is the result of *mat* + acting on *tau*, + - *ct* is the transpose of `(\gamma \tau + \delta)^{-1}` where + `\gamma,\delta` are the lower `g\times g` blocks of *mat*, + - *N* is the matrix `i \pi \gamma \cdot \mathit{ct}` that appears in the + transformation formula, + - and finally *new_zs* is the list of *nb* vectors in `\mathbb{C}^g` + corresponding to the elements in *zs* multiplied by *ct* on the left. -.. function:: slong acb_theta_transform_kappa(acb_t sqrtdet, const fmpz_mat_t mat, const acb_mat_t tau, slong prec) + If reduction was unsuccessful (usually indicating that the input is + malformed or that the working precision is insufficient to detect that `Y` + is positive definite), the return value is 0 and the above output is left + undefined. Otherwise, the return value is 1. - 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)`. +.. function:: int acb_theta_reduce_z(acb_ptr new_zs, arb_ptr rs, acb_ptr cs, acb_srcptr zs, slong nb, const acb_mat_t tau, slong prec) - 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`. + Reduces the *nb* vectors stored in *zs* in the context of evaluating theta + functions with *tau* as a second argument. -.. function:: slong acb_theta_transform_kappa2(const fmpz_mat_t mat) + The output vectors *new_zs*, *rs* and *cs* should have lengths *nb* times + `g`, *nb* times `g`, and *nb* respectively. For a given vector *z* + appearing in *zs*, we round `Y^{-1}y` to the nearest even, integral vector + `r`, and store it in *rs*. Then, we consider the vector `z - \tau r`, + substract the nearest even integral vector from its real part, and store + the result `z'` into *new_zs*. For all characteristics `a,b`, we have - 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}`. + .. math:: - We adopt a similar strategy to :func:`acb_theta_transform_kappa` but do not - call :func:`acb_theta_transform_sqrtdet`. + \theta_{a,b}(z',\tau) = e^{- i \pi r^T (z + z')} \theta_{a,b}(z,\tau). -.. function:: void acb_theta_transform_proj(acb_ptr res, const fmpz_mat_t mat, acb_srcptr th, int sqr, slong prec) + Finally, we store this exponential factor as the corresponding entry of *cs*. - 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. + If rounding the imaginary part to integers does not succeed due to extreme + values, then the return value is 0 and the output vectors are left + undefined. Otherwise, the return value is 1. - 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. +We then assemble the main function :func:`acb_theta_jet` as follows. If +:func:`acb_theta_reduce_tau`, then :func:`acb_theta_reduce_z` succeed, we call +:func:`acb_theta_jet_notransform` on the reduced pairs `(z,\tau)`. We finally +apply the transformation formula with the help of :func:`acb_theta_char_table` +and :func:`acb_siegel_kappa`. If reduction does not succeed, then the +output is set to indeterminate values. Dimension 2 specifics ------------------------------------------------------------------------------- @@ -1347,8 +1559,8 @@ Siegel modular forms. This section contains methods to do so, in analogy with 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 +`f: \mathcal{H}_g\to \mathbb{C}_j[X]` (the vector space of polynomials of degree +at most `j`) such that for any `\tau\in \mathcal{H}_g` and `m\in \mathrm{Sp}_4(\mathbb{Z})`, we have .. math:: @@ -1368,9 +1580,9 @@ 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 = +allow them to have a pole along the diagonal `\mathcal{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 +\tau_2\end{smallmatrix}\bigr)\bigr\}\subset \mathcal{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`, @@ -1378,32 +1590,21 @@ 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) +.. 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, int lead, 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 + Sets *res* to the `k`-th transvectant of the polynomials `g` and `h` of + degrees `m` and `n`. If *lead* is true (nonzero), we instead set *res* to + the constant polynomial containing the leading coefficient of this + transvectant. + + Considering `g` and `h` as homogeneous polynomials of degree `m` + (resp. `n`) in `x_1,x_2`, this sets *res* to (the leading coefficient of) + the polynomial .. math:: @@ -1414,28 +1615,18 @@ modular forms and covariants. 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) +.. function:: void acb_theta_g2_even_weight(acb_t psi4, acb_t psi6, acb_t chi10, acb_t chi12, 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). + Sets *psi4*, *psi6*, *chi10*, and *chi12* to the value of the Eisenstein + series `\psi_4`, `\psi_6` and 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: @@ -1484,8 +1675,8 @@ modular forms and covariants. 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]_: + given values of *dth*, computed as in e.g. :func:`acb_theta_jet` with + `\mathit{ord}=1` and *all* set to true. We have by [CFG2017]_: .. math:: @@ -1493,25 +1684,21 @@ modular forms and covariants. \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. + `\tau`. We reduce `\tau` to the Siegel fundamental domain, call + :func:`acb_theta_jet_notransform`, then apply the transformation formula + for Siegel modular forms (which is simpler than the transformation formula + on derivatives of theta functions.) 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_covariants(acb_poly_struct * res, const acb_poly_t f, slong prec) +.. function:: void acb_theta_g2_covariants(acb_poly_struct * res, const acb_poly_t f, int lead, 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: + Sets *res* to the vector of 26 generators of the ring of covariants (or + their leading coefficients, when *lead* is true) evaluated at the sextic + *f* (any terms of degree `>6` are ignored). We use the following ordering: 0. `C_{1,6}=f` 1. `C_{2,0}= 60(f,f)_6` @@ -1544,20 +1731,12 @@ modular forms and covariants. = \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 + ./build/acb_theta/test/main acb_theta_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 @@ -1565,7 +1744,7 @@ matrix. .. code-block:: bash - ./build/acb_theta/test/main sp2gz_is_correct + ./build/acb_theta/test/main acb_theta_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 @@ -1574,14 +1753,14 @@ even size. .. code-block:: bash - ./build/acb_theta/test/main sp2gz_inv + ./build/acb_theta/test/main acb_theta_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 + ./build/acb_theta/test/main acb_theta_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 @@ -1589,91 +1768,93 @@ original matrix. .. code-block:: bash - ./build/acb_theta/test/main acb_siegel_cocycle + ./build/acb_theta/test/main acb_theta_siegel_cocycle + ./build/acb_theta/test/main acb_theta_siegel_transform -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`. +Checks that the chain rules hold: if `m'' = m'm` is a product of two symplectic +matrices and `\tau\in \mathcal{H}_g`, then `\gamma''\tau + \delta'' = +(\gamma'\tau' + \delta')(\gamma\tau+\delta)` where `\tau' = m\tau`, and +`m''\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 + ./build/acb_theta/test/main acb_theta_siegel_reduce -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`. +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_transform_z + ./build/acb_theta/test/main acb_theta_siegel_is_reduced -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. +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_siegel_reduce + ./build/acb_theta/test/main acb_theta_siegel_kappa -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`. +Checks that the results of :func:`acb_siegel_kappa` are compatible under matrix +multiplication, and when varying the *sqr* parameter. .. code-block:: bash - ./build/acb_theta/test/main acb_siegel_is_reduced + ./build/acb_theta/test/main acb_theta_char_dot -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. +Checks that dot products computed by :func:`acb_theta_char_dot`, and +:func:`acb_theta_char_dot_slong` agree on random input. .. code-block:: bash - ./build/acb_theta/test/main acb_theta_char_get_a + ./build/acb_theta/test/main acb_theta_char_table -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*. +Checks that the `a` part of characteristics remains invariant when calling +:func:`acb_theta_char_table` on trigonal symplectic matrices as in +:func:`sp2gz_trig`. .. code-block:: bash - ./build/acb_theta/test/main acb_theta_char_dot + ./build/acb_theta/test/main acb_theta_char_shuffle -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. +Checks that calling :func:`acb_theta_char_shuffle` on a random matrix and its +inverse yields inverse transformations. .. code-block:: bash - ./build/acb_theta/test/main acb_theta_char_is_even + ./build/acb_theta/test/main acb_theta_jet_tuples -Checks that the 10 even theta characteristics for `g=2` are 0, 1, 2, 3, 4, 6, -8, 9, 12, 15. +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`-th tuple is indeed +`i`. .. code-block:: bash - ./build/acb_theta/test/main acb_theta_char_is_goepel + ./build/acb_theta/test/main acb_theta_jet_mul -Checks that there are exactly 15 Göpel quadruples for `g=2`. +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_char_is_syzygous + ./build/acb_theta/test/main acb_theta_jet_compose -Checks that there are exactly 60 syzygous triples for `g=2`. +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_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*. +Generates a random ellipsoid *E* using :func:`acb_theta_eld_set`. 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 and +be contained in exactly one child of *E*; otherwise the norm of *pt* according +to the chosen Cholesky matrix must be at least the radius of *E*. .. code-block:: bash @@ -1681,45 +1862,50 @@ least the radius of *E*. 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. +in *E*. .. code-block:: bash - ./build/acb_theta/test/main acb_theta_naive_radius + ./build/acb_theta/test/main acb_theta_eld_distances -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. +Checks that when `y = Y \tfrac{a}{2}` for some theta characteristic `a`, the +result of :func:`acb_theta_eld_distances` on `(z,\tau)` contains zero in its +`a`-th entry. .. code-block:: bash - ./build/acb_theta/test/main acb_theta_naive_reduce + ./build/acb_theta/test/main acb_theta_sum_radius + ./build/acb_theta/test/main acb_theta_sum_jet_radius -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. +Generates a reduced matrix `\tau` in `\mathcal{H}_g` and vector `z\in +\mathbb{C}^g`, calls the tested function, constructs the associated ellipsoid +*E*, and checks that the sums of absolute values of terms of the theta series +(differentiated or not) on the border of *E* is at most the specified bound. .. code-block:: bash - ./build/acb_theta/test/main acb_theta_naive_term + ./build/acb_theta/test/main acb_theta_ctx_exp_inv + ./build/acb_theta/test/main acb_theta_ctx_sqr_inv -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. +Checks that the output of both functions, even at low precision, contains the +expected values and are never indeterminate. .. code-block:: bash - ./build/acb_theta/test/main acb_theta_naive_00 + ./build/acb_theta/test/main acb_theta_ctx_tau_dupl + ./build/acb_theta/test/main acb_theta_ctx_z_dupl + ./build/acb_theta/test/main acb_theta_ctx_z_add_real -Checks that the output of :func:`acb_theta_naive_00` overlaps the first entry of -the output of :func:`acb_theta_naive_0b`. +Checks that the result of those functions overlaps with new contexts +constructed with :func:`acb_theta_ctx_tau_set` and/or +:func:`acb_theta_ctx_z_set` at `2\tau`, `(2z,2\tau)`, and `(z+t,\tau)` +respectively. .. code-block:: bash - ./build/acb_theta/test/main acb_theta_naive_all + ./build/acb_theta/test/main acb_theta_sum -Checks that the results of :func:`acb_theta_naive_all` agree with +Checks that the results of :func:`acb_theta_sum` 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 @@ -1730,57 +1916,9 @@ with coefficients `\tau_0,\ldots, \tau_{g-1}`, then for all characteristics .. 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 + ./build/acb_theta/test/main acb_theta_sum_jet -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 +Checks that the results of :func:`acb_theta_sum_jet` 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 @@ -1792,71 +1930,14 @@ characteristics `(a,b)`, any vector `z`, and any derivation tuple 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 +Generates a random complex number *t*, sets *roots* 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. +*t*, and that the precision loss is small when *roots* does not contain zero. .. code-block:: bash @@ -1872,141 +1953,83 @@ contains the squared theta values `\theta_{0,b}^2(2z,2\tau)`. ./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. +associated vectors of distances *d0* and *d* using +:func:`acb_theta_eld_distances`, 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 the `k`-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 + ./build/acb_theta/test/main acb_theta_ql_lower_dim -Checks that :func:`acb_theta_ql_a0`, if successful, agrees with -:func:`acb_theta_ql_a0_naive` on random input. +Checks that applying :func:`acb_theta_ql_lower_dim`, computing +lower-dimensional theta values using :func:`acb_theta_sum`, then recombining +them using :func:`acb_theta_ql_recombine` agrees with a call to summation +algorithms in dimension `g`. .. code-block:: bash - ./build/acb_theta/test/main acb_theta_ql_reduce + ./build/acb_theta/test/main acb_theta_ql_setup -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`. +Calls :func:`acb_theta_ql_setup` on random input, checks that the output value +is 1, then checks that all the computed low-precision approximations of theta +values are indeed nonzero. .. code-block:: bash - ./build/acb_theta/test/main acb_theta_ql_all + ./build/acb_theta/test/main acb_theta_ql_exact -Checks that :func:`acb_theta_ql_all` agrees with :func:`acb_theta_naive_all` on -random input. +Checks that the result of :func:`acb_theta_ql_exact` agrees with +:func:`acb_theta_sum` on random input, and is not indeterminate. .. code-block:: bash - ./build/acb_theta/test/main acb_theta_jet_ql_bounds + ./build/acb_theta/test/main acb_theta_ql_local_bound 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 +calls :func:`acb_theta_ql_local_bound` 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). +precisions with :func:`acb_theta_sum`. .. code-block:: bash - ./build/acb_theta/test/main acb_theta_transform_proj + ./build/acb_theta/test/main acb_theta_ql_jet_error -Checks that applying :func:`acb_theta_transform_proj` with a random symplectic -matrix, then its inverse gives back the initial vector up to scaling. +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_ql_jet_error` on `(z,\tau)` for some +choice of derivation order. The difference between the results of +:func:`acb_theta_sum_jet` 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_all + ./build/acb_theta/test/main acb_theta_ql_jet_fd + ./build/acb_theta/test/main acb_theta_jet_notransform -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. +Checks that the output of these functions agrees with :func:`acb_theta_sum_jet` +on random input, and is not indeterminate. .. code-block:: bash - ./build/acb_theta/test/main acb_theta_jet_all + ./build/acb_theta/test/main acb_theta_reduce_z -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. +Checks that on random input, the entries of the output *r* always consist of +even integers, and the imaginary part of *new_zs* is indeed `y - Yr`. .. code-block:: bash - ./build/acb_theta/test/main acb_theta_g2_jet_naive_1 + ./build/acb_theta/test/main acb_theta_jet -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`. +Checks that the output of :func:`acb_theta_jet` and +:func:`acb_theta_jet_notransform` agree. The matrix `\tau` is chosen to be a +priori non-reduced but still reasonably close to the reduced domain. .. code-block:: bash @@ -2023,67 +2046,31 @@ 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. +:func:`acb_siegel_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 + ./build/acb_theta/test/main acb_theta_g2_even_weight + ./build/acb_theta/test/main acb_theta_g2_chi35 -Checks that the result of :func:`acb_theta_g2_chi12` is invariant when applying -:func:`acb_theta_transform_proj` on any input vector. +Checks that the computed values of those modular forms are either invariant, +multiplied by `\pm 1`, or by a power of `i` (depending on the weight modulo 4) +when applying :func:`acb_theta_char_shuffle` on any input vector. The +multiplicative factor is given by :func:`acb_siegel_kappa2`. .. 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`. +Checks that the result of :func:`acb_theta_g2_chi5` squares to `\chi_{10}` on +any input vector. .. code-block:: bash @@ -2094,106 +2081,74 @@ Checks that the product `\chi_{8,6} = \chi_{5}\chi_{3,6}`, computed using 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. +Checks that the discriminant of the computed sextic on a random matrix `\tau` +is `2^{12}\chi_{10}(\tau)`, as computed by :func:`acb_theta_g2_even_weight`, +and also that `\chi_5(\tau)^2 = \chi_{10}(\tau)`. .. 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 +:func:`acb_theta_g2_even_weight` 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. +:func:`acb_theta_g2_sextic_chi5`, 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. 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 + ./build/acb_theta/profile/p-sum g prec all_a all_b tilde skew -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`. +Profiles :func:`acb_theta_sum` for the given parameters on a random pair +`(z,\tau)` generated by :func:`acb_siegel_randtest_compact` and +:func:`acb_siegel_randtest_vec_reduced`, except that the last line and last +column of `\tau` are multiplied by the integer *skew* (which should be at least +1). -This is meant to provide information on how the choice of splitting in -:func:`acb_theta_ql_a0` should be made. +This is to observe how the performance of different steps in the summation +algorithm (computing distances, setting contexts, and summation itself) +behaves, especially as `g` grows and at low precisions. .. code-block:: bash - ./build/acb_theta/profile/p-ql_a0_steps g pstep pmax + ./build/acb_theta/profile/p-ql_setup g nb_steps all skew -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*. +Profiles :func:`acb_theta_ql_setup` for the given parameters on random pairs +`(0,\tau)` and `(z,\tau)`, generated using *skew* as above. -This is meant to provide information on the correct value to return in -:func:`acb_theta_ql_a0_nb_steps`. +This is to check that :func:`acb_theta_ql_setup` continues to succeed and to be +cheap even as *prec* or *nb_steps* grow, thus validating how precisions are +managed internally. .. code-block:: bash - ./build/acb_theta/profile/p-all g nb_steps hasz + ./build/acb_theta/profile/p-ql_exact g pmin pmax cst s exp -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). +Profiles :func:`acb_theta_ql_exact` on random pairs `(0,\tau)` and `(z,\tau)` +in dimension `g`. The input is generated by :func:`acb_siegel_randtest_compact` +and :func:`acb_siegel_randtest_vec_reduced`, except that the last *s* lines and +columns of *tau* are multiplied by `2^{\mathit{exp}}`. -This is meant to show whether the main user function is slower than naive -algorithms at low precisions. (This is currently the case.) +For growing precisions from *pmin* to *pmax* (by exponential steps), we call +:func:`acb_theta_ql_nb_steps` to get a suggested *pattern*, then measure how +:func:`acb_theta_ql_exact` performs on this pattern and neighboring +ones. Ideally, the suggested pattern should be the optimal one, indicating that +:func:`acb_theta_ql_nb_steps` gives an optimal answer for the given input. .. 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`. + ./build/acb_theta/profile/p-ql_jet_fd g prec ord all -This is meant to show whether the main user function is slower than naive -algorithms at low precisions. (This is currently the case.) +Profiles :func:`acb_theta_ql_jet_fd` and compares it with +:func:`acb_theta_sum_jet` for the given parameters on random input `(z,\tau)` +generated by :func:`acb_siegel_randtest_compact` and +:func:`acb_siegel_randtest_vec_reduced`. This is to gain information on which +algorithm :func:`acb_theta_ql_jet` should use. diff --git a/doc/source/references.rst b/doc/source/references.rst index 83ed474d26..b2d086ff30 100644 --- a/doc/source/references.rst +++ b/doc/source/references.rst @@ -107,7 +107,7 @@ 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. +.. [EK2025] \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 @@ -339,4 +339,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]_, [Bin1996]_, [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]_, [HZ2011]_, [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]_, [Zun2023b]_, [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]_, [Bin1996]_, [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]_, [EK2025]_, [Fie2007]_, [FieHof2014]_, [Fil1992]_, [GCL1992]_, [GG2003]_, [GS2003]_, [GVL1996]_, [Gas2018]_, [Gos1974]_, [GowWag2008]_, [GraMol2010]_, [HM2017]_, [HS1967]_, [HZ2004]_, [HanZim2004]_, [Har2010]_, [HZ2011]_, [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]_, [Zun2023b]_, [vHP2012]_, [vdH1995]_, [vdH2006]_ diff --git a/src/acb_theta.h b/src/acb_theta.h index b0e99f8c43..cb996ed4cd 100644 --- a/src/acb_theta.h +++ b/src/acb_theta.h @@ -12,7 +12,7 @@ #ifndef ACB_THETA_H #define ACB_THETA_H -#include "fmpz_mat.h" +#include "fmpz_types.h" #include "acb_types.h" #ifdef __cplusplus @@ -26,7 +26,7 @@ extern "C" { FLINT_FORCE_INLINE slong sp2gz_dim(const fmpz_mat_t mat) { - return fmpz_mat_nrows(mat) / 2; + return (mat->r) / 2; } void sp2gz_set_blocks(fmpz_mat_t mat, const fmpz_mat_t alpha, const fmpz_mat_t beta, @@ -37,7 +37,17 @@ 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); +FLINT_FORCE_INLINE 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); +} + void sp2gz_fundamental(fmpz_mat_t mat, slong j); int sp2gz_is_correct(const fmpz_mat_t mat); @@ -57,35 +67,60 @@ void acb_siegel_cocycle(acb_mat_t c, const fmpz_mat_t mat, const acb_mat_t tau, 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_cho_yinv(arb_mat_t cho, 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); +slong acb_siegel_kappa(acb_t sqrtdet, const fmpz_mat_t mat, const acb_mat_t tau, int sqr, slong prec); +slong acb_siegel_kappa2(const fmpz_mat_t mat); + 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_compact(acb_mat_t tau, flint_rand_t state, int exact, slong prec); void acb_siegel_randtest_vec(acb_ptr z, flint_rand_t state, slong g, slong prec); +void acb_siegel_randtest_vec_reduced(acb_ptr zs, flint_rand_t state, + slong nb, const acb_mat_t tau, int exact, 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); +FLINT_FORCE_INLINE int +acb_theta_char_bit(ulong ch, slong j, slong n) +{ + return (ch >> (n - 1 - j)) & 1; +} + 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); +FLINT_FORCE_INLINE int +acb_theta_char_is_even(ulong ab, slong g) +{ + return acb_theta_char_dot(ab >> g, ab, g) % 2 == 0; +} + +void acb_theta_char_table(ulong * ch, slong * e, const fmpz_mat_t mat, slong ab); +void acb_theta_char_shuffle(acb_ptr res, const fmpz_mat_t mat, acb_srcptr th, + int sqr, slong prec); + +/* Toolbox 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_exp_qf(acb_ptr res, acb_srcptr z, const acb_mat_t N, slong ord, slong prec); -/* Ellipsoids in naive algorithms */ +/* Ellipsoids */ struct acb_theta_eld_struct { @@ -100,160 +135,190 @@ struct acb_theta_eld_struct 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); + +FLINT_FORCE_INLINE slong +acb_theta_eld_nb_pts(const acb_theta_eld_t E) +{ + return E->nb_pts; +} + void acb_theta_eld_points(slong * pts, const acb_theta_eld_t E); + +FLINT_FORCE_INLINE slong +acb_theta_eld_box(const acb_theta_eld_t E, slong j) +{ + return E->box[j]; +} + +FLINT_FORCE_INLINE slong +acb_theta_eld_nb_border(const acb_theta_eld_t E) +{ + return E->nb_border; +} + 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_eld_distances(arb_ptr ds, acb_srcptr zs, slong nb, + const acb_mat_t tau, slong prec); -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, +/* Error bounds in summation algorithms */ + +void acb_theta_sum_radius(arf_t R2, arf_t eps, const arb_mat_t cho, slong ord, slong prec); +void acb_theta_sum_jet_radius(arf_t R2, arf_t eps, const arb_mat_t cho, arb_srcptr v, + slong ord, slong prec); +void acb_theta_sum_term(acb_t res, acb_srcptr z, const acb_mat_t tau, const slong * tup, const slong * n, slong prec); +slong acb_theta_sum_addprec(const arb_t d); -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); +/* Context structures in summation algorithms */ -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); +struct acb_theta_ctx_tau_struct +{ + slong g; + int allow_shift; + arb_mat_struct yinv; + arb_mat_struct cho; + + acb_mat_t exp_tau_div_4; + acb_mat_t exp_tau_div_2; + acb_mat_t exp_tau; + acb_mat_t exp_tau_div_4_inv; + acb_mat_t exp_tau_div_2_inv; + acb_mat_t exp_tau_inv; + + /* allow_shift only */ + acb_ptr exp_tau_a; + acb_ptr exp_tau_a_inv; + acb_ptr exp_a_tau_a_div_4; +}; -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); +typedef struct acb_theta_ctx_tau_struct acb_theta_ctx_tau_t[1]; -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); +typedef struct +{ + slong g; + acb_ptr exp_z; + acb_ptr exp_2z; + acb_ptr exp_z_inv; + acb_ptr exp_2z_inv; + arb_ptr v; + arb_struct u; + arb_struct uinv; + int is_real; +} +acb_theta_ctx_z_struct; -/* Naive algorithms for derivatives */ +typedef acb_theta_ctx_z_struct acb_theta_ctx_z_t[1]; -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_ctx_tau_init(acb_theta_ctx_tau_t ctx, int allow_shift, slong g); +void acb_theta_ctx_tau_clear(acb_theta_ctx_tau_t ctx); +void acb_theta_ctx_z_init(acb_theta_ctx_z_t ctx, slong g); +void acb_theta_ctx_z_clear(acb_theta_ctx_z_t ctx); +acb_theta_ctx_z_struct * acb_theta_ctx_z_vec_init(slong nb, slong g); +void acb_theta_ctx_z_vec_clear(acb_theta_ctx_z_struct * vec, slong nb); -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_ctx_exp_inv(acb_t exp_inv, const acb_t exp, const acb_t x, int is_real, slong prec); +void acb_theta_ctx_sqr_inv(acb_t sqr_inv, const acb_t inv, const acb_t sqr, int is_real, 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_ctx_tau_set(acb_theta_ctx_tau_t ctx, const acb_mat_t tau, slong prec); +void acb_theta_ctx_tau_dupl(acb_theta_ctx_tau_t ctx, slong prec); +int acb_theta_ctx_tau_overlaps(const acb_theta_ctx_tau_t ctx1, const acb_theta_ctx_tau_t ctx2); -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_ctx_z_set(acb_theta_ctx_z_t ctx, acb_srcptr z, const acb_theta_ctx_tau_t ctx_tau, slong prec); +void acb_theta_ctx_z_dupl(acb_theta_ctx_z_t ctx, slong prec); +void acb_theta_ctx_z_add_real(acb_theta_ctx_z_t res, const acb_theta_ctx_z_t ctx, + const acb_theta_ctx_z_t ctx_real, slong prec); +void acb_theta_ctx_z_common_v(arb_ptr v, const acb_theta_ctx_z_struct * vec, slong nb, slong prec); +int acb_theta_ctx_z_overlaps(const acb_theta_ctx_z_t ctx1, const acb_theta_ctx_z_t ctx2); -void acb_theta_jet_error_bounds(arb_ptr err, acb_srcptr z, const acb_mat_t tau, - acb_srcptr dth, slong ord, slong prec); +/* Summation algorithms */ + +typedef void (*acb_theta_sum_worker_t)(acb_ptr, acb_srcptr, acb_srcptr, const slong *, + slong, const acb_t, const slong *, slong, slong, slong, slong); -/* Quasi-linear algorithms on the reduced domain */ +void acb_theta_sum_sqr_pow(acb_ptr * sqr_pow, const acb_mat_t exp_tau, const acb_theta_eld_t E, slong prec); +void acb_theta_sum_work(acb_ptr th, slong len, 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, acb_theta_sum_worker_t worker); +void acb_theta_sum(acb_ptr th, const acb_theta_ctx_z_struct * vec, slong nb, + const acb_theta_ctx_tau_t ctx_tau, arb_srcptr distances, int all_a, + int all_b, int tilde, slong prec); +void acb_theta_sum_jet(acb_ptr th, const acb_theta_ctx_z_struct * vec, slong nb, + const acb_theta_ctx_tau_t ctx_tau, slong ord, int all_a, int all_b, slong prec); -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); +/* AGM steps */ -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(acb_ptr res, acb_srcptr a1, acb_srcptr a2, slong g, int all, 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); + arb_srcptr d0, arb_srcptr d, slong g, int all, slong prec); + +/* Quasilinear algorithms on reduced input */ + +int acb_theta_ql_nb_steps(slong * pattern, const acb_mat_t tau, int cst, slong prec); + +int acb_theta_ql_lower_dim(acb_ptr * new_zs, acb_ptr * cofactors, slong ** pts, + slong * nb, arf_t err, slong * fullprec, acb_srcptr z, const acb_mat_t tau, + arb_srcptr distances, slong s, ulong a, slong prec); +void acb_theta_ql_recombine(acb_ptr th, acb_srcptr th0, acb_srcptr cofactors, + const slong * pts, slong nb, const arf_t err, slong fullprec, + slong s, ulong a, int all, slong g, slong prec); +int acb_theta_ql_setup(acb_ptr rts, acb_ptr rts_all, acb_ptr t, slong * guard, slong * easy_steps, + acb_srcptr zs, slong nb, const acb_mat_t tau, arb_srcptr distances, + slong nb_steps, int all, slong prec); +void acb_theta_ql_exact(acb_ptr th, acb_srcptr zs, slong nb, const acb_mat_t tau, + const slong * pattern, int all, int shifted_prec, slong prec); + +void acb_theta_ql_local_bound(arb_t c, arb_t rho, acb_srcptr z, const acb_mat_t tau, slong ord); +void acb_theta_ql_jet_error(arb_ptr err, acb_srcptr z, const acb_mat_t tau, + acb_srcptr dth, slong ord, slong prec); -/* Quasi-linear algorithms for derivatives */ +void acb_theta_ql_jet_fd(acb_ptr th, acb_srcptr zs, slong nb, + const acb_mat_t tau, slong ord, int all, slong prec); +void acb_theta_ql_jet(acb_ptr th, acb_srcptr zs, slong nb, + const acb_mat_t tau, slong ord, int all, slong prec); -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); +/* Reduction and main functions */ -void acb_theta_jet_ql_all(acb_ptr dth, acb_srcptr z, const acb_mat_t tau, slong ord, slong prec); +void acb_theta_jet_notransform(acb_ptr th, acb_srcptr zs, slong nb, + const acb_mat_t tau, slong ord, ulong ab, int all, int sqr, slong prec); -/* Transformation formulas */ +int acb_theta_reduce_tau(acb_ptr new_zs, acb_mat_t new_tau, fmpz_mat_t mat, acb_mat_t N, + acb_mat_t ct, acb_ptr exps, acb_srcptr zs, slong nb, const acb_mat_t tau, slong prec); +int acb_theta_reduce_z(acb_ptr new_zs, arb_ptr rs, acb_ptr cs, acb_srcptr zs, + slong nb, const acb_mat_t tau, slong prec); -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_jet(acb_ptr th, acb_srcptr zs, slong nb, + const acb_mat_t tau, slong ord, int all, 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 */ +FLINT_FORCE_INLINE void +acb_theta_all(acb_ptr th, acb_srcptr z, const acb_mat_t tau, int sqr, slong prec) +{ + acb_theta_jet(th, z, 1, tau, 0, 1, sqr, prec); +} -#define ACB_THETA_G2_COV_NB 26 +/* Dimension 2 specifics */ -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 m, slong n, slong k, int lead, 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_even_weight(acb_t psi4, acb_t psi6, acb_t chi10, acb_t chi12, + 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); +void acb_theta_g2_sextic_chi5(acb_poly_t f, acb_t chi5, const acb_mat_t tau, slong prec); +void acb_theta_g2_covariants(acb_poly_struct * res, const acb_poly_t f, int lead, slong prec); #ifdef __cplusplus } diff --git a/src/acb_theta/agm_hadamard.c b/src/acb_theta/agm_hadamard.c deleted file mode 100644 index b343c8cab9..0000000000 --- a/src/acb_theta/agm_hadamard.c +++ /dev/null @@ -1,37 +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 3 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 index 9ba2a6b0df..8a47cfeb7b 100644 --- a/src/acb_theta/agm_mul.c +++ b/src/acb_theta/agm_mul.c @@ -12,8 +12,32 @@ #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) +static 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); + } +} + +static void +acb_theta_agm_mul_one(acb_ptr res, acb_srcptr a1, acb_srcptr a2, slong g, slong prec) { acb_ptr v; slong n = 1 << g; @@ -40,7 +64,43 @@ acb_theta_agm_mul(acb_ptr res, acb_srcptr a1, acb_srcptr a2, slong g, slong prec } acb_theta_agm_hadamard(res, v, g, prec); - _acb_vec_scalar_mul_2exp_si(res, res, n, -2 * g); + _acb_vec_scalar_mul_2exp_si(res, res, n, -g); _acb_vec_clear(v, 2 * n); } + +void +acb_theta_agm_mul(acb_ptr res, acb_srcptr a1, acb_srcptr a2, slong g, int all, slong prec) +{ + if (all) + { + 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, a2, 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_one(v, a1, v, g, prec); + for (b = 0; b < n; b++) + { + acb_set(&res[b * n + a], &v[b]); + } + } + + _acb_vec_clear(v, n); + } + else + { + acb_theta_agm_mul_one(res, a1, a2, g, prec); + } +} diff --git a/src/acb_theta/agm_mul_tight.c b/src/acb_theta/agm_mul_tight.c index 2eda1c55f2..edd498e5b5 100644 --- a/src/acb_theta/agm_mul_tight.c +++ b/src/acb_theta/agm_mul_tight.c @@ -16,13 +16,10 @@ 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); @@ -34,38 +31,34 @@ acb_theta_agm_rel_mag_err(arf_t m, arf_t eps, acb_srcptr a, arb_srcptr d, 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); + arb_set_arf(y, abs); + + acb_get_abs_ubound_arf(abs, &a[k], prec); + arf_mul(abs, abs, arb_midref(y), prec, ARF_RND_CEIL); 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); + acb_get_rad_ubound_arf(abs, &a[k], prec); + arf_mul(abs, abs, arb_midref(y), prec, ARF_RND_CEIL); 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) +void +acb_theta_agm_mul_tight(acb_ptr res, acb_srcptr a0, acb_srcptr a, + arb_srcptr d0, arb_srcptr d, slong g, int all, 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; + arf_t m0, m, eps0, eps, e; arb_t err; - slong k; + slong k, b; v0 = _acb_vec_init(n); v = _acb_vec_init(n); @@ -74,15 +67,14 @@ acb_theta_agm_mul_tight_gen(acb_ptr res, acb_srcptr a0, acb_srcptr a, 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); + acb_theta_agm_rel_mag_err(m0, eps0, a0, d0, n, lp); + acb_theta_agm_rel_mag_err(m, eps, a, d, n, lp); for (k = 0; k < n; k++) { - hprec = FLINT_MAX(hprec, prec + acb_theta_dist_addprec(&d[k])); + hprec = FLINT_MAX(hprec, prec + acb_theta_sum_addprec(&d[k])); acb_get_mid(&v0[k], &a0[k]); acb_get_mid(&v[k], &a[k]); } @@ -90,26 +82,36 @@ acb_theta_agm_mul_tight_gen(acb_ptr res, acb_srcptr a0, acb_srcptr a, /* Perform agm_mul or agm_sqr at high precision */ if (a0 == a) { - acb_theta_agm_mul(res, v0, v0, g, hprec); + acb_theta_agm_mul(res, v0, v0, g, all, hprec); } else { - acb_theta_agm_mul(res, v0, v, g, hprec); + acb_theta_agm_mul(res, v0, v, g, all, hprec); } - /* New relative error wrt distances is m0 eps + m eps0 + eps0 eps */ + /* New relative error wrt distances is 2^g (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); + arf_addmul(e, m, eps0, lp, ARF_RND_CEIL); + arf_addmul(e, eps, eps0, lp, ARF_RND_CEIL); + arf_mul_2exp_si(e, e, g); for (k = 0; k < n; k++) { arb_neg(err, &d[k]); - arb_exp(err, err, prec); + arb_exp(err, err, lp); arb_mul_arf(err, err, e, lp); - acb_add_error_arb(&res[k], err); + + if (all) + { + for (b = 0; b < n; b++) + { + acb_add_error_arb(&res[k * n + b], err); + } + } + else + { + acb_add_error_arb(&res[k], err); + } } _acb_vec_clear(v0, n); @@ -119,55 +121,5 @@ acb_theta_agm_mul_tight_gen(acb_ptr res, acb_srcptr a0, acb_srcptr a, 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 index d4a5517ec0..49bc7f6160 100644 --- a/src/acb_theta/agm_sqrt.c +++ b/src/acb_theta/agm_sqrt.c @@ -18,6 +18,12 @@ 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; + if (acb_is_zero(rt) && acb_contains_zero(a)) + { + acb_zero(res); + return; + } + acb_init(y1); acb_init(y2); diff --git a/src/acb_theta/all.c b/src/acb_theta/all.c deleted file mode 100644 index 2e3223ce70..0000000000 --- a/src/acb_theta/all.c +++ /dev/null @@ -1,95 +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 3 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_slong.c b/src/acb_theta/char_dot_slong.c index 6f55304a05..80af57bd5f 100644 --- a/src/acb_theta/char_dot_slong.c +++ b/src/acb_theta/char_dot_slong.c @@ -14,17 +14,15 @@ 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) + if (acb_theta_char_bit(a, k, g)) { - sgn += n[g - 1 - k] & 3; + sgn += n[k] & 3; } - a_shift = a_shift >> 1; } return sgn % 4; diff --git a/src/acb_theta/char_get_acb.c b/src/acb_theta/char_get_acb.c index c301063291..a47cc77596 100644 --- a/src/acb_theta/char_get_acb.c +++ b/src/acb_theta/char_get_acb.c @@ -17,10 +17,9 @@ acb_theta_char_get_acb(acb_ptr v, ulong a, slong g) { slong k; - for (k = g - 1; k >= 0; k--) + for (k = 0; k < g; k++) { - acb_set_si(&v[k], a & 1); - a = a >> 1; + acb_set_si(&v[k], acb_theta_char_bit(a, k, g)); } _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 index a221607733..eea1938107 100644 --- a/src/acb_theta/char_get_arb.c +++ b/src/acb_theta/char_get_arb.c @@ -17,10 +17,9 @@ acb_theta_char_get_arb(arb_ptr v, ulong a, slong g) { slong k; - for (k = g - 1; k >= 0; k--) + for (k = 0; k < g; k++) { - arb_set_si(&v[k], a & 1); - a = a >> 1; + arb_set_si(&v[k], acb_theta_char_bit(a, k, g)); } _arb_vec_scalar_mul_2exp_si(v, v, g, -1); } diff --git a/src/acb_theta/char_is_even.c b/src/acb_theta/char_is_even.c deleted file mode 100644 index 6beb05d41f..0000000000 --- a/src/acb_theta/char_is_even.c +++ /dev/null @@ -1,19 +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 3 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 deleted file mode 100644 index 9abdbb9fde..0000000000 --- a/src/acb_theta/char_is_goepel.c +++ /dev/null @@ -1,28 +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 3 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 deleted file mode 100644 index 0a789fc5f1..0000000000 --- a/src/acb_theta/char_is_syzygous.c +++ /dev/null @@ -1,18 +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 3 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/transform_proj.c b/src/acb_theta/char_shuffle.c similarity index 55% rename from src/acb_theta/transform_proj.c rename to src/acb_theta/char_shuffle.c index ecf59bd9b3..52da6ea145 100644 --- a/src/acb_theta/transform_proj.c +++ b/src/acb_theta/char_shuffle.c @@ -13,29 +13,32 @@ #include "acb_theta.h" void -acb_theta_transform_proj(acb_ptr res, const fmpz_mat_t mat, acb_srcptr th, int sqr, slong prec) +acb_theta_char_shuffle(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; + acb_ptr aux, units; + ulong ab; + ulong * chars; + slong * es; aux = _acb_vec_init(n2); - acb_init(c); + units = _acb_vec_init(k); + chars = flint_malloc(n2 * sizeof(ulong)); + es = flint_malloc(n2 * sizeof(slong)); + + acb_theta_char_table(chars, es, mat, -1); + _acb_vec_unit_roots(units, k, k, prec); 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_mul(&aux[ab], &units[es[ab] % k], &th[chars[ab]], prec); } _acb_vec_set(res, aux, n2); _acb_vec_clear(aux, n2); - acb_clear(c); + _acb_vec_clear(units, k); + flint_free(es); + flint_free(chars); } diff --git a/src/acb_theta/char_table.c b/src/acb_theta/char_table.c new file mode 100644 index 0000000000..7bd2aef9d1 --- /dev/null +++ b/src/acb_theta/char_table.c @@ -0,0 +1,142 @@ +/* + Copyright (C) 2024 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 3 of the License, or + (at your option) any later version. See . +*/ + +#include "fmpz.h" +#include "fmpz_mat.h" +#include "acb_theta.h" + +void +acb_theta_char_table(ulong * ch, slong * e, const fmpz_mat_t mat, slong ab) +{ + slong g = sp2gz_dim(mat); + slong n2 = 1 << (2 * g); + fmpz_mat_t a, b, c, d, cdt, abt; + fmpz_mat_t mat_tp; + fmpz_mat_t diags, alphabeta, alpha, beta; + fmpz_mat_t Cvec_1, Cvec_2, Lvec; + fmpz_mat_t coef; + fmpz_t eps, x; + slong i; + slong start = (ab < 0 ? 0 : ab); + slong end = (ab < 0 ? n2 : ab + 1); + + 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(cdt, g, g); + fmpz_mat_init(abt, g, g); + fmpz_mat_init(mat_tp, 2 * g, 2 * g); + fmpz_mat_init(diags, 2 * g, 1); + 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); + + /* Set diags */ + fmpz_mat_transpose(mat_tp, mat); + fmpz_mat_transpose(cdt, d); + fmpz_mat_mul(cdt, c, cdt); + fmpz_mat_transpose(abt, b); + fmpz_mat_mul(abt, a, abt); + for (i = 0; i < g; i++) + { + fmpz_neg(fmpz_mat_entry(diags, i, 0), fmpz_mat_entry(cdt, i, i)); + fmpz_neg(fmpz_mat_entry(diags, g + i, 0), fmpz_mat_entry(abt, i, i)); + } + + for (ab = start; ab < end; ab++) + { + ch[ab - start] = 0; + + /* Turn ab into a 2g x 1 fmpz matrix, set alphabeta = diags + ab */ + for (i = 0; i < 2 * g; i++) + { + fmpz_add_si(fmpz_mat_entry(alphabeta, i, 0), + fmpz_mat_entry(diags, i, 0), acb_theta_char_bit(ab, i, 2 * g)); + } + + /* 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)); + + for (i = 0; i < g; i++) + { + fmpz_set(fmpz_mat_entry(Lvec, 0, i), fmpz_mat_entry(abt, 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++) + { + ch[ab - start] = ch[ab - start] << 1; + ch[ab - start] += 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[ab - start] = 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(cdt); + fmpz_mat_clear(abt); + fmpz_mat_clear(mat_tp); + fmpz_mat_clear(diags); + 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); +} diff --git a/src/acb_theta/ctx_exp_inv.c b/src/acb_theta/ctx_exp_inv.c new file mode 100644 index 0000000000..6dd9c14451 --- /dev/null +++ b/src/acb_theta/ctx_exp_inv.c @@ -0,0 +1,35 @@ +/* + Copyright (C) 2024 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 3 of the License, or + (at your option) any later version. See . +*/ + +#include "acb.h" +#include "acb_theta.h" + +void acb_theta_ctx_exp_inv(acb_t exp_inv, const acb_t exp, const acb_t z, + int z_is_real, slong prec) +{ + if (z_is_real) + { + acb_conj(exp_inv, exp); + } + else if (acb_contains_zero(exp) + || acb_rel_error_bits(exp) <= 0.75 * prec) + { + acb_t x; + acb_init(x); + acb_neg(x, z); + acb_exp_pi_i(exp_inv, x, prec); + acb_clear(x); + } + else + { + acb_inv(exp_inv, exp, prec); + } +} diff --git a/src/acb_theta/g2_sextic.c b/src/acb_theta/ctx_sqr_inv.c similarity index 59% rename from src/acb_theta/g2_sextic.c rename to src/acb_theta/ctx_sqr_inv.c index 02bc9f0b9a..024387f331 100644 --- a/src/acb_theta/g2_sextic.c +++ b/src/acb_theta/ctx_sqr_inv.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2023 Jean Kieffer + Copyright (C) 2024 Jean Kieffer This file is part of FLINT. @@ -12,12 +12,15 @@ #include "acb.h" #include "acb_theta.h" -void -acb_theta_g2_sextic(acb_poly_t res, const acb_mat_t tau, slong prec) +void acb_theta_ctx_sqr_inv(acb_t sqr_inv, const acb_t inv, const acb_t sqr, + int z_is_real, slong prec) { - acb_t chi5; - - acb_init(chi5); - acb_theta_g2_sextic_chi5(res, chi5, tau, prec); - acb_clear(chi5); + if (z_is_real) + { + acb_conj(sqr_inv, sqr); + } + else + { + acb_sqr(sqr_inv, inv, prec); + } } diff --git a/src/acb_theta/ctx_tau_clear.c b/src/acb_theta/ctx_tau_clear.c new file mode 100644 index 0000000000..a976c67aff --- /dev/null +++ b/src/acb_theta/ctx_tau_clear.c @@ -0,0 +1,39 @@ +/* + Copyright (C) 2024 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 3 of the License, or + (at your option) any later version. See . +*/ + +#include "arb.h" +#include "acb.h" +#include "arb_mat.h" +#include "acb_mat.h" +#include "acb_theta.h" + +void +acb_theta_ctx_tau_clear(acb_theta_ctx_tau_t ctx) +{ + slong g = ctx->g; + slong n = 1 << g; + + arb_mat_clear(&ctx->yinv); + arb_mat_clear(&ctx->cho); + acb_mat_clear(ctx->exp_tau_div_4); + acb_mat_clear(ctx->exp_tau_div_2); + acb_mat_clear(ctx->exp_tau); + acb_mat_clear(ctx->exp_tau_div_4_inv); + acb_mat_clear(ctx->exp_tau_div_2_inv); + acb_mat_clear(ctx->exp_tau_inv); + + if (ctx->allow_shift) + { + _acb_vec_clear(ctx->exp_tau_a, n * g); + _acb_vec_clear(ctx->exp_tau_a_inv, n * g); + _acb_vec_clear(ctx->exp_a_tau_a_div_4, n); + } +} diff --git a/src/acb_theta/ctx_tau_dupl.c b/src/acb_theta/ctx_tau_dupl.c new file mode 100644 index 0000000000..2c4d14f80d --- /dev/null +++ b/src/acb_theta/ctx_tau_dupl.c @@ -0,0 +1,68 @@ +/* + Copyright (C) 2024 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 3 of the License, or + (at your option) any later version. See . +*/ + +#include "arb.h" +#include "acb.h" +#include "arb_mat.h" +#include "acb_mat.h" +#include "acb_theta.h" + +void +acb_theta_ctx_tau_dupl(acb_theta_ctx_tau_t ctx, slong prec) +{ + slong g = ctx->g; + slong n = 1 << g; + slong j, k; + arb_t sqrt2; + + arb_init(sqrt2); + arb_set_si(sqrt2, 2); + arb_sqrt(sqrt2, sqrt2, prec); + + /* Update cho, yinv */ + arb_mat_scalar_mul_arb(&ctx->cho, &ctx->cho, sqrt2, prec); + arb_mat_scalar_mul_2exp_si(&ctx->yinv, &ctx->yinv, -1); + + /* Swap matrices around */ + FLINT_SWAP(acb_mat_struct, *ctx->exp_tau_div_4, *ctx->exp_tau_div_2); + FLINT_SWAP(acb_mat_struct, *ctx->exp_tau_div_2, *ctx->exp_tau); + FLINT_SWAP(acb_mat_struct, *ctx->exp_tau_div_4_inv, *ctx->exp_tau_div_2_inv); + FLINT_SWAP(acb_mat_struct, *ctx->exp_tau_div_2_inv, *ctx->exp_tau_inv); + + /* Update exp_tau and exp_tau_inv */ + for (j = 0; j < g; j++) + { + for (k = j; k < g; k++) + { + acb_sqr(acb_mat_entry(ctx->exp_tau, j, k), + acb_mat_entry(ctx->exp_tau_div_2, j, k), prec); + acb_sqr(acb_mat_entry(ctx->exp_tau_inv, j, k), + acb_mat_entry(ctx->exp_tau_div_2_inv, j, k), prec); + } + } + + if (ctx->allow_shift) + { + /* Update exp_tau_a */ + for (j = 0; j < n * g; j++) + { + acb_sqr(&ctx->exp_tau_a[j], &ctx->exp_tau_a[j], prec); + acb_sqr(&ctx->exp_tau_a_inv[j], &ctx->exp_tau_a_inv[j], prec); + } + /* Update exp_a_tau_a_div_4 */ + for (j = 0; j < n; j++) + { + acb_sqr(&ctx->exp_a_tau_a_div_4[j], &ctx->exp_a_tau_a_div_4[j], prec); + } + } + + arb_clear(sqrt2); +} diff --git a/src/acb_theta/ctx_tau_init.c b/src/acb_theta/ctx_tau_init.c new file mode 100644 index 0000000000..7e0d7d794a --- /dev/null +++ b/src/acb_theta/ctx_tau_init.c @@ -0,0 +1,42 @@ +/* + Copyright (C) 2024 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 3 of the License, or + (at your option) any later version. See . +*/ + +#include "arb.h" +#include "acb.h" +#include "arb_mat.h" +#include "acb_mat.h" +#include "acb_theta.h" + +void +acb_theta_ctx_tau_init(acb_theta_ctx_tau_t ctx, int allow_shift, slong g) +{ + slong n = 1 << g; + FLINT_ASSERT(g >= 1); + + ctx->g = g; + ctx->allow_shift = allow_shift; + arb_mat_init(&ctx->yinv, g, g); + arb_mat_init(&ctx->cho, g, g); + + acb_mat_init(ctx->exp_tau_div_4, g, g); + acb_mat_init(ctx->exp_tau_div_2, g, g); + acb_mat_init(ctx->exp_tau, g, g); + acb_mat_init(ctx->exp_tau_div_4_inv, g, g); + acb_mat_init(ctx->exp_tau_div_2_inv, g, g); + acb_mat_init(ctx->exp_tau_inv, g, g); + + if (allow_shift) + { + ctx->exp_tau_a = _acb_vec_init(g * n); + ctx->exp_tau_a_inv = _acb_vec_init(g * n); + ctx->exp_a_tau_a_div_4 = _acb_vec_init(n); + } +} diff --git a/src/acb_theta/ctx_tau_overlaps.c b/src/acb_theta/ctx_tau_overlaps.c new file mode 100644 index 0000000000..5b0f451433 --- /dev/null +++ b/src/acb_theta/ctx_tau_overlaps.c @@ -0,0 +1,44 @@ +/* + Copyright (C) 2025 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 3 of the License, or + (at your option) any later version. See . +*/ + +#include "acb.h" +#include "arb_mat.h" +#include "acb_mat.h" +#include "acb_theta.h" + +int +acb_theta_ctx_tau_overlaps(const acb_theta_ctx_tau_t ctx1, const acb_theta_ctx_tau_t ctx2) +{ + slong g = ctx2->g; + slong n = 1 << g; + int res; + + FLINT_ASSERT(ctx1->g == g); + FLINT_ASSERT(ctx1->allow_shift == ctx2->allow_shift); + + res = arb_mat_overlaps(&ctx1->yinv, &ctx2->yinv) + && arb_mat_overlaps(&ctx1->cho, &ctx2->cho) + && acb_mat_overlaps(ctx1->exp_tau_div_4, ctx2->exp_tau_div_4) + && acb_mat_overlaps(ctx1->exp_tau_div_2, ctx2->exp_tau_div_2) + && acb_mat_overlaps(ctx1->exp_tau, ctx2->exp_tau) + && acb_mat_overlaps(ctx1->exp_tau_div_4_inv, ctx2->exp_tau_div_4_inv) + && acb_mat_overlaps(ctx1->exp_tau_div_2_inv, ctx2->exp_tau_div_2_inv) + && acb_mat_overlaps(ctx1->exp_tau_inv, ctx2->exp_tau_inv); + + if (ctx1->allow_shift && res) + { + res = _acb_vec_overlaps(ctx1->exp_tau_a, ctx2->exp_tau_a, n * g) + && _acb_vec_overlaps(ctx1->exp_tau_a_inv, ctx2->exp_tau_a_inv, n * g) + && _acb_vec_overlaps(ctx1->exp_a_tau_a_div_4, ctx2->exp_a_tau_a_div_4, n); + } + + return res; +} diff --git a/src/acb_theta/ctx_tau_set.c b/src/acb_theta/ctx_tau_set.c new file mode 100644 index 0000000000..b962998e32 --- /dev/null +++ b/src/acb_theta/ctx_tau_set.c @@ -0,0 +1,148 @@ +/* + Copyright (C) 2024 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 3 of the License, or + (at your option) any later version. See . +*/ + +#include "acb.h" +#include "arb_mat.h" +#include "acb_mat.h" +#include "acb_theta.h" + +static void +acb_theta_ctx_tau_set_shift(acb_theta_ctx_tau_t ctx, slong a, slong prec) +{ + slong g = ctx->g; + acb_t x; + slong j, k; + + acb_init(x); + + for (j = 0; j < g; j++) + { + /* Set exp_tau_a */ + acb_one(x); + for (k = 0; k < g; k++) + { + if (!acb_theta_char_bit(a, k, g)) + { + continue; + } + if (k < j) + { + acb_mul(x, x, acb_mat_entry(ctx->exp_tau_div_2, k, j), prec); + } + else if (k == j) + { + acb_mul(x, x, acb_mat_entry(ctx->exp_tau, k, k), prec); + } + else + { + acb_mul(x, x, acb_mat_entry(ctx->exp_tau_div_2, j, k), prec); + } + } + acb_set(&ctx->exp_tau_a[a * g + j], x); + + /* Set exp_tau_a_inv; recompute products to avoid inversions */ + acb_one(x); + for (k = 0; k < g; k++) + { + if (!acb_theta_char_bit(a, k, g)) + { + continue; + } + if (k < j) + { + acb_mul(x, x, acb_mat_entry(ctx->exp_tau_div_2_inv, k, j), prec); + } + else if (k == j) + { + acb_mul(x, x, acb_mat_entry(ctx->exp_tau_inv, k, k), prec); + } + else + { + acb_mul(x, x, acb_mat_entry(ctx->exp_tau_div_2_inv, j, k), prec); + } + } + acb_set(&ctx->exp_tau_a_inv[a * g + j], x); + } + /* Set exp_a_tau_a_div_4 */ + acb_one(x); + for (j = 0; j < g; j++) + { + if (!acb_theta_char_bit(a, j, g)) + { + continue; + } + for (k = j; k < g; k++) + { + if (!acb_theta_char_bit(a, k, g)) + { + continue; + } + acb_mul(x, x, acb_mat_entry(ctx->exp_tau_div_4, j, k), prec); + } + } + acb_set(&ctx->exp_a_tau_a_div_4[a], x); + + acb_clear(x); +} + +void +acb_theta_ctx_tau_set(acb_theta_ctx_tau_t ctx, const acb_mat_t tau, slong prec) +{ + slong g = ctx->g; + slong n = 1 << g; + acb_t x; + slong j, k, a; + int b; + + FLINT_ASSERT(g == acb_mat_nrows(tau)); + acb_init(x); + + /* Set exp_tau and inverses */ + for (j = 0; j < g; j++) + { + for (k = j; k < g; k++) + { + acb_set_round(x, acb_mat_entry(tau, j, k), prec); + acb_mul_2exp_si(x, x, (k == j ? -2 : -1)); + acb_exp_pi_i(acb_mat_entry(ctx->exp_tau_div_4, j, k), x, prec); + + acb_sqr(acb_mat_entry(ctx->exp_tau_div_2, j, k), + acb_mat_entry(ctx->exp_tau_div_4, j, k), prec); + acb_sqr(acb_mat_entry(ctx->exp_tau, j, k), + acb_mat_entry(ctx->exp_tau_div_2, j, k), prec); + + /* Diagonal entries of exp_tau_inv are needed to set exp_tau_a_inv */ + b = acb_is_real(acb_mat_entry(tau, j, k)); + acb_theta_ctx_exp_inv(acb_mat_entry(ctx->exp_tau_div_4_inv, j, k), + acb_mat_entry(ctx->exp_tau_div_4, j, k), x, b, prec); + acb_theta_ctx_sqr_inv(acb_mat_entry(ctx->exp_tau_div_2_inv, j, k), + acb_mat_entry(ctx->exp_tau_div_4_inv, j, k), + acb_mat_entry(ctx->exp_tau_div_2, j, k), b, prec); + acb_theta_ctx_sqr_inv(acb_mat_entry(ctx->exp_tau_inv, j, k), + acb_mat_entry(ctx->exp_tau_div_2_inv, j, k), + acb_mat_entry(ctx->exp_tau, j, k), b, prec); + } + } + + /* Set cho, yinv */ + acb_siegel_cho_yinv(&ctx->cho, &ctx->yinv, tau, prec); + + /* Set exponentials for shifts */ + if (ctx->allow_shift) + { + for (a = 0; a < n; a++) + { + acb_theta_ctx_tau_set_shift(ctx, a, prec); + } + } + + acb_clear(x); +} diff --git a/src/acb_theta/ctx_z_add_real.c b/src/acb_theta/ctx_z_add_real.c new file mode 100644 index 0000000000..683e89df8d --- /dev/null +++ b/src/acb_theta/ctx_z_add_real.c @@ -0,0 +1,40 @@ +/* + Copyright (C) 2024 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 3 of the License, or + (at your option) any later version. See . +*/ + +#include "acb.h" +#include "acb_theta.h" + +void +acb_theta_ctx_z_add_real(acb_theta_ctx_z_t res, const acb_theta_ctx_z_t ctx, + const acb_theta_ctx_z_t ctx_real, slong prec) +{ + slong g = ctx->g; + slong j; + + FLINT_ASSERT(ctx_real->g == g); + FLINT_ASSERT(res->g == g); + + /* Copy things */ + arb_set(&res->u, &ctx->u); + arb_set(&res->uinv, &ctx->uinv); + res->is_real = ctx->is_real; + _arb_vec_set(res->v, ctx->v, g); + + /* Exponentials */ + for (j = 0; j < g; j++) + { + acb_mul(&res->exp_z[j], &ctx->exp_z[j], &ctx_real->exp_z[j], prec); + acb_mul(&res->exp_z_inv[j], &ctx->exp_z_inv[j], &ctx_real->exp_z_inv[j], prec); + acb_sqr(&res->exp_2z[j], &res->exp_z[j], prec); + acb_theta_ctx_sqr_inv(&res->exp_2z_inv[j], &res->exp_z_inv[j], + &res->exp_2z[j], ctx->is_real, prec); + } +} diff --git a/src/acb_theta/char_dot_acb.c b/src/acb_theta/ctx_z_clear.c similarity index 52% rename from src/acb_theta/char_dot_acb.c rename to src/acb_theta/ctx_z_clear.c index 91361ab7e8..7440762853 100644 --- a/src/acb_theta/char_dot_acb.c +++ b/src/acb_theta/ctx_z_clear.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2023 Jean Kieffer + Copyright (C) 2024 Jean Kieffer This file is part of FLINT. @@ -9,19 +9,20 @@ (at your option) any later version. See . */ +#include "arb.h" #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) +acb_theta_ctx_z_clear(acb_theta_ctx_z_t ctx) { - slong * v; + slong g = ctx->g; - 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); + _acb_vec_clear(ctx->exp_z, g); + _acb_vec_clear(ctx->exp_2z, g); + _acb_vec_clear(ctx->exp_z_inv, g); + _acb_vec_clear(ctx->exp_2z_inv, g); + _arb_vec_clear(ctx->v, g); + arb_clear(&ctx->u); + arb_clear(&ctx->uinv); } diff --git a/src/acb_theta/ctx_z_common_v.c b/src/acb_theta/ctx_z_common_v.c new file mode 100644 index 0000000000..292389746c --- /dev/null +++ b/src/acb_theta/ctx_z_common_v.c @@ -0,0 +1,45 @@ +/* + Copyright (C) 2024 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 3 of the License, or + (at your option) any later version. See . +*/ + +#include "acb.h" +#include "acb_mat.h" +#include "acb_modular.h" +#include "acb_theta.h" + +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); + } +} + +void +acb_theta_ctx_z_common_v(arb_ptr v, const acb_theta_ctx_z_struct * vec, slong nb, slong prec) +{ + slong g; + slong j; + + FLINT_ASSERT(nb >= 0); + + if (nb == 0) return; + + g = vec->g; + _arb_vec_set(v, vec->v, g); + for (j = 1; j < nb; j++) + { + _arb_vec_union(v, v, (vec + j)->v, g, prec); + } +} + diff --git a/src/acb_theta/ctx_z_dupl.c b/src/acb_theta/ctx_z_dupl.c new file mode 100644 index 0000000000..8bec69dcf9 --- /dev/null +++ b/src/acb_theta/ctx_z_dupl.c @@ -0,0 +1,48 @@ +/* + Copyright (C) 2024 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 3 of the License, or + (at your option) any later version. See . +*/ + +#include "arb.h" +#include "acb.h" +#include "acb_theta.h" + +void +acb_theta_ctx_z_dupl(acb_theta_ctx_z_t ctx, slong prec) +{ + slong g = ctx->g; + acb_ptr temp; + arb_t sqrt2; + slong j; + + arb_init(sqrt2); + arb_set_si(sqrt2, 2); + arb_sqrt(sqrt2, sqrt2, prec); + + /* Swap vectors around */ + temp = ctx->exp_z; + ctx->exp_z = ctx->exp_2z; + ctx->exp_2z = temp; + temp = ctx->exp_z_inv; + ctx->exp_z_inv = ctx->exp_2z_inv; + ctx->exp_2z_inv = temp; + for (j = 0; j < g; j++) + { + acb_sqr(&ctx->exp_2z[j], &ctx->exp_z[j], prec); + acb_theta_ctx_sqr_inv(&ctx->exp_2z_inv[j], &ctx->exp_z_inv[j], + &ctx->exp_2z[j], ctx->is_real, prec); + } + + /* Compute other quantities */ + _arb_vec_scalar_mul(ctx->v, ctx->v, g, sqrt2, prec); + arb_sqr(&ctx->u, &ctx->u, prec); + arb_sqr(&ctx->uinv, &ctx->uinv, prec); + + arb_clear(sqrt2); +} diff --git a/src/acb_theta/ctx_z_init.c b/src/acb_theta/ctx_z_init.c new file mode 100644 index 0000000000..9501cc9088 --- /dev/null +++ b/src/acb_theta/ctx_z_init.c @@ -0,0 +1,29 @@ +/* + Copyright (C) 2024 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 3 of the License, or + (at your option) any later version. See . +*/ + +#include "arb.h" +#include "acb.h" +#include "acb_theta.h" + +void +acb_theta_ctx_z_init(acb_theta_ctx_z_t ctx, slong g) +{ + FLINT_ASSERT(g >= 1); + + ctx->g = g; + ctx->exp_z = _acb_vec_init(g); + ctx->exp_2z = _acb_vec_init(g); + ctx->exp_z_inv = _acb_vec_init(g); + ctx->exp_2z_inv = _acb_vec_init(g); + ctx->v = _arb_vec_init(g); + arb_init(&ctx->u); + arb_init(&ctx->uinv); +} diff --git a/src/acb_theta/ctx_z_overlaps.c b/src/acb_theta/ctx_z_overlaps.c new file mode 100644 index 0000000000..25a8b16d84 --- /dev/null +++ b/src/acb_theta/ctx_z_overlaps.c @@ -0,0 +1,35 @@ +/* + Copyright (C) 2024 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 3 of the License, or + (at your option) any later version. See . +*/ + +#include "acb.h" +#include "acb_theta.h" + +int acb_theta_ctx_z_overlaps(const acb_theta_ctx_z_t ctx1, const acb_theta_ctx_z_t ctx2) +{ + slong g = ctx1->g; + int res; + + if (ctx2->g != g) + { + return 0; + } + + res = _acb_vec_overlaps(ctx1->exp_z, ctx2->exp_z, g) + && _acb_vec_overlaps(ctx1->exp_2z, ctx2->exp_2z, g) + && _acb_vec_overlaps(ctx1->exp_z_inv, ctx2->exp_z_inv, g) + && _acb_vec_overlaps(ctx1->exp_2z_inv, ctx2->exp_2z_inv, g) + && (ctx1->is_real == ctx2->is_real) + && _arb_vec_overlaps(ctx1->v, ctx2->v, g) + && arb_overlaps(&ctx1->u, &ctx2->u) + && arb_overlaps(&ctx1->uinv, &ctx2->uinv); + + return res; +} diff --git a/src/acb_theta/ctx_z_set.c b/src/acb_theta/ctx_z_set.c new file mode 100644 index 0000000000..a05e73fe01 --- /dev/null +++ b/src/acb_theta/ctx_z_set.c @@ -0,0 +1,62 @@ +/* + Copyright (C) 2024 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 3 of the License, or + (at your option) any later version. See . +*/ + +#include "arb.h" +#include "acb.h" +#include "arb_mat.h" +#include "acb_mat.h" +#include "acb_theta.h" + +void +acb_theta_ctx_z_set(acb_theta_ctx_z_t ctx, acb_srcptr z, const acb_theta_ctx_tau_t ctx_tau, slong prec) +{ + slong g = ctx_tau->g; + arb_t u; + arb_ptr y, t; + acb_ptr s; + slong k; + int is_real; + + arb_init(u); + y = _arb_vec_init(g); + t = _arb_vec_init(g); + s = _acb_vec_init(g); + + /* Set t = Yinv y */ + _acb_vec_get_imag(y, z, g); + arb_mat_vector_mul_col(t, &ctx_tau->yinv, y, prec); + + /* u is exp(pi y^T Yinv y) and v is cho * t */ + arb_dot(u, NULL, 0, y, 1, t, 1, g, prec); + arb_const_pi(&ctx->u, prec); + arb_mul(&ctx->u, &ctx->u, u, prec); + arb_exp(&ctx->u, &ctx->u, prec); + arb_inv(&ctx->uinv, &ctx->u, prec); + arb_mat_vector_mul_col(ctx->v, &ctx_tau->cho, t, prec); + + /* Set z_is_real, exp_z, exp_z_inv, exp_2z, exp_2z_inv */ + for (k = 0; k < g; k++) + { + acb_exp_pi_i(&ctx->exp_z[k], &z[k], prec); + is_real = acb_is_real(&z[k]); + acb_sqr(&ctx->exp_2z[k], &ctx->exp_z[k], prec); + acb_theta_ctx_exp_inv(&ctx->exp_z_inv[k], &ctx->exp_z[k], + &z[k], is_real, prec); + acb_theta_ctx_sqr_inv(&ctx->exp_2z_inv[k], &ctx->exp_z_inv[k], + &ctx->exp_2z[k], is_real, prec); + } + ctx->is_real = _acb_vec_is_real(z, g); + + arb_clear(u); + _arb_vec_clear(y, g); + _arb_vec_clear(t, g); + _acb_vec_clear(s, g); +} diff --git a/src/acb_theta/char_get_slong.c b/src/acb_theta/ctx_z_vec_clear.c similarity index 61% rename from src/acb_theta/char_get_slong.c rename to src/acb_theta/ctx_z_vec_clear.c index 411fe21e0e..7fa54c20ea 100644 --- a/src/acb_theta/char_get_slong.c +++ b/src/acb_theta/ctx_z_vec_clear.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2023 Jean Kieffer + Copyright (C) 2024 Jean Kieffer This file is part of FLINT. @@ -9,16 +9,19 @@ (at your option) any later version. See . */ +#include "arb.h" +#include "acb.h" #include "acb_theta.h" void -acb_theta_char_get_slong(slong * n, ulong a, slong g) +acb_theta_ctx_z_vec_clear(acb_theta_ctx_z_struct * vec, slong nb) { slong k; - for (k = g - 1; k >= 0; k--) + FLINT_ASSERT(nb >= 0); + for (k = 0; k < nb; k++) { - n[k] = a & 1; - a = a >> 1; + acb_theta_ctx_z_clear(&vec[k]); } + flint_free(vec); } diff --git a/src/acb_theta/char_get_a.c b/src/acb_theta/ctx_z_vec_init.c similarity index 51% rename from src/acb_theta/char_get_a.c rename to src/acb_theta/ctx_z_vec_init.c index f13bdcb015..247682adb7 100644 --- a/src/acb_theta/char_get_a.c +++ b/src/acb_theta/ctx_z_vec_init.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2023 Jean Kieffer + Copyright (C) 2024 Jean Kieffer This file is part of FLINT. @@ -9,19 +9,24 @@ (at your option) any later version. See . */ +#include "arb.h" +#include "acb.h" #include "acb_theta.h" -ulong -acb_theta_char_get_a(const slong * n, slong g) +acb_theta_ctx_z_struct * +acb_theta_ctx_z_vec_init(slong nb, slong g) { + acb_theta_ctx_z_struct * res; slong k; - ulong a = 0; - for (k = 0; k < g; k++) + FLINT_ASSERT(nb >= 0); + FLINT_ASSERT(g >= 1); + + res = flint_malloc(nb * sizeof(acb_theta_ctx_z_struct)); + for (k = 0; k < nb; k++) { - a *= 2; - a += ((n[k] % 2) + 2) % 2; + acb_theta_ctx_z_init(&res[k], g); } - return a; + return res; } diff --git a/src/acb_theta/dist_a0.c b/src/acb_theta/dist_a0.c deleted file mode 100644 index 43bf9e8c64..0000000000 --- a/src/acb_theta/dist_a0.c +++ /dev/null @@ -1,50 +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 3 of the License, or - (at your option) any later version. See . -*/ - -#include "arb.h" -#include "arb_mat.h" -#include "acb.h" -#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_pt.c b/src/acb_theta/dist_pt.c deleted file mode 100644 index 91f6d5c652..0000000000 --- a/src/acb_theta/dist_pt.c +++ /dev/null @@ -1,34 +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 3 of the License, or - (at your option) any later version. See . -*/ - -#include "arb.h" -#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 index 5125f3e056..00c2169010 100644 --- a/src/acb_theta/eld_border.c +++ b/src/acb_theta/eld_border.c @@ -14,36 +14,32 @@ 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 d = E->dim; + slong g = E->ambient_dim; slong k, i; if (d == 1) { - pts[0] = min - 1; - pts[g] = max + 1; + pts[0] = (E->min) - 1; + pts[g] = (E->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); + pts[k] = E->last_coords[k - d]; + pts[k + g] = E->last_coords[k - d]; } } else /* d > 1 */ { i = 0; - for (k = 0; k < nr; k++) + for (k = 0; k < (E->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)); + acb_theta_eld_border(&pts[i], &E->rchildren[k]); + i += g * acb_theta_eld_nb_border(&E->rchildren[k]); } - for (k = 0; k < nl; k++) + for (k = 0; k < (E->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)); + acb_theta_eld_border(&pts[i], &E->lchildren[k]); + i += g * acb_theta_eld_nb_border(&E->lchildren[k]); } } } diff --git a/src/acb_theta/eld_clear.c b/src/acb_theta/eld_clear.c index 268ae74972..b48e53d977 100644 --- a/src/acb_theta/eld_clear.c +++ b/src/acb_theta/eld_clear.c @@ -15,22 +15,20 @@ 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) + if ((E->nr) > 0) { - for (k = 0; k < nr; k++) + for (k = 0; k < (E->nr); k++) { - acb_theta_eld_clear(acb_theta_eld_rchild(E, k)); + acb_theta_eld_clear(&E->rchildren[k]); } flint_free(E->rchildren); } - if (nl > 0) + if ((E->nl) > 0) { - for (k = 0; k < nl; k++) + for (k = 0; k < (E->nl); k++) { - acb_theta_eld_clear(acb_theta_eld_lchild(E, k)); + acb_theta_eld_clear(&E->lchildren[k]); } flint_free(E->lchildren); } diff --git a/src/acb_theta/eld_contains.c b/src/acb_theta/eld_contains.c index f5d94e8b9e..71645fb0fd 100644 --- a/src/acb_theta/eld_contains.c +++ b/src/acb_theta/eld_contains.c @@ -14,12 +14,11 @@ static int acb_theta_eld_contains_rec(const acb_theta_eld_t E, const slong * pt) { - slong d = acb_theta_eld_dim(E); + slong d = E->dim; slong c = pt[d - 1]; slong k; - if (c < acb_theta_eld_min(E) - || c > acb_theta_eld_max(E)) + if (c < (E->min) || c > (E)->max) { return 0; } @@ -27,23 +26,23 @@ acb_theta_eld_contains_rec(const acb_theta_eld_t E, const slong * pt) { return 1; } - else if (c >= acb_theta_eld_mid(E)) + else if (c >= (E->mid)) { - k = c - acb_theta_eld_mid(E); - return acb_theta_eld_contains_rec(acb_theta_eld_rchild(E, k), pt); + k = c - (E->mid); + return acb_theta_eld_contains_rec(&E->rchildren[k], pt); } else { - k = acb_theta_eld_mid(E) - 1 - c; - return acb_theta_eld_contains_rec(acb_theta_eld_lchild(E, k), pt); + k = (E->mid) - 1 - c; + return acb_theta_eld_contains_rec(&E->lchildren[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 g = E->ambient_dim; + slong d = E->dim; slong k; if (acb_theta_eld_nb_pts(E) == 0) @@ -53,7 +52,7 @@ acb_theta_eld_contains(const acb_theta_eld_t E, const slong * pt) for (k = d; k < g; k++) { - if (pt[k] != acb_theta_eld_coord(E, k)) + if (pt[k] != E->last_coords[k - d]) { return 0; } diff --git a/src/acb_theta/dist_lat.c b/src/acb_theta/eld_distances.c similarity index 51% rename from src/acb_theta/dist_lat.c rename to src/acb_theta/eld_distances.c index 9b637f742c..3f1381f485 100644 --- a/src/acb_theta/dist_lat.c +++ b/src/acb_theta/eld_distances.c @@ -11,13 +11,15 @@ #include "arb.h" #include "arb_mat.h" +#include "acb.h" #include "acb_mat.h" #include "acb_theta.h" +/* Should not be used in tests */ static void -acb_theta_dist_unif(arb_t d, const arb_mat_t C, slong prec) +acb_theta_dist_unif(arb_t d, const arb_mat_t cho, slong prec) { - slong g = arb_mat_nrows(C); + slong g = arb_mat_nrows(cho); arb_ptr v; slong k; @@ -28,19 +30,38 @@ acb_theta_dist_unif(arb_t d, const arb_mat_t C, slong prec) 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_mat_vector_mul_col(v, cho, 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) +acb_theta_dist_pt(arb_t d, arb_srcptr v, const arb_mat_t cho, const slong * n, slong prec) +{ + slong g = arb_mat_nrows(cho); + 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, cho, 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); +} + +static void +acb_theta_dist_ubound(arf_t u, arb_srcptr v, const arb_mat_t cho, slong prec) { - slong g = acb_mat_nrows(C); + slong g = acb_mat_nrows(cho); slong nb = 1 << g; - arb_mat_t Cinv; + arb_mat_t choinv; arb_ptr x; slong * approx; slong * pt; @@ -49,16 +70,16 @@ acb_theta_dist_ubound(arf_t u, arb_srcptr v, const arb_mat_t C, slong prec) slong j, k; int r = 1; - arb_mat_init(Cinv, g, g); + arb_mat_init(choinv, 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); + arb_mat_one(choinv); + arb_mat_solve_triu(choinv, cho, choinv, 0, prec); + arb_mat_vector_mul_col(x, choinv, v, prec); r = _arb_vec_is_finite(x, g); for (k = 0; (k < g) && r; k++) @@ -80,13 +101,13 @@ acb_theta_dist_ubound(arf_t u, arb_srcptr v, const arb_mat_t C, slong prec) { pt[j] = approx[2 * j + (k & (1 << j) ? 0 : 1)]; } - acb_theta_dist_pt(d, v, C, pt, prec); + acb_theta_dist_pt(d, v, cho, pt, prec); arb_get_ubound_arf(b, d, prec); arf_min(u, u, b); } } - arb_mat_clear(Cinv); + arb_mat_clear(choinv); _arb_vec_clear(x, g); flint_free(approx); flint_free(pt); @@ -94,10 +115,10 @@ acb_theta_dist_ubound(arf_t u, arb_srcptr v, const arb_mat_t C, slong prec) arf_clear(b); } -void -acb_theta_dist_lat(arb_t d, arb_srcptr v, const arb_mat_t C, slong prec) +static void +acb_theta_dist_lat(arb_t d, arb_srcptr v, const arb_mat_t cho, slong prec) { - slong g = arb_mat_nrows(C); + slong g = arb_mat_nrows(cho); acb_theta_eld_t E; slong nb; slong * pts; @@ -110,8 +131,8 @@ acb_theta_dist_lat(arb_t d, arb_srcptr v, const arb_mat_t C, slong prec) arf_init(u); arb_init(x); - acb_theta_dist_ubound(u, v, C, prec); - b = acb_theta_eld_set(E, C, u, v); + acb_theta_dist_ubound(u, v, cho, prec); + b = acb_theta_eld_set(E, cho, u, v); if (b) { @@ -122,7 +143,7 @@ acb_theta_dist_lat(arb_t d, arb_srcptr v, const arb_mat_t C, slong prec) arb_pos_inf(d); for (k = 0; k < nb; k++) { - acb_theta_dist_pt(x, v, C, pts + k * g, prec); + acb_theta_dist_pt(x, v, cho, pts + k * g, prec); arb_min(d, d, x, prec); } @@ -130,7 +151,8 @@ acb_theta_dist_lat(arb_t d, arb_srcptr v, const arb_mat_t C, slong prec) } else { - acb_theta_dist_unif(d, C, prec); + /* Should not happen in tests */ + acb_theta_dist_unif(d, cho, prec); } arb_nonnegative_part(d, d); @@ -138,3 +160,41 @@ acb_theta_dist_lat(arb_t d, arb_srcptr v, const arb_mat_t C, slong prec) arf_clear(u); arb_clear(x); } + +void +acb_theta_eld_distances(arb_ptr ds, acb_srcptr zs, slong nb, + const acb_mat_t tau, slong prec) +{ + slong g = acb_mat_nrows(tau); + slong n = 1 << g; + arb_mat_t yinv, cho; + arb_ptr v, w; + ulong a; + slong j; + + arb_mat_init(yinv, g, g); + arb_mat_init(cho, g, g); + v = _arb_vec_init(g); + w = _arb_vec_init(g); + + acb_siegel_cho_yinv(cho, yinv, tau, prec); + + for (j = 0; j < nb; j++) + { + _acb_vec_get_imag(v, zs + j * g, 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, cho, w, prec); + acb_theta_dist_lat(&ds[j * n + a], w, cho, prec); + } + } + + arb_mat_clear(yinv); + arb_mat_clear(cho); + _arb_vec_clear(v, g); + _arb_vec_clear(w, g); +} diff --git a/src/acb_theta/eld_init.c b/src/acb_theta/eld_init.c index a45c81fb7b..f36c41dad6 100644 --- a/src/acb_theta/eld_init.c +++ b/src/acb_theta/eld_init.c @@ -16,12 +16,12 @@ 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->dim = d; + E->ambient_dim = g; E->last_coords = flint_malloc((g - d) * sizeof(slong)); E->rchildren = NULL; - acb_theta_eld_nr(E) = 0; + E->nr = 0; E->lchildren = NULL; - acb_theta_eld_nl(E) = 0; + E->nl = 0; E->box = flint_malloc(d * sizeof(slong)); } diff --git a/src/acb_theta/eld_points.c b/src/acb_theta/eld_points.c index aa6136bad0..b1cd302cb4 100644 --- a/src/acb_theta/eld_points.c +++ b/src/acb_theta/eld_points.c @@ -14,21 +14,19 @@ 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 d = E->dim; + slong g = E->ambient_dim; slong k, j, i; if (d == 1) { i = 0; - for (k = acb_theta_eld_min(E); k <= acb_theta_eld_max(E); k++) + for (k = (E->min); k <= (E->max); k++) { pts[i] = k; for (j = 1; j < g; j++) { - pts[i + j] = acb_theta_eld_coord(E, j); + pts[i + j] = E->last_coords[j - d]; } i += g; } @@ -36,15 +34,15 @@ acb_theta_eld_points(slong * pts, const acb_theta_eld_t E) else /* d > 1 */ { i = 0; - for (k = 0; k < nr; k++) + for (k = 0; k < (E->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)); + acb_theta_eld_points(&pts[i], &E->rchildren[k]); + i += g * ((&E->rchildren[k])->nb_pts); } - for (k = 0; k < nl; k++) + for (k = 0; k < (E->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)); + acb_theta_eld_points(&pts[i], &E->lchildren[k]); + i += g * ((&E->lchildren[k])->nb_pts); } } } diff --git a/src/acb_theta/eld_print.c b/src/acb_theta/eld_print.c index 24c5ab06fa..a0bae0be32 100644 --- a/src/acb_theta/eld_print.c +++ b/src/acb_theta/eld_print.c @@ -14,8 +14,8 @@ 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 d = E->dim; + slong g = E->ambient_dim; slong k; for (k = 0; k < g - d; k++) @@ -25,19 +25,18 @@ acb_theta_eld_print(const acb_theta_eld_t E) flint_printf("Slice (..."); for (k = 0; k < g - d; k++) { - flint_printf(", %wd", acb_theta_eld_coord(E, k + d)); + flint_printf(", %wd", E->last_coords[k]); } - flint_printf("): from %wd to %wd (mid: %wd)\n", - acb_theta_eld_min(E), acb_theta_eld_max(E), acb_theta_eld_mid(E)); + flint_printf("): from %wd to %wd (mid: %wd)\n", E->min, E->max, E->mid); if (d > 1) { - for (k = 0; k < acb_theta_eld_nr(E); k++) + for (k = 0; k < (E->nr); k++) { - acb_theta_eld_print(acb_theta_eld_rchild(E, k)); + acb_theta_eld_print(&E->rchildren[k]); } - for (k = 0; k < acb_theta_eld_nl(E); k++) + for (k = 0; k < (E->nl); k++) { - acb_theta_eld_print(acb_theta_eld_lchild(E, k)); + acb_theta_eld_print(&E->lchildren[k]); } } } diff --git a/src/acb_theta/eld_set.c b/src/acb_theta/eld_set.c index 14b7a5edc1..8ca1d101fa 100644 --- a/src/acb_theta/eld_set.c +++ b/src/acb_theta/eld_set.c @@ -13,7 +13,7 @@ #include "arb_mat.h" #include "acb_theta.h" -#define ACB_THETA_ELD_MAX_PTS 1000000 +#define ACB_THETA_ELD_MAX_PTS 10000000 #define ACB_THETA_ELD_MAX_ERR 100 static void @@ -105,26 +105,26 @@ acb_theta_eld_next_R2(arf_t next_R2, const arf_t R2, const arb_t gamma, const ar 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 d = E->dim; + slong g = E->ambient_dim; 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; + E->nr = nr; for (k = 0; k < nr; k++) { - acb_theta_eld_init(acb_theta_eld_rchild(E, k), d - 1, g); + acb_theta_eld_init(&E->rchildren[k], d - 1, g); } } if (nl > 0) { E->lchildren = flint_malloc(nl * sizeof(struct acb_theta_eld_struct)); - acb_theta_eld_nl(E) = nl; + E->nl = nl; for (k = 0; k < nl; k++) { - acb_theta_eld_init(acb_theta_eld_lchild(E, k), d - 1, g); + acb_theta_eld_init(&E->lchildren[k], d - 1, g); } } } @@ -134,8 +134,8 @@ 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 d = E->dim; + slong g = E->ambient_dim; slong lp = ACB_THETA_LOW_PREC; slong k; arb_t x, ctr; @@ -169,9 +169,9 @@ acb_theta_eld_init_interval(acb_theta_eld_t E, const arb_mat_t C, if (res) { - acb_theta_eld_min(E) = min; - acb_theta_eld_mid(E) = mid; - acb_theta_eld_max(E) = max; + E->min = min; + E->mid = mid; + E->max = max; } arb_clear(x); @@ -186,17 +186,16 @@ 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 d = E->dim; + slong g = E->ambient_dim; + slong min, mid, max; 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; + slong c, nr, nl, k; int res; res = acb_theta_eld_init_interval(E, C, R2, v, last_coords); @@ -204,34 +203,34 @@ acb_theta_eld_set_rec(acb_theta_eld_t E, const arb_mat_t C, { return 0; } - min = acb_theta_eld_min(E); - mid = acb_theta_eld_mid(E); - max = acb_theta_eld_max(E); + min = E->min; + mid = E->mid; + max = E->max; /* Induction only if d > 1 and min <= max */ if (min > max) { - acb_theta_eld_nb_pts(E) = 0; + E->nb_pts = 0; if (d == 1) { - acb_theta_eld_nb_border(E) = 2; + E->nb_border = 2; } else { - acb_theta_eld_nb_border(E) = 0; + E->nb_border = 0; } for (k = 0; k < d; k++) { - acb_theta_eld_box(E, k) = 0; + E->box[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); + E->nb_pts = max - min + 1; + E->nb_border = 2; + E->box[0] = FLINT_MAX(max, -min); + return (E->nb_pts <= ACB_THETA_ELD_MAX_PTS); } /* Begin main function */ @@ -259,12 +258,12 @@ acb_theta_eld_set_rec(acb_theta_eld_t E, const arb_mat_t C, } /* 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); + E->nb_pts = 0; + E->nb_border = 0; + E->box[d - 1] = FLINT_MAX(max, -min); for (k = 0; k < d - 1; k++) { - acb_theta_eld_box(E, k) = 0; + E->box[k] = 0; } /* Right loop */ @@ -279,14 +278,13 @@ acb_theta_eld_set_rec(acb_theta_eld_t E, const arb_mat_t C, 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); + res = acb_theta_eld_set_rec(&E->rchildren[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); + E->nb_pts += (&E->rchildren[k])->nb_pts; + E->nb_border += (&E->rchildren[k])->nb_border; + slong_vec_max(E->box, E->box, (&E->rchildren[k])->box, d - 1); + res = ((E->nb_pts) <= ACB_THETA_ELD_MAX_PTS); } } @@ -299,15 +297,14 @@ acb_theta_eld_set_rec(acb_theta_eld_t E, const arb_mat_t C, 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); + res = acb_theta_eld_set_rec(&E->lchildren[k], C, next_R2, next_v, next_coords); - if (res) /* we expect this always holds */ + if (res) { - 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); + E->nb_pts += (&E->lchildren[k])->nb_pts; + E->nb_border += (&E->lchildren[k])->nb_border; + slong_vec_max(E->box, E->box, (&E->lchildren[k])->box, d - 1); + res = ((E->nb_pts) <= ACB_THETA_ELD_MAX_PTS); } } @@ -322,8 +319,8 @@ acb_theta_eld_set_rec(acb_theta_eld_t E, const arb_mat_t C, 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); + slong d = E->dim; + slong g = E->ambient_dim; acb_theta_eld_clear(E); acb_theta_eld_init(E, d, g); diff --git a/src/acb_theta/g2_character.c b/src/acb_theta/g2_character.c index fe28d042bf..2f0a7ed4b9 100644 --- a/src/acb_theta/g2_character.c +++ b/src/acb_theta/g2_character.c @@ -10,6 +10,7 @@ */ #include "fmpz.h" +#include "fmpz_mat.h" #include "acb_theta.h" /* See Cléry, Faber, van der Geer, "Covariants of binary sextics and modular diff --git a/src/acb_theta/g2_chi10.c b/src/acb_theta/g2_chi10.c deleted file mode 100644 index dba212a0e4..0000000000 --- a/src/acb_theta/g2_chi10.c +++ /dev/null @@ -1,36 +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 3 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 deleted file mode 100644 index a0dc9892dd..0000000000 --- a/src/acb_theta/g2_chi12.c +++ /dev/null @@ -1,57 +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 3 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_covariants.c b/src/acb_theta/g2_covariants.c index 5264d4a8a5..59922b139a 100644 --- a/src/acb_theta/g2_covariants.c +++ b/src/acb_theta/g2_covariants.c @@ -13,49 +13,79 @@ #include "acb_theta.h" static void -acb_theta_g2_transvectants(acb_poly_struct * res, const acb_poly_t f, slong prec) +acb_theta_g2_transvectants(acb_poly_struct * res, const acb_poly_t f, int lead, slong prec) { + slong degrees[5] = {4, 8, 2, 6, 8}; + acb_t x; acb_poly_t s; + slong k; + acb_init(x); 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); + /* Get polynomials r2, r3, ..., r6 */ + acb_theta_g2_transvectant(&res[2], f, f, 6, 6, 4, 0, prec); + acb_theta_g2_transvectant(&res[3], f, f, 6, 6, 2, 0, prec); + acb_theta_g2_transvectant(&res[4], f, &res[2], 6, 4, 4, 0, prec); + acb_theta_g2_transvectant(&res[5], f, &res[2], 6, 4, 2, 0, prec); + acb_theta_g2_transvectant(&res[6], f, &res[2], 6, 4, 1, 0, prec); + + /* Get other transvectants or leading terms */ + acb_theta_g2_transvectant(&res[1], f, f, 6, 6, 6, lead, prec); + acb_theta_g2_transvectant(&res[7], f, &res[3], 6, 8, 1, lead, prec); + acb_theta_g2_transvectant(&res[8], &res[2], &res[2], 4, 4, 4, lead, prec); + acb_theta_g2_transvectant(&res[9], f, &res[4], 6, 2, 2, lead, prec); + acb_theta_g2_transvectant(&res[10], f, &res[4], 6, 2, 1, lead, prec); + acb_theta_g2_transvectant(&res[11], &res[3], &res[2], 8, 4, 1, lead, prec); + acb_theta_g2_transvectant(&res[12], &res[2], &res[4], 4, 2, 2, lead, prec); + acb_theta_g2_transvectant(&res[13], &res[2], &res[4], 4, 2, 1, lead, prec); + acb_theta_g2_transvectant(&res[14], &res[3], &res[4], 8, 2, 1, lead, prec); + acb_theta_g2_transvectant(&res[15], &res[4], &res[4], 2, 2, 2, lead, prec); + acb_theta_g2_transvectant(&res[16], &res[5], &res[4], 6, 2, 1, lead, prec); + acb_theta_g2_transvectant(&res[17], &res[6], &res[4], 8, 2, 2, lead, 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_theta_g2_transvectant(&res[18], f, s, 6, 4, 4, lead, prec); + acb_theta_g2_transvectant(&res[19], f, s, 6, 4, 3, lead, prec); + acb_theta_g2_transvectant(&res[20], &res[2], s, 4, 4, 3, lead, prec); + acb_theta_g2_transvectant(&res[21], &res[6], s, 8, 4, 4, lead, 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_theta_g2_transvectant(&res[22], f, s, 6, 6, 6, lead, prec); + acb_theta_g2_transvectant(&res[23], f, s, 6, 6, 5, lead, prec); + acb_theta_g2_transvectant(&res[24], &res[6], s, 8, 6, 6, lead, 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_theta_g2_transvectant(&res[25], &res[6], s, 8, 8, 8, lead, prec); + + /* Get leading coefficients of r2, ..., r6 if needed */ + if (lead) + { + for (k = 2; k <= 6; k++) + { + acb_poly_get_coeff_acb(x, &res[k], degrees[k - 2]); + acb_poly_zero(&res[k]); + acb_poly_set_coeff_acb(&res[k], 0, x); + } + } + + /* Get r0 */ + if (lead) + { + acb_poly_zero(&res[0]); + acb_poly_get_coeff_acb(x, f, 6); + acb_poly_set_coeff_acb(&res[0], 0, x); + } + else + { + acb_poly_set(&res[0], f); + } + acb_clear(x); acb_poly_clear(s); } void -acb_theta_g2_covariants(acb_poly_struct * res, const acb_poly_t f, slong prec) +acb_theta_g2_covariants(acb_poly_struct * res, const acb_poly_t f, int lead, slong prec) { - double cofactors[ACB_THETA_G2_COV_NB] = {1, 60, 75, 90, 2250, 2250, 450, + double cofactors[26] = {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}; @@ -66,8 +96,8 @@ acb_theta_g2_covariants(acb_poly_struct * res, const acb_poly_t f, slong prec) acb_init(c); fmpz_init(m); - acb_theta_g2_transvectants(res, f, prec); - for (k = 0; k < ACB_THETA_G2_COV_NB; k++) + acb_theta_g2_transvectants(res, f, lead, prec); + for (k = 0; k < 26; k++) { fmpz_set_d(m, cofactors[k]); acb_set_fmpz(c, m); diff --git a/src/acb_theta/g2_covariants_lead.c b/src/acb_theta/g2_covariants_lead.c deleted file mode 100644 index b4134e9ce8..0000000000 --- a/src/acb_theta/g2_covariants_lead.c +++ /dev/null @@ -1,95 +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 3 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_even_weight.c b/src/acb_theta/g2_even_weight.c new file mode 100644 index 0000000000..e60e054afc --- /dev/null +++ b/src/acb_theta/g2_even_weight.c @@ -0,0 +1,183 @@ +/* + Copyright (C) 2025 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 3 of the License, or + (at your option) any later version. See . +*/ + +#include "acb.h" +#include "acb_theta.h" + +static 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); +} + +static 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); +} + +static void +g2_psi6_bits(int * b1, int * b2, int * b3, int * b4, ulong b) +{ + *b1 = acb_theta_char_bit(b, 0, 4); + *b2 = acb_theta_char_bit(b, 1, 4); + *b3 = acb_theta_char_bit(b, 2, 4); + *b4 = acb_theta_char_bit(b, 3, 4); +} + +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; +} + +static 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); +} + +static 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); +} + +void +acb_theta_g2_even_weight(acb_t psi4, acb_t psi6, acb_t chi10, acb_t chi12, + acb_srcptr th2, slong prec) +{ + slong g = 2; + slong n = 1 << (2 * g); + 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(psi4, s, -2); + + 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(chi10, t, -12); + + acb_theta_g2_psi6(psi6, th2, prec); + acb_theta_g2_chi12(chi12, th2, prec); + + acb_clear(t); + acb_clear(s); +} diff --git a/src/acb_theta/g2_jet_naive_1.c b/src/acb_theta/g2_jet_naive_1.c deleted file mode 100644 index 58cd4ae055..0000000000 --- a/src/acb_theta/g2_jet_naive_1.c +++ /dev/null @@ -1,253 +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 3 of the License, or - (at your option) any later version. See . -*/ - -#include "arb_mat.h" -#include "acb.h" -#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 deleted file mode 100644 index 4612a2a660..0000000000 --- a/src/acb_theta/g2_psi4.c +++ /dev/null @@ -1,37 +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 3 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 deleted file mode 100644 index fa5d3aa83a..0000000000 --- a/src/acb_theta/g2_psi6.c +++ /dev/null @@ -1,82 +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 3 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_chi5.c b/src/acb_theta/g2_sextic_chi5.c index 11c900fd92..835d488f13 100644 --- a/src/acb_theta/g2_sextic_chi5.c +++ b/src/acb_theta/g2_sextic_chi5.c @@ -9,21 +9,20 @@ (at your option) any later version. See . */ +#include "fmpz_mat.h" #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) +acb_theta_g2_sextic_chi5(acb_poly_t f, 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_ptr zero, dth, th; acb_t det; slong k; @@ -33,38 +32,43 @@ acb_theta_g2_sextic_chi5(acb_poly_t res, acb_t chi5, const acb_mat_t tau, slong acb_mat_init(cinv, g, g); dth = _acb_vec_init(n2 * nb); th = _acb_vec_init(n2); - z = _acb_vec_init(g); + zero = _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) + if (acb_siegel_is_reduced(w, -10, prec)) { - acb_theta_g2_jet_naive_1(dth, w, prec); - } - else - { - acb_theta_jet_ql_all(dth, z, w, 1, prec); - } + acb_theta_jet_notransform(dth, zero, 1, w, 1, 0, 1, 0, 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); + for (k = 0; k < n2; k++) + { + acb_set(&th[k], &dth[k * nb]); + } + acb_theta_g2_chi3_6(f, dth, prec); + acb_theta_g2_chi5(chi5, th, prec); + acb_poly_scalar_div(f, f, 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); + acb_theta_g2_detk_symj(f, cinv, f, -2, 6, prec); + acb_mat_det(det, cinv, prec); + acb_pow_ui(det, det, 5, prec); - if (acb_theta_g2_character(mat) == 1) + if (acb_theta_g2_character(mat) == 1) + { + acb_neg(det, det); + } + acb_mul(chi5, chi5, det, prec); + } + else { - acb_neg(det, det); + /* Should not happen in tests */ + acb_indeterminate(chi5); + for (k = 0; k < 6; k++) + { + acb_poly_set_coeff_acb(f, k, chi5); + } } - acb_mul(chi5, chi5, det, prec); fmpz_mat_clear(mat); acb_mat_clear(w); @@ -72,6 +76,6 @@ acb_theta_g2_sextic_chi5(acb_poly_t res, acb_t chi5, const acb_mat_t tau, slong acb_mat_clear(cinv); _acb_vec_clear(dth, n2 * nb); _acb_vec_clear(th, n2); - _acb_vec_clear(z, g); + _acb_vec_clear(zero, g); acb_clear(det); } diff --git a/src/acb_theta/g2_transvectant.c b/src/acb_theta/g2_transvectant.c index f58a3ae5d2..650e170f2e 100644 --- a/src/acb_theta/g2_transvectant.c +++ b/src/acb_theta/g2_transvectant.c @@ -12,8 +12,52 @@ #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, +static 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); +} + +static void +acb_theta_g2_transvectant_poly(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; @@ -86,3 +130,24 @@ acb_theta_g2_transvectant(acb_poly_t res, const acb_poly_t g, const acb_poly_t h fmpz_clear(num); fmpz_clear(f); } + +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, int lead, slong prec) +{ + if (lead) + { + acb_t x; + acb_init(x); + + acb_theta_g2_transvectant_lead(x, g, h, m, n, k, prec); + acb_poly_zero(res); + acb_poly_set_coeff_acb(res, 0, x); + + acb_clear(x); + } + else + { + acb_theta_g2_transvectant_poly(res, g, h, m, n, k, prec); + } +} diff --git a/src/acb_theta/g2_transvectant_lead.c b/src/acb_theta/g2_transvectant_lead.c deleted file mode 100644 index a1f5a56af0..0000000000 --- a/src/acb_theta/g2_transvectant_lead.c +++ /dev/null @@ -1,57 +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 3 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.c b/src/acb_theta/jet.c new file mode 100644 index 0000000000..11dea20a2b --- /dev/null +++ b/src/acb_theta/jet.c @@ -0,0 +1,140 @@ +/* + Copyright (C) 2025 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 3 of the License, or + (at your option) any later version. See . +*/ + +#include "fmpz_mat.h" +#include "acb_poly.h" +#include "acb_mat.h" +#include "acb_theta.h" + +void +acb_theta_jet(acb_ptr th, acb_srcptr zs, slong nb, const acb_mat_t tau, + slong ord, int all, int sqr, slong prec) +{ + slong g = acb_mat_nrows(tau); + slong n = 1 << g; + slong nbth = (all ? n * n : 1); + slong nbjet = acb_theta_jet_nb(ord, g); + slong nbu; + fmpz_mat_t mat; + acb_mat_t new_tau, N, ct; + acb_ptr new_zs, exps, cs, aux, units, jet; + arb_ptr rs, r; + acb_t s, t; + ulong ab; + ulong * ch; + slong * e; + slong kappa, j; + int res; + + if (nb <= 0) + { + return; + } + sqr = sqr && (ord == 0); + nbu = (sqr ? 4 : 8); + + fmpz_mat_init(mat, 2 * g, 2 * g); + acb_mat_init(new_tau, g, g); + acb_mat_init(N, g, g); + acb_mat_init(ct, g, g); + new_zs = _acb_vec_init(nb * g); + exps = _acb_vec_init(nb); + cs = _acb_vec_init(nb); + aux = _acb_vec_init(nbth * nb * nbjet); + units = _acb_vec_init(nbu); + jet = _acb_vec_init(nbjet); + rs = _arb_vec_init(nb * g); + r = _arb_vec_init(g); + acb_init(s); + acb_init(t); + ch = flint_malloc(nbth * sizeof(ulong)); + e = flint_malloc(nbth * sizeof(slong)); + + res = acb_theta_reduce_tau(new_zs, new_tau, mat, N, ct, exps, zs, nb, tau, prec); + if (res) + { + res = acb_theta_reduce_z(new_zs, rs, cs, new_zs, nb, new_tau, prec); + } + + if (res) + { + /* Setup */ + _acb_vec_unit_roots(units, nbu, nbu, prec); + kappa = acb_siegel_kappa(s, mat, new_tau, sqr, prec); + acb_theta_char_table(ch, e, mat, (all ? -1 : 0)); + + acb_theta_jet_notransform(aux, new_zs, nb, new_tau, ord, *ch, all, sqr, prec); + + /* Account for reduce_z */ + for (j = 0; j < nb; j++) + { + if (sqr) + { + acb_sqr(&cs[j], &cs[j], prec); + } + _acb_vec_scalar_mul(aux + j * nbth * nbjet, aux + j * nbth * nbjet, + nbth * nbjet, &cs[j], prec); + _arb_vec_neg(r, rs + j * g, g); + _arb_vec_scalar_mul_2exp_si(r, r, g, 1); + acb_theta_jet_exp_pi_i(jet, r, ord, g, prec); + for (ab = 0; ab < nbth; ab++) + { + acb_theta_jet_mul(aux + j * nbth * nbjet + ab * nbjet, + aux + j * nbth * nbjet + ab * nbjet, jet, ord, g, prec); + /* No signs because 2r is divisible by 4 */ + } + } + + /* Account for reduce_tau */ + for (j = 0; j < nb; j++) + { + acb_theta_jet_exp_qf(jet, zs + j * g, N, ord, prec); + if (sqr) + { + acb_sqr(&jet[0], &jet[0], prec); + } + + for (ab = 0; ab < nbth; ab++) + { + acb_mul(t, s, &units[(kappa + e[ab]) % (sqr ? 4 : 8)], prec); + _acb_vec_scalar_mul(th + j * nbth * nbjet + ab * nbjet, + aux + j * nbth * nbjet + (all ? ch[ab] : 0) * nbjet, + nbjet, t, prec); + acb_theta_jet_compose(th + j * nbth * nbjet + ab * nbjet, + th + j * nbth * nbjet + ab * nbjet, ct, ord, prec); + acb_theta_jet_mul(th + j * nbth * nbjet + ab * nbjet, + th + j * nbth * nbjet + ab * nbjet, jet, ord, g, prec); + } + } + } + else + { + /* Should not happen in tests */ + _acb_vec_indeterminate(th, nb * nbth * nbjet); + } + + fmpz_mat_clear(mat); + acb_mat_clear(new_tau); + acb_mat_clear(N); + acb_mat_clear(ct); + _acb_vec_clear(new_zs, nb * g); + _acb_vec_clear(exps, nb); + _acb_vec_clear(cs, nb); + _acb_vec_clear(aux, nb * nbth * nbjet); + _acb_vec_clear(units, nbu); + _acb_vec_clear(jet, nbjet); + _arb_vec_clear(rs, nb * g); + _arb_vec_clear(r, g); + acb_clear(s); + acb_clear(t); + flint_free(ch); + flint_free(e); +} diff --git a/src/acb_theta/jet_all.c b/src/acb_theta/jet_all.c deleted file mode 100644 index 3c9a66c7b0..0000000000 --- a/src/acb_theta/jet_all.c +++ /dev/null @@ -1,175 +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 3 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_exp_qf.c b/src/acb_theta/jet_exp_qf.c new file mode 100644 index 0000000000..f4a6e4589f --- /dev/null +++ b/src/acb_theta/jet_exp_qf.c @@ -0,0 +1,100 @@ +/* + Copyright (C) 2024 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 3 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) */ +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); + + if (ord > 0) + { + 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); +} diff --git a/src/acb_theta/jet_naive_00.c b/src/acb_theta/jet_naive_00.c deleted file mode 100644 index d8adf978d8..0000000000 --- a/src/acb_theta/jet_naive_00.c +++ /dev/null @@ -1,191 +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 3 of the License, or - (at your option) any later version. See . -*/ - -#include "arb_mat.h" -#include "acb.h" -#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 deleted file mode 100644 index 0eb29898fb..0000000000 --- a/src/acb_theta/jet_naive_all.c +++ /dev/null @@ -1,245 +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 3 of the License, or - (at your option) any later version. See . -*/ - -#include "arb_mat.h" -#include "acb.h" -#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 deleted file mode 100644 index a82ddc81d1..0000000000 --- a/src/acb_theta/jet_naive_fixed_ab.c +++ /dev/null @@ -1,68 +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 3 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_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_notransform.c b/src/acb_theta/jet_notransform.c new file mode 100644 index 0000000000..04f757bb2c --- /dev/null +++ b/src/acb_theta/jet_notransform.c @@ -0,0 +1,205 @@ +/* + Copyright (C) 2025 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 3 of the License, or + (at your option) any later version. See . +*/ + +#include "acb.h" +#include "acb_mat.h" +#include "acb_theta.h" + +/* We use the formula: + theta_00(z, tau) = sum_a theta_{a,0}(2z, 4tau) */ + +static void +acb_theta_jet_notransform_00(acb_ptr th, acb_srcptr zs, slong nb, + const acb_mat_t tau, slong ord, slong prec) +{ + slong g = acb_mat_nrows(tau); + slong n = 1 << g; + slong nbjet = acb_theta_jet_nb(ord, g); + slong * tups; + acb_ptr new_zs, aux; + acb_mat_t new_tau; + slong j, k, a; + + tups = flint_malloc(g * nbjet * sizeof(slong)); + new_zs = _acb_vec_init(nb * g); + aux = _acb_vec_init(nb * n * nbjet); + acb_mat_init(new_tau, g, g); + + _acb_vec_scalar_mul_2exp_si(new_zs, zs, nb * g, 1); + acb_mat_scalar_mul_2exp_si(new_tau, tau, 2); + acb_theta_ql_jet(aux, new_zs, nb, new_tau, ord, 0, prec); + + _acb_vec_zero(th, nb * nbjet); + acb_theta_jet_tuples(tups, ord, g); + for (j = 0; j < nb; j++) + { + for (k = 0; k < nbjet; k++) + { + for (a = 0; a < n; a++) + { + acb_add(&th[j * nbjet + k], &th[j * nbjet + k], + &aux[j * n * nbjet + a * nbjet + k], prec); + } + acb_mul_2exp_si(&th[j * nbjet + k], &th[j * nbjet + k], + acb_theta_jet_total_order(tups + k * g, g)); + } + } + + flint_free(tups); + _acb_vec_clear(new_zs, nb * g); + _acb_vec_clear(aux, nb * n * nbjet); + acb_mat_clear(new_tau); +} + +/* We use the formula: + theta_ab(z, tau) = exp(pi i a^T tau a/4) exp(2 pi i a^T (z + b/2)) + theta_00(z + tau a/2 + b/2, tau) */ + +static void +acb_theta_char_dot_acb(acb_t x, ulong a, acb_srcptr z, slong g, slong prec) +{ + slong * v; + slong j; + + v = flint_malloc(g * sizeof(slong)); + + for (j = 0; j < g; j++) + { + v[j] = acb_theta_char_bit(a, j, g); + } + acb_dot_si(x, NULL, 0, z, 1, v, 1, g, prec); + acb_mul_2exp_si(x, x, -1); + + flint_free(v); +} + +static void +acb_theta_jet_notransform_one(acb_ptr th, acb_srcptr zs, slong nb, + const acb_mat_t tau, slong ord, ulong ab, slong prec) +{ + slong g = acb_mat_nrows(tau); + slong nbjet = acb_theta_jet_nb(ord, g); + ulong b = ab % (1 << g); + ulong a = ab >> g; + acb_ptr new_zs, v, w, aux; + arb_ptr u; + acb_t c, x; + slong j; + + new_zs = _acb_vec_init(nb * g); + v = _acb_vec_init(g); + w = _acb_vec_init(g); + aux = _acb_vec_init(nbjet); + u = _arb_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 (j = 0; j < nb; j++) + { + _acb_vec_add(new_zs + j * g, zs + j * g, w, g, prec); + } + + acb_theta_jet_notransform_00(th, new_zs, nb, tau, ord, prec); + + acb_theta_char_dot_acb(c, a, v, g, prec); + for (j = 0; j < nb; j++) + { + acb_theta_char_get_acb(w, b, g); + _acb_vec_add(w, w, zs + j * 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_vec_scalar_mul(th + j * nbjet, th + j * nbjet, nbjet, x, prec); + } + + if (ord > 0) + { + acb_theta_char_get_arb(u, a, g); + _arb_vec_scalar_mul_2exp_si(u, u, g, 1); + acb_theta_jet_exp_pi_i(aux, u, ord, g, prec); + for (j = 0; j < nb; j++) + { + acb_theta_jet_mul(th + j * nbjet, th + j * nbjet, aux, ord, g, prec); + } + } + + _acb_vec_clear(new_zs, nb * g); + _acb_vec_clear(v, g); + _acb_vec_clear(w, g); + _acb_vec_clear(aux, nbjet); + _arb_vec_clear(u, g); + acb_clear(c); + acb_clear(x); +} + +void +acb_theta_jet_notransform(acb_ptr th, acb_srcptr zs, slong nb, + const acb_mat_t tau, slong ord, ulong ab, int all, int sqr, slong prec) +{ + if (nb <= 0) + { + return; + } + else if (all && ord == 0 && sqr) + { + /* Use duplication formula */ + slong g = acb_mat_nrows(tau); + slong n = 1 << g; + acb_ptr new_zs, new_th; + acb_mat_t new_tau; + slong j; + int add_zero; + + add_zero = !_acb_vec_is_zero(zs, g); + new_zs = _acb_vec_init((nb + add_zero) * g); + new_th = _acb_vec_init((nb + add_zero) * n); + acb_mat_init(new_tau, g, g); + + _acb_vec_scalar_mul_2exp_si(new_zs + add_zero * g, zs, nb * g, 1); + acb_mat_scalar_mul_2exp_si(new_tau, tau, 1); + acb_theta_ql_jet(new_th, new_zs, nb + add_zero, new_tau, + 0, 0, prec); + + for (j = 0; j < nb; j++) + { + acb_theta_agm_mul(th + j * n * n, new_th, + new_th + (j + add_zero) * n, g, 1, prec); + } + + _acb_vec_clear(new_zs, (nb + add_zero) * g); + _acb_vec_clear(new_th, (nb + add_zero) * n); + acb_mat_clear(new_tau); + } + else if (all) + { + acb_theta_ql_jet(th, zs, nb, tau, ord, 1, prec); + } + else /* just one theta value */ + { + if (ab == 0) + { + acb_theta_jet_notransform_00(th, zs, nb, tau, ord, prec); + } + else + { + acb_theta_jet_notransform_one(th, zs, nb, tau, ord, ab, prec); + } + if (ord == 0 && sqr) + { + _acb_vec_sqr(th, th, nb, prec); + } + } +} diff --git a/src/acb_theta/jet_ql_all.c b/src/acb_theta/jet_ql_all.c deleted file mode 100644 index d00dbf6eda..0000000000 --- a/src/acb_theta/jet_ql_all.c +++ /dev/null @@ -1,184 +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 3 of the License, or - (at your option) any later version. See . -*/ - -#include "ulong_extras.h" -#include "acb.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_finite_diff.c b/src/acb_theta/jet_ql_finite_diff.c deleted file mode 100644 index 9ff323c3ae..0000000000 --- a/src/acb_theta/jet_ql_finite_diff.c +++ /dev/null @@ -1,79 +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 3 of the License, or - (at your option) any later version. See . -*/ - -#include "ulong_extras.h" -#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 deleted file mode 100644 index 18f2c0bc48..0000000000 --- a/src/acb_theta/jet_ql_radius.c +++ /dev/null @@ -1,46 +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 3 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/naive_00.c b/src/acb_theta/naive_00.c deleted file mode 100644 index e209a5c9d0..0000000000 --- a/src/acb_theta/naive_00.c +++ /dev/null @@ -1,131 +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 3 of the License, or - (at your option) any later version. See . -*/ - -#include "arb_mat.h" -#include "acb.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 deleted file mode 100644 index 26966fd36c..0000000000 --- a/src/acb_theta/naive_0b.c +++ /dev/null @@ -1,171 +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 3 of the License, or - (at your option) any later version. See . -*/ - -#include "arb_mat.h" -#include "acb.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 deleted file mode 100644 index 9b8d7e7906..0000000000 --- a/src/acb_theta/naive_all.c +++ /dev/null @@ -1,123 +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 3 of the License, or - (at your option) any later version. See . -*/ - -#include "acb.h" -#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 deleted file mode 100644 index aa8c003d2f..0000000000 --- a/src/acb_theta/naive_fixed_a.c +++ /dev/null @@ -1,62 +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 3 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_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 deleted file mode 100644 index 3686600b30..0000000000 --- a/src/acb_theta/naive_fixed_ab.c +++ /dev/null @@ -1,62 +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 3 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_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_reduce.c b/src/acb_theta/naive_reduce.c deleted file mode 100644 index 06dbaba79d..0000000000 --- a/src/acb_theta/naive_reduce.c +++ /dev/null @@ -1,158 +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 3 of the License, or - (at your option) any later version. See . -*/ - -#include "acb.h" -#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/profile/p-all.c b/src/acb_theta/profile/p-all.c deleted file mode 100644 index 183762e79e..0000000000 --- a/src/acb_theta/profile/p-all.c +++ /dev/null @@ -1,114 +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 3 of the License, or - (at your option) any later version. See . -*/ - -#include -#include "profiler.h" -#include "acb.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 deleted file mode 100644 index 1895bdf12b..0000000000 --- a/src/acb_theta/profile/p-jet_all.c +++ /dev/null @@ -1,59 +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 3 of the License, or - (at your option) any later version. See . -*/ - -#include "profiler.h" -#include "ulong_extras.h" -#include "acb.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 deleted file mode 100644 index cc45df2906..0000000000 --- a/src/acb_theta/profile/p-ql_a0.c +++ /dev/null @@ -1,121 +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 3 of the License, or - (at your option) any later version. See . -*/ - -#include -#include -#include "profiler.h" -#include "acb.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_rand_init(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_rand_clear(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 deleted file mode 100644 index 26720499d8..0000000000 --- a/src/acb_theta/profile/p-ql_a0_split.c +++ /dev/null @@ -1,151 +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 3 of the License, or - (at your option) any later version. See . -*/ - -#include -#include "profiler.h" -#include "arb_mat.h" -#include "acb.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_rand_init(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_rand_clear(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 deleted file mode 100644 index 29e16d052e..0000000000 --- a/src/acb_theta/profile/p-ql_a0_steps.c +++ /dev/null @@ -1,134 +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 3 of the License, or - (at your option) any later version. See . -*/ - -#include -#include "profiler.h" -#include "arb_mat.h" -#include "acb.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_rand_init(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_rand_clear(state); - flint_cleanup(); - return 0; -} diff --git a/src/acb_theta/profile/p-ql_exact.c b/src/acb_theta/profile/p-ql_exact.c new file mode 100644 index 0000000000..a41e055efa --- /dev/null +++ b/src/acb_theta/profile/p-ql_exact.c @@ -0,0 +1,187 @@ +/* + 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 3 of the License, or + (at your option) any later version. See . +*/ + +#include +#include +#include "profiler.h" +#include "ulong_extras.h" +#include "acb.h" +#include "acb_mat.h" +#include "acb_theta.h" + +static int usage(char * argv[]) +{ + flint_printf("usage: %s g pmin pmax cst s exp\n", argv[0]); + return 1; +} + +int main(int argc, char * argv[]) +{ + flint_rand_t state; + timeit_t t0; + slong g, n, pmin, pmax, prec, s, exp, reps; + double tmin, tref, tcur; + int cst; + slong * pattern; + slong * test_pattern; + slong * delta; + slong * best_delta; + acb_mat_t tau; + acb_ptr z, th; + slong j, k, l, m; + int useful; + + if (argc < 7) + { + return usage(argv); + } + + g = atol(argv[1]); + n = 1 << g; + pmin = atol(argv[2]); + pmax = atol(argv[3]); + cst = atoi(argv[4]); + s = atol(argv[5]); + exp = atol(argv[6]); + + flint_rand_init(state); + pattern = flint_malloc(g * sizeof(slong)); + test_pattern = flint_malloc(g * sizeof(slong)); + delta = flint_malloc(g * sizeof(slong)); + best_delta = flint_malloc(g * sizeof(slong)); + acb_mat_init(tau, g, g); + z = _acb_vec_init(2 * g); + th = _acb_vec_init(2 * n * n); + + prec = pmin; + acb_siegel_randtest_compact(tau, state, 1, prec); + for (j = g - s; j < g; j++) + { + for (k = 0; k < g; k++) + { + acb_mul_2exp_si(acb_mat_entry(tau, j, k), acb_mat_entry(tau, j, k), exp); + acb_mul_2exp_si(acb_mat_entry(tau, k, j), acb_mat_entry(tau, k, j), exp); + } + } + if (!cst) + { + acb_siegel_randtest_vec_reduced(z + g, state, 1, tau, 1, prec); + } + flint_printf("g = %wd, cst = %wd, s = %wd\n", g, cst, s); + flint_printf("Values of tau, z:\n"); + acb_mat_printd(tau, 5); + _acb_vec_printd(z + g, g, 5); + + for (prec = pmin; prec <= pmax; prec = ceil(1.5 * prec)) + { + acb_theta_ql_nb_steps(pattern, tau, cst, prec); + + flint_printf("\nAt prec = %wd, suggested pattern:", prec); + for (j = 0; j < g; j++) + { + flint_printf(" %wd", pattern[j]); + best_delta[j] = 0; + } + flint_printf("\n"); + + tref = 0; + tmin = 0; + m = 9 - 2 * g; + for (k = 0; k < n_pow(m, g); k++) + { + l = k; + for (j = 0; j < g; j++) + { + delta[j] = l % m; + l = l / m; + if (delta[j] > m/2) + { + delta[j] -= m; + } + } + + useful = 1; + for (j = 0; j < g; j++) + { + test_pattern[j] = pattern[j] + delta[j]; + if (test_pattern[j] < 0 + || (j > 0 && test_pattern[j] > test_pattern[j - 1])) + { + useful = 0; + break; + } + } + if (!useful) + { + continue; + } + + flint_printf("Testing pattern", delta); + for (j = 0; j < g; j++) + { + flint_printf(" %wd", test_pattern[j]); + } + flint_printf(", "); + if (k == 0) + { + /* Do it once prior to measuring ? */ + acb_theta_ql_exact(th, z, 2, tau, test_pattern, 1, 0, prec); + } + TIMEIT_REPEAT(t0, reps); + acb_theta_ql_exact(th, z, 2, tau, test_pattern, 1, 0, prec); + TIMEIT_END_REPEAT(t0, reps); + + tcur = ((double) t0->cpu) / reps; + flint_printf("time: %f ms (%wd reps)\n", tcur, reps); + + if (k == 0) + { + tmin = tcur; + tref = tcur; + } + else if (tcur < tmin) + { + tmin = tcur; + for (j = 0; j < g; j++) + { + best_delta[j] = delta[j]; + } + } + } + + if (tmin < tref) + { + flint_printf("\nAt prec = %wd, best pattern had t = %f ms:", prec, tmin); + for (j = 0; j < g; j++) + { + flint_printf(" %wd", pattern[j] + best_delta[j]); + } + flint_printf("\nCompared to suggested pattern with t = %f ms\n", tref); + } + else + { + flint_printf("\nAt prec = %wd, suggested pattern was the best with t = %f ms\n", + prec, tref); + } + } + + flint_rand_clear(state); + flint_free(pattern); + flint_free(test_pattern); + flint_free(delta); + flint_free(best_delta); + acb_mat_clear(tau); + _acb_vec_clear(z, g); + _acb_vec_clear(th, 2 * n * n); + + flint_cleanup(); + return 0; +} diff --git a/src/acb_theta/profile/p-ql_jet_fd.c b/src/acb_theta/profile/p-ql_jet_fd.c new file mode 100644 index 0000000000..db7034d45b --- /dev/null +++ b/src/acb_theta/profile/p-ql_jet_fd.c @@ -0,0 +1,93 @@ +/* + Copyright (C) 2025 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 3 of the License, or + (at your option) any later version. See . +*/ + +#include +#include +#include "profiler.h" +#include "ulong_extras.h" +#include "acb.h" +#include "acb_mat.h" +#include "acb_theta.h" + +static int usage(char * argv[]) +{ + flint_printf("usage: %s g prec ord all\n", argv[0]); + return 1; +} + +int main(int argc, char * argv[]) +{ + flint_rand_t state; + slong g, n, ord, prec, nbth, nbjet; + int all; + acb_mat_t tau; + acb_ptr z, th; + slong * pattern; + acb_theta_ctx_tau_t ctx_tau; + acb_theta_ctx_z_t ctx_z; + slong j; + + if (argc < 5) + { + return usage(argv); + } + + g = atol(argv[1]); + prec = atol(argv[2]); + ord = atol(argv[3]); + all = atoi(argv[4]); + + n = 1 << g; + nbjet = acb_theta_jet_nb(ord, g); + nbth = (all ? n * n : n); + + flint_rand_init(state); + acb_mat_init(tau, g, g); + z = _acb_vec_init(g); + th = _acb_vec_init(nbth * nbjet); + pattern = flint_malloc(g * sizeof(slong)); + acb_theta_ctx_tau_init(ctx_tau, 0, g); + acb_theta_ctx_z_init(ctx_z, g); + + acb_siegel_randtest_compact(tau, state, 0, prec); + acb_siegel_randtest_vec_reduced(z, state, 1, tau, 0, prec); + acb_theta_ql_nb_steps(pattern, tau, 0, prec); + + flint_printf("g = %wd, prec = %wd, pattern:", g, prec); + for (j = 0; j < g; j++) + { + flint_printf(" %wd", pattern[j]); + } + + flint_printf("\nql_jet_fd: "); + TIMEIT_START; + acb_theta_ql_jet_fd(th, z, 1, tau, ord, all, prec); + TIMEIT_STOP; + acb_printd(&th[nbth * nbjet - 1], 5); + flint_printf("\nsum_jet: "); + TIMEIT_START; + acb_theta_ctx_tau_set(ctx_tau, tau, prec + ACB_THETA_LOW_PREC); + acb_theta_ctx_z_set(ctx_z, z, ctx_tau, prec + ACB_THETA_LOW_PREC); + acb_theta_sum_jet(th, ctx_z, 1, ctx_tau, ord, 1, all, prec); + TIMEIT_STOP; + acb_printd(&th[nbth * nbjet - 1], 5); + flint_printf("\n"); + + flint_rand_clear(state); + acb_mat_clear(tau); + _acb_vec_clear(z, g); + _acb_vec_clear(th, nbth * nbjet); + flint_free(pattern); + acb_theta_ctx_tau_clear(ctx_tau); + acb_theta_ctx_z_clear(ctx_z); + flint_cleanup(); + return 0; +} diff --git a/src/acb_theta/profile/p-ql_setup.c b/src/acb_theta/profile/p-ql_setup.c new file mode 100644 index 0000000000..653ae51b88 --- /dev/null +++ b/src/acb_theta/profile/p-ql_setup.c @@ -0,0 +1,96 @@ +/* + Copyright (C) 2025 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 3 of the License, or + (at your option) any later version. See . +*/ + +#include +#include +#include "profiler.h" +#include "ulong_extras.h" +#include "acb.h" +#include "acb_mat.h" +#include "acb_theta.h" + +static int usage(char * argv[]) +{ + flint_printf("usage: %s g nb_steps all skew\n", argv[0]); + return 1; +} + +int main(int argc, char * argv[]) +{ + flint_rand_t state; + slong g, n, prec, nb_steps, guard, skew; + int all, res; + slong nb = 2; + acb_mat_t tau; + acb_ptr zs, t; + acb_ptr rts, rts_all; + arb_ptr distances; + slong * easy_steps; + slong j; + + if (argc < 5) + { + return usage(argv); + } + + g = atol(argv[1]); + n = 1 << g; + prec = 100000; + nb_steps = atol(argv[2]); + all = atoi(argv[3]); + skew = atol(argv[4]); + + flint_rand_init(state); + acb_mat_init(tau, g, g); + zs = _acb_vec_init(2 * g); + t = _acb_vec_init(g); + rts = _acb_vec_init(6 * n * nb_steps); + rts_all = _acb_vec_init(6 * n * n); + distances = _arb_vec_init(2 * n); + easy_steps = flint_malloc(2 * sizeof(slong)); + + acb_siegel_randtest_compact(tau, state, 1, prec); + for (j = 0; j < g; j++) + { + acb_mul_si(acb_mat_entry(tau, j, g - 1), acb_mat_entry(tau, j, g - 1), skew, prec); + acb_mul_si(acb_mat_entry(tau, g - 1, j), acb_mat_entry(tau, g - 1, j), skew, prec); + } + acb_siegel_randtest_vec_reduced(zs + g, state, 1, tau, 1, prec); + acb_theta_eld_distances(distances, zs, 2, tau, 32); + + flint_printf("g = %wd, nb_steps = %wd, tau, z:\n", g, nb_steps); + acb_mat_printd(tau, 5); + _acb_vec_printd(zs, 2 * g, 5); + flint_printf("distances:\n"); + _arb_vec_printd(distances, 2 * n, 5); + + TIMEIT_START; + res = acb_theta_ql_setup(rts, rts_all, t, &guard, easy_steps, + zs, 2, tau, distances, nb_steps, all, prec); + TIMEIT_STOP; + + flint_printf("res = %wd, guard = %wd, easy_steps:", res, guard); + for (j = 0; j < nb; j++) + { + flint_printf(" %wd", easy_steps[j]); + } + flint_printf("\n"); + + flint_rand_clear(state); + acb_mat_clear(tau); + _acb_vec_clear(zs, 2 * g); + _acb_vec_clear(t, g); + _acb_vec_clear(rts, 6 * n * nb_steps); + _acb_vec_clear(rts_all, 6 * n * n); + _arb_vec_clear(distances, 2 * n); + flint_free(easy_steps); + flint_cleanup(); +} diff --git a/src/acb_theta/profile/p-siegel_reduce.c b/src/acb_theta/profile/p-siegel_reduce.c deleted file mode 100644 index fb935e7a6e..0000000000 --- a/src/acb_theta/profile/p-siegel_reduce.c +++ /dev/null @@ -1,88 +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 3 of the License, or - (at your option) any later version. See . -*/ - -#include -#include "profiler.h" -#include "acb.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_rand_init(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_rand_clear(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/profile/p-sum.c b/src/acb_theta/profile/p-sum.c new file mode 100644 index 0000000000..8a424e27ec --- /dev/null +++ b/src/acb_theta/profile/p-sum.c @@ -0,0 +1,114 @@ +/* + Copyright (C) 2025 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 3 of the License, or + (at your option) any later version. See . +*/ + +#include +#include "profiler.h" +#include "acb.h" +#include "arb_mat.h" +#include "acb_mat.h" +#include "acb_theta.h" + + +static int usage(char * argv[]) +{ + flint_printf("usage: %s g prec all_a all_b tilde skew\n", argv[0]); + return 1; +} + +int main(int argc, char * argv[]) +{ + flint_rand_t state; + slong g, n, prec, skew, nbth, j; + int all_a, all_b, tilde; + acb_mat_t tau; + arb_mat_t im; + acb_ptr z, th; + arb_t det; + acb_theta_ctx_tau_t ctx_tau; + acb_theta_ctx_z_t ctx_z; + arb_ptr distances; + slong lp = ACB_THETA_LOW_PREC; + + if (argc < 7) + { + return usage(argv); + } + + g = atol(argv[1]); + n = 1 << g; + prec = atol(argv[2]); + all_a = atol(argv[3]); + all_b = atoi(argv[4]); + tilde = atoi(argv[5]); + skew = atol(argv[6]); + nbth = (all_a ? n : 1) * (all_b ? n : 1); + + flint_rand_init(state); + acb_mat_init(tau, g, g); + arb_mat_init(im, g, g); + z = _acb_vec_init(g); + acb_theta_ctx_tau_init(ctx_tau, all_a, g); + acb_theta_ctx_z_init(ctx_z, g); + distances = _arb_vec_init(n); + th = _acb_vec_init(nbth); + arb_init(det); + + flint_printf("Siegel reduction: "); + TIMEIT_START; + acb_siegel_randtest_compact(tau, state, 1, prec + lp); + TIMEIT_STOP; + + for (j = 0; j < g; j++) + { + acb_mul_si(acb_mat_entry(tau, j, g - 1), acb_mat_entry(tau, j, g - 1), skew, prec + lp); + acb_mul_si(acb_mat_entry(tau, g - 1, j), acb_mat_entry(tau, g - 1, j), skew, prec + lp); + } + acb_siegel_randtest_vec_reduced(z, state, 1, tau, 1, prec + lp); + acb_mat_get_imag(im, tau); + arb_mat_det(det, im, prec + lp); + + flint_printf("g = %wd, prec = %wd, tau, z:\n", g, prec); + acb_mat_printd(tau, 5); + _acb_vec_printd(z, g, 5); + flint_printf("det(im tau): "); + arb_printd(det, 5); + flint_printf("\nComputing distances: "); + TIMEIT_START; + acb_theta_eld_distances(distances, z, 1, tau, lp); + TIMEIT_STOP; + + flint_printf("Setting contexts: "); + TIMEIT_START; + acb_theta_ctx_tau_set(ctx_tau, tau, prec + lp); + acb_theta_ctx_z_set(ctx_z, z, ctx_tau, prec + lp); + TIMEIT_STOP; + + flint_printf("Evaluating theta: "); + TIMEIT_START; + acb_theta_sum(th, ctx_z, 1, ctx_tau, distances, all_a, all_b, tilde, prec); + TIMEIT_STOP; + + acb_printd(&th[0], 5); + flint_printf("\n"); + acb_printd(&th[nbth - 1], 5); + flint_printf("\n"); + + flint_rand_clear(state); + acb_mat_clear(tau); + arb_mat_clear(im); + _acb_vec_clear(z, g); + acb_theta_ctx_tau_clear(ctx_tau); + acb_theta_ctx_z_clear(ctx_z); + _arb_vec_clear(distances, n); + _acb_vec_clear(th, nbth); + arb_clear(det); + flint_cleanup(); +} diff --git a/src/acb_theta/ql_a0.c b/src/acb_theta/ql_a0.c deleted file mode 100644 index 0c8de385b0..0000000000 --- a/src/acb_theta/ql_a0.c +++ /dev/null @@ -1,143 +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 3 of the License, or - (at your option) any later version. See . -*/ - -#include "acb.h" -#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 deleted file mode 100644 index 9728f415b0..0000000000 --- a/src/acb_theta/ql_a0_naive.c +++ /dev/null @@ -1,159 +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 3 of the License, or - (at your option) any later version. See . -*/ - -#include "acb.h" -#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 deleted file mode 100644 index 296ac51438..0000000000 --- a/src/acb_theta/ql_a0_nb_steps.c +++ /dev/null @@ -1,68 +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 3 of the License, or - (at your option) any later version. See . -*/ - -#include "arb.h" -#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 deleted file mode 100644 index 98f4142cc3..0000000000 --- a/src/acb_theta/ql_a0_split.c +++ /dev/null @@ -1,243 +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 3 of the License, or - (at your option) any later version. See . -*/ - -#include "arb_mat.h" -#include "acb.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 deleted file mode 100644 index 101906dc04..0000000000 --- a/src/acb_theta/ql_a0_steps.c +++ /dev/null @@ -1,378 +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 3 of the License, or - (at your option) any later version. See . -*/ - -#include "acb.h" -#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 deleted file mode 100644 index 68dae55267..0000000000 --- a/src/acb_theta/ql_all.c +++ /dev/null @@ -1,404 +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 3 of the License, or - (at your option) any later version. See . -*/ - -#include "acb.h" -#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_rand_init(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_rand_clear(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_rand_init(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_rand_clear(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_exact.c b/src/acb_theta/ql_exact.c new file mode 100644 index 0000000000..613b7b704d --- /dev/null +++ b/src/acb_theta/ql_exact.c @@ -0,0 +1,606 @@ +/* + Copyright (C) 2024 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 3 of the License, or + (at your option) any later version. See . +*/ + +#include "acb.h" +#include "arb_mat.h" +#include "acb_mat.h" +#include "acb_theta.h" + +/* Same as ql_exact when pattern suggests that summation must be used */ + +static void +acb_theta_ql_exact_sum(acb_ptr th, acb_srcptr zs, slong nb, const acb_mat_t tau, + arb_srcptr distances, int all, int shifted_prec, slong prec) +{ + slong g = acb_mat_nrows(tau); + slong n = 1 << g; + slong nba = (all ? n : 1); + acb_theta_ctx_tau_t ctx_tau; + acb_theta_ctx_z_struct * vec; + slong j; + + FLINT_ASSERT(nb >= 1); + + acb_theta_ctx_tau_init(ctx_tau, 1, g); + vec = acb_theta_ctx_z_vec_init(nb, g); + + acb_theta_ctx_tau_set(ctx_tau, tau, prec); + for (j = 0; j < nb; j++) + { + acb_theta_ctx_z_set(&vec[j], zs + j * g, ctx_tau, prec); + } + + if (shifted_prec) + { + for (j = 0; j < nb; j++) + { + acb_theta_sum(th + j * nba * n, &vec[j], 1, ctx_tau, + distances + j * n, 1, all, 1, prec); + } + } + else + { + /* distances are all set to zero; use one ellipsoid */ + acb_theta_sum(th, vec, nb, ctx_tau, distances, 1, all, 1, prec); + } + + acb_theta_ctx_tau_clear(ctx_tau); + acb_theta_ctx_z_vec_clear(vec, nb); +} + +/* Same as ql_exact when pattern suggests that duplication must be used */ + +static void +acb_theta_ql_exact_lower_dim(acb_ptr th, acb_srcptr zs, slong nb, + const acb_mat_t tau, arb_srcptr distances, slong s, + const slong * pattern, int all, int shifted_prec, slong prec) +{ + slong g = acb_mat_nrows(tau); + slong nba = 1 << (g - s); + slong n0 = 1 << s; + slong n = 1 << g; + slong nbth = (all ? n * n : n); + acb_ptr * new_zs; + acb_ptr * cofactors; + slong * nb_pts; + slong ** pts; + slong * fullprec; + arf_struct * err; + acb_ptr th0, z0s; + slong nb0, nbth0; + acb_mat_t tau0; + acb_t x; + slong j, a, index; + int res = 1; + + new_zs = flint_malloc(nb * nba * sizeof(acb_ptr)); + cofactors = flint_malloc(nb * nba * sizeof(acb_ptr)); + nb_pts = flint_malloc(nb * nba * sizeof(slong)); + err = flint_malloc(nb * nba * sizeof(arf_struct)); + pts = flint_malloc(nb * nba * sizeof(slong *)); + for (j = 0; j < nb * nba; j++) + { + arf_init(&err[j]); + } + fullprec = flint_malloc(nb * nba * sizeof(arf_struct)); + acb_mat_window_init(tau0, tau, 0, 0, s, s); + acb_init(x); + + nb0 = 1; /* always start with zero vector */ + for (j = 0; j < nb; j++) + { + for (a = 0; a < nba; a++) + { + if (res) + { + res = acb_theta_ql_lower_dim(&new_zs[j * nba + a], &cofactors[j * nba + a], + &pts[j * nba + a], &nb_pts[j * nba + a], &err[j * nba + a], &fullprec[j * nba + a], + zs + j * g, tau, distances + j * n, s, a, prec); + nb0 += nb_pts[j * nba + a]; + } + else + { + /* Initialize with length 0 to be able to free later. */ + /* Should not happen in tests */ + new_zs[j * nba + a] = _acb_vec_init(0); + cofactors[j * nba + a] = _acb_vec_init(0); + nb_pts[j * nba + a] = 0; + fullprec[j * nba + a] = 0; + pts[j * nba + a] = flint_malloc(0); + } + } + } + + if (!res) + { + /* Should not happen in tests */ + nb0 = 0; + } + z0s = _acb_vec_init(nb0 * s); + nbth0 = (all ? n0 * n0 : n0); + th0 = _acb_vec_init(nb0 * nbth0); + + if (res) + { + /* Put everything together */ + index = 1; /* always start with zero vector */ + for (j = 0; j < nb; j++) + { + for (a = 0; a < nba; a++) + { + _acb_vec_set(z0s + index * s, new_zs[j * nba + a], nb_pts[j * nba + a] * s); + index += nb_pts[j * nba + a]; + } + } + + /* Call acb_theta_ql_exact in dimension s */ + acb_theta_ql_exact(th0, z0s, nb0, tau0, pattern, all, shifted_prec, prec); + + /* Recombine into th */ + index = 1; /* do not use result from zero vector */ + _acb_vec_zero(th, nb * nbth); + for (j = 0; j < nb; j++) + { + for (a = 0; a < nba; a++) + { + acb_theta_ql_recombine(th + j * nbth, th0 + index * nbth0, cofactors[j * nba + a], + pts[j * nba + a], nb_pts[j * nba + a], &err[j * nba + a], fullprec[j * nba + a], + s, a, all, g, prec); + index += nb_pts[j * nba + a]; + } + } + } + else + { + /* Should not happen in tests */ + acb_theta_ql_exact_sum(th, zs, nb, tau, distances, all, shifted_prec, prec); + } + + /* Clear */ + for (j = 0; j < nb; j++) + { + for (a = 0; a < nba; a++) + { + _acb_vec_clear(new_zs[j * nba + a], nb_pts[j * nba + a] * s); + _acb_vec_clear(cofactors[j * nba + a], nb_pts[j * nba + a]); + } + } + flint_free(new_zs); + flint_free(cofactors); + flint_free(nb_pts); + for (j = 0; j < nb * nba; j++) + { + arf_clear(&err[j]); + flint_free(pts[j]); + } + flint_free(pts); + flint_free(err); + flint_free(fullprec); + acb_mat_window_clear(tau0); + _acb_vec_clear(z0s, nb0 * s); + _acb_vec_clear(th0, nb0 * nbth0); + acb_clear(x); +} + +/* Now assume that pattern suggests to perform duplication steps. Set up + input vector to acb_theta_ql_perform_steps using sum */ +/* We take advantage of the fact that some zs differ by a real value; this is + why we don't use acb_theta_ql_exact_sum here */ + +static void +acb_theta_ql_steps_input_sum(acb_ptr th_init, acb_srcptr zs, slong nb, + acb_srcptr t, const acb_mat_t tau, arb_srcptr distances, slong nb_steps, + const slong * easy_steps, slong prec) +{ + slong g = acb_mat_nrows(tau); + slong n = 1 << g; + acb_theta_ctx_tau_t ctx_tau; + acb_theta_ctx_z_struct * aux; + acb_theta_ctx_z_t ctxt; + acb_mat_t new_tau; + acb_ptr new_z; + arb_ptr d; + slong j; + + acb_theta_ctx_tau_init(ctx_tau, 1, g); + aux = acb_theta_ctx_z_vec_init(3, g); + acb_mat_init(new_tau, g, g); + new_z = _acb_vec_init(g); + d = _arb_vec_init(n); + if (easy_steps[0] < nb_steps) + { + acb_theta_ctx_z_init(ctxt, g); + } + + acb_mat_scalar_mul_2exp_si(new_tau, tau, nb_steps); + acb_theta_ctx_tau_set(ctx_tau, new_tau, prec); + if (easy_steps[0] < nb_steps) + { + _acb_vec_scalar_mul_2exp_si(new_z, t, g, nb_steps); + acb_theta_ctx_z_set(ctxt, new_z, ctx_tau, prec); + } + + for (j = 0; j < nb; j++) + { + _acb_vec_scalar_mul_2exp_si(new_z, zs + j * g, g, nb_steps); + acb_theta_ctx_z_set(&aux[0], new_z, ctx_tau, prec); + _arb_vec_scalar_mul_2exp_si(d, distances + j * n, n, nb_steps); + if (easy_steps[j] == nb_steps) + { + acb_theta_sum(th_init + 3 * n * j, aux, 1, ctx_tau, d, 1, 0, 1, prec); + } + else + { + acb_theta_ctx_z_add_real(&aux[1], &aux[0], ctxt, prec); + acb_theta_ctx_z_add_real(&aux[2], &aux[1], ctxt, prec); + if (j == 0) + { + acb_theta_sum(th_init + 3 * n * j, aux, 3, ctx_tau, d, 1, 0, 1, prec); + } + else + { + acb_theta_sum(th_init + 3 * n * j + n, aux + 1, 2, ctx_tau, d, 1, 0, 1, prec); + } + } + } + + acb_theta_ctx_tau_clear(ctx_tau); + acb_theta_ctx_z_vec_clear(aux, 3); + acb_mat_clear(new_tau); + _acb_vec_clear(new_z, g); + _arb_vec_clear(d, n); + if (easy_steps[0] < nb_steps) + { + acb_theta_ctx_z_clear(ctxt); + } +} + +/* Still assume that pattern suggests to perform duplication steps. Set up + input vector to acb_theta_ql_perform_steps using dimension-lowering */ +/* We don't take advantage of the fact that some zs differ by a real value, + in contrast with acb_theta_ql_steps_input_sum */ + +static void +acb_theta_ql_steps_input_lower_dim(acb_ptr th_init, acb_srcptr zs, slong nb, + acb_srcptr t, const acb_mat_t tau, arb_srcptr distances, slong split, + slong nb_steps, const slong * pattern, const slong * easy_steps, slong prec) +{ + slong g = acb_mat_nrows(tau); + slong n = 1 << g; + acb_ptr new_th, new_z; + acb_mat_t new_tau; + arb_ptr new_distances; + slong new_nb, add; + slong * new_pattern; + slong j, k; + + /* Count number of vectors */ + new_nb = 0; + for (j = 0; j < nb; j++) + { + if (j == 0 && easy_steps[j] < nb_steps) + { + new_nb += 3; + } + else if (easy_steps[j] < nb_steps) + { + new_nb += 2; + } + else + { + new_nb += 1; + } + } + + acb_mat_init(new_tau, g, g); + new_th = _acb_vec_init(n * new_nb); + new_z = _acb_vec_init(g * new_nb); + new_distances = _arb_vec_init(n * new_nb); + new_pattern = flint_malloc(split * sizeof(slong)); + + /* Set up input of ql_exact_lower_dim */ + for (j = 0; j < split; j++) + { + new_pattern[j] = FLINT_MAX(0, pattern[j] - nb_steps); + } + new_nb = 0; + for (j = 0; j < nb; j++) + { + if (j == 0 && easy_steps[j] < nb_steps) + { + _acb_vec_zero(new_z + new_nb * g, g); + _acb_vec_set(new_z + (new_nb + 1) * g, t, g); + _acb_vec_scalar_mul_2exp_si(new_z + (new_nb + 2) * g, t, g, 1); + add = 3; + } + else if (easy_steps[j] < nb_steps) + { + _acb_vec_add(new_z + new_nb * g, zs + j * g, t, g, prec); + _acb_vec_add(new_z + (new_nb + 1) * g, new_z + new_nb * g, t, g, prec); + add = 2; + } + else + { + _acb_vec_set(new_z + new_nb * g, zs + j * g, g); + add = 1; + } + for (k = 0; k < add; k++) + { + _arb_vec_set(new_distances + (new_nb + k) * n, distances + j * n, n); + } + new_nb += add; + } + + acb_mat_scalar_mul_2exp_si(new_tau, tau, nb_steps); + _acb_vec_scalar_mul_2exp_si(new_z, new_z, new_nb * g, nb_steps); + _arb_vec_scalar_mul_2exp_si(new_distances, new_distances, new_nb * n, nb_steps); + + /* Call ql_exact_lower_dim */ + acb_theta_ql_exact_lower_dim(new_th, new_z, new_nb, new_tau, new_distances, + split, new_pattern, 0, 1, prec); + + /* Set up th_init from computed data */ + new_nb = 0; + for (j = 0; j < nb; j++) + { + if (j == 0 && easy_steps[j] < nb_steps) + { + _acb_vec_set(th_init, new_th, 3 * n); + new_nb += 3; + } + else if (easy_steps[j] < nb_steps) + { + _acb_vec_set(th_init + 3 * j * n + n, new_th + new_nb * n, 2 * n); + new_nb += 2; + } + else + { + _acb_vec_set(th_init + 3 * j * n, new_th + new_nb * n, n); + new_nb += 1; + } + } + + acb_mat_clear(new_tau); + _acb_vec_clear(new_th, new_nb * n); + _acb_vec_clear(new_z, new_nb * g); + _arb_vec_clear(new_distances, new_nb * n); + flint_free(new_pattern); +} + +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, 0, prec); + acb_theta_agm_sqrt(res, res, rts, n, prec); +} + +static void +acb_theta_ql_perform_steps(acb_ptr th, acb_ptr th_init, acb_srcptr rts, + acb_srcptr rts_all, slong nb, slong nb_steps, arb_srcptr distances, + const slong * easy_steps, int all, slong g, slong prec) +{ + slong n = 1 << g; + acb_ptr th_next, th_temp, aux; + arb_ptr d0, d; + slong j, k, a; + + FLINT_ASSERT(nb_steps >= 1); + + th_next = _acb_vec_init(3 * n * nb); + d = _arb_vec_init(n); + d0 = _arb_vec_init(n); + if (all) + { + aux = _acb_vec_init(n * n); + } + + for (k = nb_steps - 1; k >= 0; k--) + { + _arb_vec_scalar_mul_2exp_si(d0, distances, n, k + 1); + for (j = 0; j < nb; j++) + { + _arb_vec_scalar_mul_2exp_si(d, distances + j * n, n, k + 1); + if (k == 0 && all && easy_steps[j] > k) + { + /* Compute all theta_ab with an easy step */ + acb_theta_agm_mul_tight(th + j * n * n, th_init, + th_init + 3 * j * n, d0, d, g, 1, prec); + acb_theta_agm_sqrt(th + j * n * n, th + j * n * n, + rts_all + j * n * n, n * n, prec); + } + else if (k == 0 && all) + { + /* Compute all theta_ab with a harder step */ + /* Store theta_ab(2t, tau) in aux */ + acb_theta_agm_mul_tight(aux, th_init, + th_init + 3 * j * n + 2 * n, d0, d, g, 1, prec); + acb_theta_agm_sqrt(aux, aux, rts_all + j * n * n, n * n, prec); + acb_theta_agm_mul_tight(th + j * n * n, th_init + n, + th_init + 3 * j * n + n, d0, d, g, 1, prec); + for (a = 0; a < n * n; a++) + { + acb_div(&th[j * n * n + a], &th[j * n * n + a], + &aux[a], prec); + } + } + else if (easy_steps[j] > k) + { + /* theta(z, tau)^2 = sum of theta(0, 2tau) theta(2z, 2tau) */ + acb_theta_ql_step_1(th_next + 3 * j * n, th_init, + th_init + 3 * j * n, rts + j * (3 * n * nb_steps) + k * (3 * n), + d0, d, g, prec); + } + else + { + if (k > 0) + { + /* theta(z + t, tau)^2 = sum of theta(0, 2tau) theta(2z + 2t, 2tau) */ + acb_theta_ql_step_1(th_next + 3 * j * n + n, th_init, + th_init + 3 * j * n + n, + rts + j * (3 * n * nb_steps) + k * (3 * n) + n, d0, d, g, prec); + } + /* theta(z + 2t, tau)^2 = sum of theta(0, 2tau) theta(2z + 4t, 2tau) */ + acb_theta_ql_step_1(th_next + 3 * j * n + 2 * n, th_init, + th_init + 3 * j * n + 2 * n, + rts + j * (3 * n * nb_steps) + k * (3 * n) + 2 * n, d0, d, g, prec); + if ((easy_steps[j] == k) || (j == 0)) + { + /* theta(z, tau) theta(z + 2t, tau) + = sum of theta(2t, 2tau) theta(2z + 2t, 2tau) */ + acb_theta_agm_mul_tight(th_next + 3 * j * n, th_init + n, + th_init + 3 * j * n + n, d0, d, g, 0, prec); + for (a = 0; a < n; a++) + { + acb_div(&th_next[3 * j * n + a], &th_next[3 * j * n + a], + &th_next[3 * j * n + 2 * n + a], prec); + } + } + } + } + /* Swap th_next and th_init */ + th_temp = th_init; + th_init = th_next; + th_next = th_temp; + } + + if (!all) + { + for (j = 0; j < nb; j++) + { + _acb_vec_set(th + j * n, th_init + 3 * j * n, n); + } + } + + if (nb_steps % 2 == 0) + { + _acb_vec_clear(th_next, 3 * n * nb); + } + else + { + _acb_vec_clear(th_init, 3 * n * nb); + } + _arb_vec_clear(d, n); + _arb_vec_clear(d0, n); + if (all) + { + _acb_vec_clear(aux, n * n); + } +} + +static void +acb_theta_ql_exact_steps(acb_ptr th, acb_srcptr zs, slong nb, + const acb_mat_t tau, arb_srcptr distances, slong split, + const slong * pattern, int all, slong prec) +{ + slong g = acb_mat_nrows(tau); + slong n = 1 << g; + slong nb_steps = pattern[g - 1]; + acb_ptr rts, th_init, t, rts_all; + slong * easy_steps; + acb_mat_t new_tau; + slong guard; + int res; + + FLINT_ASSERT(nb_steps > 0); + + rts = _acb_vec_init(3 * n * nb * nb_steps); + t = _acb_vec_init(g); + th_init = _acb_vec_init(3 * n * nb); + easy_steps = flint_malloc(nb * sizeof(slong)); + acb_mat_init(new_tau, g, g); + if (all) + { + rts_all = _acb_vec_init(nb * n * n); + } + + res = acb_theta_ql_setup(rts, rts_all, t, &guard, easy_steps, zs, nb, tau, distances, + nb_steps, all, prec); + + if (res) + { + if (split > 0) + { + acb_theta_ql_steps_input_lower_dim(th_init, zs, nb, t, tau, + distances, split, nb_steps, pattern, easy_steps, prec + guard); + } + else + { + acb_theta_ql_steps_input_sum(th_init, zs, nb, t, tau, distances, + nb_steps, easy_steps, prec + guard); + } + + acb_theta_ql_perform_steps(th, th_init, rts, rts_all, nb, nb_steps, + distances, easy_steps, all, g, prec + guard); + } + else + { + /* Setup or intput did not succeed, fall back to summation */ + /* Should not happen in tests */ + acb_theta_ql_exact_sum(th, zs, nb, tau, distances, all, 1, prec); + } + + _acb_vec_clear(rts, 3 * n * nb * nb_steps); + _acb_vec_clear(t, g); + _acb_vec_clear(th_init, 3 * n * nb); + flint_free(easy_steps); + acb_mat_clear(new_tau); + if (all) + { + _acb_vec_clear(rts_all, nb * n * n); + } +} + +void +acb_theta_ql_exact(acb_ptr th, acb_srcptr zs, slong nb, const acb_mat_t tau, + const slong * pattern, int all, int shifted_prec, slong prec) +{ + slong g = acb_mat_nrows(tau); + slong n = 1 << g; + arb_ptr distances; + slong nb_steps, split; + slong lp = ACB_THETA_LOW_PREC; + + FLINT_ASSERT(nb >= 1); + FLINT_ASSERT(_acb_vec_is_zero(zs, g)); + + distances = _arb_vec_init(n * nb); + + nb_steps = pattern[g - 1]; + split = g - 1; + while ((split > 0) && (pattern[split - 1] <= nb_steps)) + { + split --; + } + + if (nb_steps > 0 || shifted_prec) + { + acb_theta_eld_distances(distances, zs, nb, tau, lp); + } + + if (nb_steps == 0 && split == 0) + { + acb_theta_ql_exact_sum(th, zs, nb, tau, distances, all, shifted_prec, prec); + } + else if (nb_steps == 0 && split > 0) + { + acb_theta_ql_exact_lower_dim(th, zs, nb, tau, distances, split, + pattern, all, shifted_prec, prec); + } + else + { + acb_theta_ql_exact_steps(th, zs, nb, tau, distances, split, pattern, all, prec); + } + + _arb_vec_clear(distances, n * nb); +} diff --git a/src/acb_theta/ql_jet.c b/src/acb_theta/ql_jet.c new file mode 100644 index 0000000000..ba2916d320 --- /dev/null +++ b/src/acb_theta/ql_jet.c @@ -0,0 +1,64 @@ +/* + Copyright (C) 2025 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 3 of the License, or + (at your option) any later version. See . +*/ + +#include "acb.h" +#include "acb_mat.h" +#include "acb_theta.h" + +/* We make a choice between direct summation or ql_jet_fd */ +/* See p-ql_jet_fd */ + +void +acb_theta_ql_jet(acb_ptr th, acb_srcptr zs, slong nb, + const acb_mat_t tau, slong ord, int all, slong prec) +{ + slong g = acb_mat_nrows(tau); + slong guard = ACB_THETA_LOW_PREC; + slong * pattern; + acb_theta_ctx_tau_t ctx_tau; + acb_theta_ctx_z_struct * vec; + slong j; + int use_sum = 1; + + pattern = flint_malloc(g * sizeof(slong)); + + acb_theta_ql_nb_steps(pattern, tau, 0, prec); + if (pattern[0] >= 8 + ord + || (g >= 2 && pattern[1] >= 6 + ord) + || (g >= 3 && pattern[2] >= 7) + || ord == 0) + { + use_sum = 0; + } + + if (use_sum) + { + acb_theta_ctx_tau_init(ctx_tau, 0, g); + vec = acb_theta_ctx_z_vec_init(nb, g); + + acb_theta_ctx_tau_set(ctx_tau, tau, prec + guard); + for (j = 0; j < nb; j++) + { + acb_theta_ctx_z_set(&vec[j], zs + j * g, ctx_tau, prec + guard); + } + + acb_theta_sum_jet(th, vec, nb, ctx_tau, ord, 1, all, prec); + + acb_theta_ctx_tau_clear(ctx_tau); + acb_theta_ctx_z_vec_clear(vec, nb); + } + else + { + acb_theta_ql_jet_fd(th, zs, nb, tau, ord, all, prec); + } + + flint_free(pattern); +} diff --git a/src/acb_theta/jet_error_bounds.c b/src/acb_theta/ql_jet_error.c similarity index 84% rename from src/acb_theta/jet_error_bounds.c rename to src/acb_theta/ql_jet_error.c index 63b2005ad6..65213cb3f5 100644 --- a/src/acb_theta/jet_error_bounds.c +++ b/src/acb_theta/ql_jet_error.c @@ -15,7 +15,7 @@ #include "acb_theta.h" void -acb_theta_jet_error_bounds(arb_ptr err, acb_srcptr z, const acb_mat_t tau, +acb_theta_ql_jet_error(arb_ptr err, acb_srcptr z, const acb_mat_t tau, acb_srcptr dth, slong ord, slong prec) { slong g = acb_mat_nrows(tau); @@ -23,6 +23,7 @@ acb_theta_jet_error_bounds(arb_ptr err, acb_srcptr z, const acb_mat_t tau, arb_mat_t tau_err; arb_ptr z_err; arb_t e, f; + arf_t half; slong nb = acb_theta_jet_nb(ord, g); slong nb_dth = acb_theta_jet_nb(ord + 2, g); slong * tups; @@ -34,19 +35,31 @@ acb_theta_jet_error_bounds(arb_ptr err, acb_srcptr z, const acb_mat_t tau, z_err = _arb_vec_init(g); arb_init(e); arb_init(f); + arf_init(half); tups = flint_malloc(nb * g * sizeof(slong)); new_tups = flint_malloc(g * sizeof(slong)); /* Get input errors on z, tau */ + arf_set_si(half, 1); + arf_mul_2exp_si(half, half, -1); for (l = 0; l < g; l++) { for (m = l; m < g; m++) { + arb_zero(e); 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); + arb_zero(e); + arb_zero(f); + /* Use that theta is 1-periodic in re(z) */ + arf_set_mag(arb_midref(e), arb_radref(acb_imagref(&z[l]))); + arf_set_mag(arb_midref(f), arb_radref(acb_realref(&z[l]))); + arf_min(arb_midref(f), arb_midref(f), half); + arb_sqr(e, e, prec); + arb_sqr(f, f, prec); + arb_add(e, e, f, prec); + arb_sqrt(&z_err[l], e, prec); } /* We need order ord + 2 to use the heat equation. */ @@ -111,6 +124,7 @@ acb_theta_jet_error_bounds(arb_ptr err, acb_srcptr z, const acb_mat_t tau, _arb_vec_clear(z_err, g); arb_clear(e); arb_clear(f); + arf_clear(half); flint_free(tups); flint_free(new_tups); } diff --git a/src/acb_theta/ql_jet_fd.c b/src/acb_theta/ql_jet_fd.c new file mode 100644 index 0000000000..bfc5dc8b19 --- /dev/null +++ b/src/acb_theta/ql_jet_fd.c @@ -0,0 +1,453 @@ +/* + Copyright (C) 2025 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 3 of the License, or + (at your option) any later version. See . +*/ + +#include +#include "ulong_extras.h" +#include "acb.h" +#include "arb_mat.h" +#include "acb_mat.h" +#include "acb_dft.h" +#include "acb_theta.h" + +void +acb_theta_ql_inexact(acb_ptr th, acb_srcptr zs, slong nb, const acb_mat_t tau, + acb_srcptr dth, int all, slong prec) +{ + slong g = acb_mat_nrows(tau); + slong n = 1 << g; + slong nbth = (all ? n * n : n); + slong nbjet = acb_theta_jet_nb(2, g); + slong lp = ACB_THETA_LOW_PREC; + acb_mat_t new_tau; + acb_ptr new_zs, new_th; + slong * pattern; + arb_ptr err; + arb_mat_t cho, yinv; + arb_ptr y, w; + arb_t u, pi; + slong j, k; + int add_zero; + + FLINT_ASSERT(nb > 0); + + add_zero = !_acb_vec_is_zero(zs, g); + acb_mat_init(new_tau, g, g); + new_zs = _acb_vec_init((nb + add_zero) * g); + new_th = _acb_vec_init((nb + add_zero) * nbth); + pattern = flint_malloc(g * sizeof(slong)); + err = _arb_vec_init(nb * nbth); + arb_mat_init(cho, g, g); + arb_mat_init(yinv, g, g); + y = _arb_vec_init(g); + w = _arb_vec_init(g); + arb_init(u); + arb_init(pi); + + /* Strip tau, z of error bounds */ + for (j = 0; j < g; j++) + { + for (k = j; k < g; k++) + { + acb_get_mid(acb_mat_entry(new_tau, j, k), acb_mat_entry(tau, j, k)); + acb_set(acb_mat_entry(new_tau, k, j), acb_mat_entry(new_tau, j, k)); + } + } + for (j = 0; j < nb * g; j++) + { + acb_get_mid(&new_zs[g * add_zero + j], &zs[j]); + } + + /* Get error bounds */ + for (j = 0; j < nb; j++) + { + for (k = 0; k < nbth; k++) + { + acb_theta_ql_jet_error(err + j * nbth + k, zs + j * g, tau, + dth + j * (nbth * nbjet) + k * nbjet, 0, lp); + } + } + + /* Call ql_exact */ + /* Todo: adjust current working precision so that 2^(-prec) is roughly err ? */ + acb_theta_ql_nb_steps(pattern, new_tau, 0, prec); + acb_theta_ql_exact(new_th, new_zs, nb + add_zero, new_tau, pattern, all, 0, prec); + _acb_vec_set(th, new_th + add_zero * nbth, nb * nbth); + + /* Multiply by exp(pi y^T Yinv y) */ + acb_siegel_cho_yinv(cho, yinv, tau, prec); + arb_const_pi(pi, prec); + for (j = 0; j < nb; j++) + { + _acb_vec_get_imag(y, new_zs + (add_zero + j) * g, g); + arb_mat_vector_mul_col(w, yinv, y, prec); + arb_dot(u, NULL, 0, y, 1, w, 1, g, prec); + arb_mul(u, u, pi, prec); + arb_exp(u, u, prec); + _acb_vec_scalar_mul_arb(th + j * nbth, th + j * nbth, nbth, u, prec); + } + + /* Add error bounds */ + for (j = 0; j < nb * nbth; j++) + { + acb_add_error_arb(&th[j], &err[j]); + } + + acb_mat_clear(new_tau); + _acb_vec_clear(new_zs, (nb + add_zero) * g); + _acb_vec_clear(new_th, (nb + add_zero) * nbth); + flint_free(pattern); + _arb_vec_clear(err, nb * nbth); + arb_mat_clear(cho); + arb_mat_clear(yinv); + _arb_vec_clear(y, g); + _arb_vec_clear(w, g); + arb_clear(u); + arb_clear(pi); +} + +/* 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 */ + +static void +acb_theta_jet_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 nbjet = acb_theta_jet_nb(ord, g); + slong nbaux = n_pow(ord + 1, g); + slong lp = ACB_THETA_LOW_PREC; + acb_ptr aux; + arb_t t, e; + slong * tups; + slong * cyc; + slong j, i, l; + slong k; + + aux = _acb_vec_init(nbaux); + arb_init(t); + arb_init(e); + tups = flint_malloc(g * nbjet * sizeof(slong)); + cyc = flint_malloc(g * sizeof(slong)); + + for (j = 0; j < g; j++) + { + cyc[j] = ord + 1; + } + acb_dft_prod(aux, val, cyc, g, prec); + arb_set_si(t, nbaux); + _acb_vec_scalar_div_arb(aux, aux, nbaux, 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 < nbjet; j++) + { + l = 0; + for (i = 0; i < g; i++) + { + l *= ord + 1; + 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, nbaux); + arb_clear(t); + arb_clear(e); + flint_free(tups); + flint_free(cyc); +} + +static void +acb_theta_jet_finite_diff_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; + 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 + where b = ord + 1 */ + arb_set_si(x, 2 * g); + arb_inv(x, x, lp); + arb_root_ui(x, x, ord + 1, lp); + arb_mul(x, x, rho, lp); + + arb_pow_ui(y, rho, 2 * ord + 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, ord + 1, 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); +} + +static void +acb_theta_ql_jet_exact(acb_ptr th, acb_srcptr zs, slong nb, const acb_mat_t tau, + acb_srcptr dth, slong ord, int all, slong prec) +{ + slong g = acb_mat_nrows(tau); + slong n = 1 << g; + slong hprec; + slong nbth = (all ? n * n : n); + slong nbaux = n_pow(ord + 1, g); + slong nbjet = acb_theta_jet_nb(ord, g); + slong nbjet_2 = acb_theta_jet_nb(ord + 2, g); + slong nbjet_0 = acb_theta_jet_nb(2, g); + arb_ptr c, rho, eps, err; + arb_t t; + arf_t e; + acb_ptr zetas, new_zs, new_th, new_dth, dft; + slong j, k, kmod, l; + int res = 1; + + c = _arb_vec_init(nb); + rho = _arb_vec_init(nb); + eps = _arb_vec_init(nb); + err = _arb_vec_init(nb); + arb_init(t); + arf_init(e); + zetas = _acb_vec_init(ord + 1); + new_zs = _acb_vec_init(g * nb * nbaux); + new_th = _acb_vec_init(nbth * nb * nbaux); + new_dth = _acb_vec_init(nb * nbth * nbaux * nbjet_0); + dft = _acb_vec_init(nbaux); + + /* Get bounds and high precision, fail if too large */ + hprec = prec; + for (j = 0; (j < nb) && res; j++) + { + acb_theta_ql_local_bound(&c[j], &rho[j], zs + j * g, tau, ord); + acb_theta_jet_finite_diff_radius(arb_midref(&eps[j]), arb_midref(&err[j]), + &c[j], &rho[j], ord, g, prec); + + arb_log_base_ui(t, &eps[j], 2, ACB_THETA_LOW_PREC); + arb_neg(t, t); + + /* we expect that the second bound holds if finite, but still check */ + res = arb_is_finite(t) && arf_cmpabs_2exp_si(arb_midref(t), 20) <= 0; + if (res) + { + hprec = FLINT_MAX(hprec, prec + ord * (arf_get_si(arb_midref(t), ARF_RND_CEIL) + g)); + } + } + + /* Fill in new_zs */ + _acb_vec_unit_roots(zetas, ord + 1, ord + 1, hprec); + for (j = 0; (j < nb) && res; j++) + { + for (k = 0; k < nbaux; k++) + { + kmod = k; + for (l = g - 1; l >= 0; l--) + { + acb_set(&new_zs[j * g * nbaux + k * g + l], &zetas[kmod % (ord + 1)]); + kmod = kmod / (ord + 1); + } + _acb_vec_scalar_mul_arb(new_zs + j * g * nbaux + k * g, + new_zs + j * g * nbaux + k * g, g, &eps[j], hprec); + /* eps[j] should be much smaller than 2^(-prec / (ord + 1)), but still check it. */ + for (l = 0; (l < g) && res; l++) + { + acb_get_abs_ubound_arf(e, &new_zs[j * g * nbaux + k * g + l], ACB_THETA_LOW_PREC); + res = arf_cmp_2exp_si(e, -floor(prec / (ord + 1))) <= 0; + } + _acb_vec_add(new_zs + j * g * nbaux + k * g, + new_zs + j * g * nbaux + k * g, zs + j * g, g, hprec); + } + } + if (res) + { + /* Call ql_inexact (as zetas is inexact). We can reuse entries from dth + here, because all the auxiliary points are contained in the vector + zs where dth was initially computed. */ + /* If ord = 1 or ord = 3, then zetas is exact. We don't make a special + case because ql_inexact doesn't have a lot of overhead */ + for (j = 0; j < nb; j++) + { + for (k = 0; k < nbaux; k++) + { + for (l = 0; l < nbth; l++) + { + _acb_vec_set(new_dth + j * nbth * nbaux * nbjet_0 + + k * nbth * nbjet_0 + l * nbjet_0, + dth + j * nbth * nbjet_2 + l * nbjet_2, + nbjet_0); + } + } + } + acb_theta_ql_inexact(new_th, new_zs, nb * nbaux, tau, new_dth, all, hprec); + + /* Make finite differences */ + for (j = 0; j < nb; j++) + { + for (k = 0; k < nbth; k++) + { + for (l = 0; l < nbaux; l++) + { + acb_set(&dft[l], &new_th[j * nbth * nbaux + l * nbth + k]); + } + acb_theta_jet_finite_diff(th + j * nbth * nbjet + k * nbjet, + arb_midref(&eps[j]), arb_midref(&err[j]), &rho[j], dft, ord, g, hprec); + } + } + } + else + { + /* Should not happen in tests */ + _acb_vec_indeterminate(th, nb * nbth * nbjet); + } + + _arb_vec_clear(c, nb); + _arb_vec_clear(rho, nb); + _arb_vec_clear(eps, nb); + _arb_vec_clear(err, nb); + arb_clear(t); + arf_clear(e); + _acb_vec_clear(zetas, ord + 1); + _acb_vec_clear(new_zs, g * nb * nbaux); + _acb_vec_clear(new_th, nbth * nb * nbaux); + _acb_vec_clear(new_dth, nb * nbth * nbaux * nbjet_0); + _acb_vec_clear(dft, nbaux); +} + +void +acb_theta_ql_jet_fd(acb_ptr th, acb_srcptr zs, slong nb, + const acb_mat_t tau, slong ord, int all, slong prec) +{ + slong g = acb_mat_nrows(tau); + slong n = 1 << g; + slong nbth = (all ? n * n : n); + slong nbjet = acb_theta_jet_nb(ord, g); + slong nbjet_2 = acb_theta_jet_nb(ord + 2, g); + acb_ptr new_zs; + acb_theta_ctx_tau_t ctx_tau; + acb_theta_ctx_z_struct * vec; + acb_ptr dth; + arb_ptr err; + acb_mat_t tau_mid; + arf_t e; + slong j, k, lp; + int res = 0; + + if (nb <= 0) + { + return; + } + + new_zs = _acb_vec_init(nb * g); + acb_theta_ctx_tau_init(ctx_tau, 0, g); + vec = acb_theta_ctx_z_vec_init(nb, g); + dth = _acb_vec_init(nb * nbjet_2 * nbth); + err = _arb_vec_init(nb * nbjet * nbth); + acb_mat_init(tau_mid, g, g); + arf_init(e); + + /* Compute low-precision derivatives of theta */ + /* Add an error of 2^(-prec/(ord + 1)) around z, so that the auxiliary + evaluation points used in finite differences are contained in new_zs */ + arf_one(e); + arf_mul_2exp_si(e, e, -floor(prec / (ord + 1))); + _acb_vec_set(new_zs, zs, nb * g); + for (j = 0; j < nb * g; j++) + { + acb_add_error_arf(&new_zs[j], e); + } + for (lp = 8; lp <= 64; lp *= 2) + { + acb_theta_ctx_tau_set(ctx_tau, tau, lp + ACB_THETA_LOW_PREC); + for (j = 0; j < nb; j++) + { + acb_theta_ctx_z_set(&vec[j], zs + j * g, ctx_tau, lp + ACB_THETA_LOW_PREC); + } + acb_theta_sum_jet(dth, vec, nb, ctx_tau, ord + 2, 1, all, lp); + if (_acb_vec_is_finite(dth, nb * nbth * nbjet_2)) + { + res = 1; + break; + } + } + + /* Get error bounds */ + lp = ACB_THETA_LOW_PREC; + for (j = 0; j < nb; j++) + { + for (k = 0; k < nbth; k++) + { + acb_theta_ql_jet_error(err + j * nbth * nbjet + k * nbjet, zs + j * g, + tau, dth + j * nbth * nbjet_2 + k * nbjet_2, ord, lp); + } + } + /* Todo: adjust current working precision so that 2^(-prec) is roughly err ? */ + + if (res && ord == 0) + { + /* Just call ql_inexact */ + acb_theta_ql_inexact(th, zs, nb, tau, dth, all, prec); + } + else if (res) + { + /* We want to call ql_jet_exact. Strip tau, z of error bounds */ + 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_set(acb_mat_entry(tau_mid, k, j), acb_mat_entry(tau_mid, j, k)); + } + } + for (j = 0; j < nb * g; j++) + { + acb_get_mid(&new_zs[j], &zs[j]); + } + + /* Call ql_jet_exact and add error */ + acb_theta_ql_jet_exact(th, new_zs, nb, tau_mid, dth, ord, all, prec); + for (j = 0; j < nb * nbth * nbjet; j++) + { + acb_add_error_arb(&th[j], &err[j]); + } + } + else + { + /* Should not happen in tests */ + _acb_vec_indeterminate(th, nb * nbth * nbjet); + } + + _acb_vec_clear(new_zs, nb * g); + acb_theta_ctx_tau_clear(ctx_tau); + acb_theta_ctx_z_vec_clear(vec, nb); + _acb_vec_clear(dth, nb * nbjet_2 * nbth); + _arb_vec_clear(err, nb * nbjet * nbth); + acb_mat_clear(tau_mid); + arf_clear(e); +} diff --git a/src/acb_theta/jet_ql_bounds.c b/src/acb_theta/ql_local_bound.c similarity index 80% rename from src/acb_theta/jet_ql_bounds.c rename to src/acb_theta/ql_local_bound.c index bad0e8effe..bb5e09b106 100644 --- a/src/acb_theta/jet_ql_bounds.c +++ b/src/acb_theta/ql_local_bound.c @@ -18,17 +18,17 @@ 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) +acb_theta_local_bound_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 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(yinv, g, g); arb_mat_init(cho, g, g); y = _arb_vec_init(g); w = _arb_vec_init(g); @@ -36,8 +36,7 @@ acb_theta_jet_ql_ci(arb_t c0, arb_t c1, arb_t c2, acb_srcptr z, const acb_mat_t arb_init(s); _acb_vec_get_imag(y, z, g); - acb_siegel_yinv(Yinv, tau, lp); - acb_siegel_cho(cho, tau, lp); + acb_siegel_cho_yinv(cho, yinv, tau, lp); /* c0 is 2^g \prod_{i=1}^g (1 + 2/\sqrt{\gamma_i}) */ arb_one(c0); @@ -51,15 +50,15 @@ acb_theta_jet_ql_ci(arb_t c0, arb_t c1, arb_t c2, acb_srcptr z, const acb_mat_t /* 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_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_cho(cho, yinv, lp); arb_mat_transpose(cho, cho); for (k = 0; k < g; k++) { @@ -75,7 +74,7 @@ acb_theta_jet_ql_ci(arb_t c0, arb_t c1, arb_t c2, acb_srcptr z, const acb_mat_t arb_nonnegative_part(c2, c2); arb_sqrt(c2, c2, lp); - arb_mat_clear(Yinv); + arb_mat_clear(yinv); arb_mat_clear(cho); _arb_vec_clear(y, g); _arb_vec_clear(w, g); @@ -85,10 +84,10 @@ acb_theta_jet_ql_ci(arb_t c0, arb_t c1, arb_t c2, acb_srcptr z, const acb_mat_t /* 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 */ + order ord. We always force rho <= 1 and c >= 1; this helps in ql_jet_fd */ void -acb_theta_jet_ql_bounds(arb_t c, arb_t rho, acb_srcptr z, const acb_mat_t tau, slong ord) +acb_theta_ql_local_bound(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; @@ -102,7 +101,7 @@ acb_theta_jet_ql_bounds(arb_t c, arb_t rho, acb_srcptr z, const acb_mat_t tau, s arf_init(x); /* Get ci */ - acb_theta_jet_ql_ci(c0, c1, c2, z, tau); + acb_theta_local_bound_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); @@ -118,6 +117,10 @@ acb_theta_jet_ql_bounds(arb_t c, arb_t rho, acb_srcptr z, const acb_mat_t tau, s arb_mul_2exp_si(t, t, 2); arb_div(rho, rho, t, lp); + /* Set rho = min(rho, 1) */ + arb_set_si(t, 1); + arb_min(rho, rho, t, lp); + /* Set c to corresponding bound */ arb_mul(c, c2, rho, lp); arb_add(c, c, c1, lp); @@ -125,6 +128,10 @@ acb_theta_jet_ql_bounds(arb_t c, arb_t rho, acb_srcptr z, const acb_mat_t tau, s arb_exp(c, c, lp); arb_mul(c, c, c0, lp); + /* Set c = max(c, 1) */ + arb_set_si(t, 1); + arb_max(c, c, t, lp); + arb_clear(t); arb_clear(c0); arb_clear(c1); diff --git a/src/acb_theta/ql_lower_dim.c b/src/acb_theta/ql_lower_dim.c new file mode 100644 index 0000000000..2a62319cc0 --- /dev/null +++ b/src/acb_theta/ql_lower_dim.c @@ -0,0 +1,171 @@ +/* + Copyright (C) 2024 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 3 of the License, or + (at your option) any later version. See . +*/ + +#include "arb_mat.h" +#include "acb.h" +#include "acb_mat.h" +#include "acb_theta.h" + +static int +acb_theta_ql_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_sum_addprec(max_d); + acb_theta_sum_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 + { + /* Should not happen in tests */ + *nb_pts = 0; + *pts = flint_malloc(0); + } + + acb_theta_eld_clear(E); + arf_clear(R2); + arb_init(max_d); + return res; +} + +int +acb_theta_ql_lower_dim(acb_ptr * new_zs, acb_ptr * cofactors, slong ** pts, + slong * nb, arf_t err, slong * fullprec, acb_srcptr z, const acb_mat_t tau, + arb_srcptr distances, slong s, ulong a, slong prec) +{ + slong g = acb_mat_nrows(tau); + arb_mat_t cho, cho0, cho1, yinv, y0inv; + acb_mat_t tau0, star, tau1; + arb_ptr y, v, w, new_y, new_w, rs; + acb_ptr u, x, cs; + slong j, k; + int res; + + FLINT_ASSERT(s >= 1 && s < g); + + arb_mat_init(cho, g, g); + arb_mat_init(cho0, s, s); + arb_mat_init(yinv, g, g); + arb_mat_init(y0inv, s, s); + 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); + y = _arb_vec_init(g); + v = _arb_vec_init(g - s); + w = _arb_vec_init(g); + u = _acb_vec_init(g - s); + x = _acb_vec_init(g - s); + new_y = _arb_vec_init(s); + new_w = _arb_vec_init(s); + + acb_siegel_cho_yinv(cho, yinv, tau, prec); + acb_siegel_cho_yinv(cho0, y0inv, tau0, prec); + arb_mat_window_init(cho1, cho, s, s, g, g); + _acb_vec_get_imag(y, z, g); + arb_mat_vector_mul_col(w, yinv, y, prec); + + res = acb_theta_ql_eld_points(pts, nb, v, fullprec, + err, distances, a, w, cho, cho1, prec); + *new_zs = _acb_vec_init((*nb) * s); + *cofactors = _acb_vec_init(*nb); + rs = _arb_vec_init((*nb) * s); + cs = _acb_vec_init(*nb); + + for (k = 0; (k < *nb) && res; k++) + { + /* 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], (*pts)[k * (g - s) + j], prec); + } + + /* Get new_z and log(cofactor) */ + acb_mat_vector_mul_col(*new_zs + k * s, star, u, prec); + _acb_vec_add(*new_zs + k * s, *new_zs + k * s, z, s, prec); + + acb_dot(*cofactors + k, NULL, 0, u, 1, z + s, 1, g - s, prec); + acb_mul_2exp_si(*cofactors + k, *cofactors + k, 1); + acb_mat_vector_mul_col(x, tau1, u, prec); + acb_dot(*cofactors + k, *cofactors + k, 0, x, 1, u, 1, g - s, prec); + } + + if (res) + { + /* Further reduce vectors */ + res = acb_theta_reduce_z(*new_zs, rs, cs, *new_zs, *nb, tau0, prec); + } + + for (k = 0; (k < *nb) && res; k++) + { + /* Adjust cofactor for theta_ab_tilde */ + arb_dot(acb_imagref(*cofactors + k), acb_imagref(*cofactors + k), 0, + y, 1, w, 1, g, prec); + _acb_vec_get_imag(new_y, *new_zs + k * s, s); + arb_mat_vector_mul_col(new_w, y0inv, new_y, prec); + arb_dot(acb_imagref(*cofactors + k), acb_imagref(*cofactors + k), 1, + new_y, 1, new_w, 1, s, prec); + acb_exp_pi_i(*cofactors + k, *cofactors + k, prec); + acb_mul(*cofactors + k, *cofactors + k, &cs[k], prec); + } + + arb_mat_clear(cho); + arb_mat_clear(cho0); + arb_mat_window_clear(cho1); + arb_mat_clear(yinv); + arb_mat_clear(y0inv); + acb_mat_window_clear(tau0); + acb_mat_window_clear(star); + acb_mat_window_clear(tau1); + _arb_vec_clear(y, g); + _arb_vec_clear(v, g - s); + _arb_vec_clear(w, g); + _acb_vec_clear(u, g - s); + _acb_vec_clear(x, g - s); + _arb_vec_clear(new_y, s); + _arb_vec_clear(new_w, s); + _arb_vec_clear(rs, (*nb) * s); + _acb_vec_clear(cs, *nb); + return res; +} diff --git a/src/acb_theta/ql_nb_steps.c b/src/acb_theta/ql_nb_steps.c new file mode 100644 index 0000000000..8ac01e05f4 --- /dev/null +++ b/src/acb_theta/ql_nb_steps.c @@ -0,0 +1,187 @@ +/* + 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 3 of the License, or + (at your option) any later version. See . +*/ + +#include "arb.h" +#include "arb_mat.h" +#include "acb_mat.h" +#include "acb_theta.h" + +/* This is the all-important function to increase performance. */ + +int +acb_theta_ql_nb_steps(slong * pattern, const acb_mat_t tau, int cst, slong prec) +{ + slong g = acb_mat_nrows(tau); + slong lp = ACB_THETA_LOW_PREC; + arb_mat_t cho, yinv; + arb_t x, t; + slong s, nb, dupl; + slong * rough; + + arb_init(x); + arb_init(t); + arb_mat_init(cho, g, g); + arb_mat_init(yinv, g, g); + rough = flint_malloc(g * sizeof(slong)); + + acb_siegel_cho_yinv(cho, yinv, tau, lp); + + /* Compute rough pattern (could be negative) */ + /* Note: we could be more precise by scaling x by something else than + powers of 2 */ + for (s = 0; s < g; s++) + { + arb_sqr(x, arb_mat_entry(cho, 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) + { + /* Should not happen in tests */ + arb_clear(x); + arb_clear(t); + return 0; + } + + rough[s] = -arf_get_si(arb_midref(x), ARF_RND_NEAR); + } + + /* Experimental data from p-acb_theta_ql_exact: rough -> desired pattern */ + /* Genus 1 theta constants */ + /* 0, ..., 9 -> 0 + 10 -> 3 + 11 -> 4 + 12, 13 -> 5 + 14, 15 -> 6 + 16 -> 7 + 17 -> 8 */ + /* Genus 1 general theta values */ + /* 0, ..., 8 -> 0 + 9 -> 0 or 4 + 10 -> 5 + 11 -> 6, ..., + 13, 14 -> 8 + 15, 16 -> 9 + 17 -> 10 */ + /* Genus 2 */ + /* 4 3, 5 3 -> 0 0 + 6 4 -> 3 3 or 4 4 + 6 5 -> 4 4 or 5 5 + 8 6 -> 6 6 + 11 10 -> 10 10 + 12 10 -> 10 10 or 11 11 + 16 14 -> 14 14 or 15 15 + 8 2 -> 3 2 or 4 2 + 9 3 -> 4 2 + 10 4 -> 6 5 or 7 6 + 11 5 -> 7 6 + 12 6 -> 8 7 + 15 9 -> 11 10 + 8 0 -> 2 0 or 0 0 + 9 1 -> 4 2 + 10 2 -> 6 2 + 12 4 -> 8 6 + 14 6 -> 8 7 */ + + /* Start adapting the pattern from s = g-1 downwards. This is because the + choice of whether to trigger dimension-lowering formulas in low + dimensions will depend on whether or not duplications/dimension-lowerings + have already been applied. */ + /* See /path/to/flint/build/acb_theta/profile/p-acb_theta_ql_exact */ + /* Some of these branches will not show up in tests. */ + for (s = g - 1; s >= 0; s--) + { + /* Find out how many duplication steps have been performed so far + (could be negative if s < g - 1) */ + if (s == g - 1) + { + dupl = 0; + } + else + { + dupl = pattern[s + 1]; + } + pattern[s] = rough[s]; + + /* Force trigger dimension-lowering at that point ? We only do this if + s = 0 and dupl is negative as the ellipsoid really contains very few + points. */ + if (s == 0 && dupl < 0) + { + pattern[s] = FLINT_MAX(1, pattern[s]); + } + + /* Force more duplication steps ? We only do this for s = 1 if there + will be a dimension-lowering later on, or for s >= 2. */ + if (s == 1 && pattern[s] < rough[0] && pattern[s] >= 1) + { + pattern[s] = FLINT_MIN(rough[0] - 1, pattern[s] + 2); + } + else if (s >= 2 && pattern[s] >= 1) + { + pattern[s] += 2; + } + + /* Remove further duplication steps in genus 1 if it doesn't mess with + the dimension-lowering strategy. */ + if (s == 0) + { + nb = pattern[s] - 5; + if (nb >= 10) + { + nb -= 2; + } + else if (nb >= 8) + { + nb -= 1; + } + if (g == 1 && cst) + { + nb -= 2; + } + pattern[s] = FLINT_MAX(FLINT_MAX(0, dupl) + 1, nb); + } + + /* Remove duplication steps to avoid dimension-lowering altogether ? We + only do this if the number of steps is <= dupl + 2. In genus 1, we + additionally demand that rough[s] be less than dupl + 1, unless dupl + == 0 */ + if (pattern[s] <= dupl + 2 + && (s > 0 || dupl == 0)) + { + pattern[s] = dupl; + } + else if (s == 0 && rough[s] <= dupl + 1) + { + pattern[s] = dupl; + } + + /* In any case, make pattern a nonincreasing vector */ + if (s < g - 1) + { + pattern[s] = FLINT_MAX(dupl, pattern[s]); + } + } + + /* Clean up: make pattern a nonnegative vector */ + for (s = 0; s < g; s++) + { + pattern[s] = FLINT_MAX(0, pattern[s]); + } + + arb_clear(x); + arb_clear(t); + arb_mat_clear(cho); + arb_mat_clear(yinv); + flint_free(rough); + return 1; +} diff --git a/src/acb_theta/ql_recombine.c b/src/acb_theta/ql_recombine.c new file mode 100644 index 0000000000..6a1b042e0b --- /dev/null +++ b/src/acb_theta/ql_recombine.c @@ -0,0 +1,79 @@ +/* + Copyright (C) 2024 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 3 of the License, or + (at your option) any later version. See . +*/ + +#include "acb.h" +#include "acb_theta.h" + +void +acb_theta_ql_recombine(acb_ptr th, acb_srcptr th0, acb_srcptr cofactors, + const slong * pts, slong nb, const arf_t err, slong fullprec, + slong s, ulong a, int all, slong g, slong prec) +{ + slong n = 1 << g; + slong n0 = 1 << s; + slong nba = 1 << (g - s); + slong nbth0 = (all ? n0 * n0 : n0); + acb_ptr aux; + acb_t x; + ulong a0, b0, b1, b; + slong k; + + acb_init(x); + aux = _acb_vec_init(nbth0); + + /* Sum contributions of each point weighted by cofactor */ + for (k = 0; k < nb; k++) + { + _acb_vec_scalar_mul(aux, th0 + k * nbth0, nbth0, &cofactors[k], prec); + + for (a0 = 0; a0 < n0; a0++) + { + if (all) + { + /* writing ab = a0 a1 b0 b1, we modify all entries with + a1 = a, with sign i^(b1 . pt) */ + for (b = 0; b < n; b++) + { + b1 = b % nba; + b0 = b >> (g - s); + acb_mul_i_pow_si(x, &aux[a0 * n0 + b0], + 2 * acb_theta_char_dot_slong(b1, pts + k * (g - s), g - s) + + acb_theta_char_dot(b1, a, g - s)); + acb_add(&th[(a0 << (g + g - s)) + (a << g) + b], + &th[(a0 << (g + g - s)) + (a << g) + b], x, fullprec); + } + } + else + { + acb_add(&th[(a0 << (g - s)) + a], &th[(a0 << (g - s)) + a], + &aux[a0], fullprec); + } + } + } + /* Add error */ + for (a0 = 0; a0 < n0; a0++) + { + if (all) + { + for (b = 0; b < n; b++) + { + acb_add_error_arf(&th[(a0 << (g + g - s)) + (a << g) + b], err); + } + } + else + { + acb_add_error_arf(&th[(a0 << (g - s)) + a], err); + } + } + + acb_clear(x); + _acb_vec_clear(aux, nbth0); +} diff --git a/src/acb_theta/ql_reduce.c b/src/acb_theta/ql_reduce.c deleted file mode 100644 index 4ccd499f29..0000000000 --- a/src/acb_theta/ql_reduce.c +++ /dev/null @@ -1,124 +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 3 of the License, or - (at your option) any later version. See . -*/ - -#include "arb_mat.h" -#include "acb.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/ql_setup.c b/src/acb_theta/ql_setup.c new file mode 100644 index 0000000000..c413306b27 --- /dev/null +++ b/src/acb_theta/ql_setup.c @@ -0,0 +1,434 @@ +/* + Copyright (C) 2024 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 3 of the License, or + (at your option) any later version. See . +*/ + +#include "arb.h" +#include "acb.h" +#include "arb_mat.h" +#include "acb_mat.h" +#include "acb_theta.h" + +/* Helper functions */ + +static void +acb_theta_ctx_tau_copy(acb_theta_ctx_tau_t res, const acb_theta_ctx_tau_t ctx) +{ + slong g = ctx->g; + slong n = 1 << g; + + FLINT_ASSERT(res->g == g); + + arb_mat_set(&res->yinv, &ctx->yinv); + arb_mat_set(&res->cho, &ctx->cho); + acb_mat_set(res->exp_tau_div_4, ctx->exp_tau_div_4); + acb_mat_set(res->exp_tau_div_2, ctx->exp_tau_div_2); + acb_mat_set(res->exp_tau, ctx->exp_tau); + acb_mat_set(res->exp_tau_div_4_inv, ctx->exp_tau_div_4_inv); + acb_mat_set(res->exp_tau_div_2_inv, ctx->exp_tau_div_2_inv); + acb_mat_set(res->exp_tau_inv, ctx->exp_tau_inv); + + if (ctx->allow_shift) /* will always be the case here */ + { + _acb_vec_set(res->exp_tau_a, ctx->exp_tau_a, n * g); + _acb_vec_set(res->exp_tau_a_inv, ctx->exp_tau_a_inv, n * g); + _acb_vec_set(res->exp_a_tau_a_div_4, ctx->exp_a_tau_a_div_4, n); + } +} + +static int +_acb_vec_contains_zero(acb_srcptr vec, slong nb) +{ + slong k; + + for (k = 0; k < nb; k++) + { + if (acb_contains_zero(&vec[k])) + { + return 1; + } + } + return 0; +} + +static int +acb_theta_contains_even_zero(acb_srcptr vec, slong g) +{ + slong n = 1 << (2 * g); + slong j; + int res = 0; + + for (j = 0; j < n; j++) + { + if (acb_theta_char_is_even(j, g) && acb_contains_zero(&vec[j])) + { + res = 1; + break; + } + } + + return res; +} + +/* Find out for which z we can use t=0 at this precision. */ +/* Input/output: + - rts, rts_all, all, nb_steps, nb, distances are as in acb_theta_ql_setup + - prec should be small + - is_zero[j] is true iff the corresponding z is zero + - easy_steps[j] should contain the number of steps that are already known to + be easy for a given z. This number may increase in the output + - &vec[j] and ctx_tau should be a valid context for the pairs (z, tau). They + will get duplicated in acb_theta_ql_setup_easy. + - At exit, &vec[j] contains the context for 2^k z, where k = easy_steps[j], + except if easy_steps[j] is nb_steps in which case we guarantee nothing. */ + +static void +acb_theta_ql_setup_easy(acb_ptr rts, acb_ptr rts_all, slong * easy_steps, + acb_theta_ctx_z_struct * vec, slong nb, const int * is_zero, acb_theta_ctx_tau_t ctx_tau, + arb_srcptr distances, slong nb_steps, int all, slong prec) +{ + slong g = ctx_tau->g; + slong n = 1 << g; + arb_ptr d; + acb_ptr th; + slong j, k; + int easy; + + d = _arb_vec_init(n); + + for (k = 0; k < nb_steps; k++) + { + for (j = 0; j < nb; j++) + { + if (easy_steps[j] == nb_steps) + { + /* nothing more to be done for this z: we continue without + duplicating the context */ + continue; + } + else if (easy_steps[j] < k) + { + /* steps for this z aren't easy anymore: we continue without + duplicating the context */ + continue; + } + else if (easy_steps[j] > k) + { + /* easy step was set up during a previous pass: we continue but + need to update the context */ + acb_theta_ctx_z_dupl(&vec[j], prec + ACB_THETA_LOW_PREC); + continue; + } + /* we now have k == easy_steps[j], and vec[j] contains a valid + context for 2^k z_j */ + + _arb_vec_scalar_mul_2exp_si(d, distances + j * n, n, k); + if (k == 0 && all) + { + th = rts_all + j * n * n; + acb_theta_sum(th, &vec[j], 1, ctx_tau, d, 1, 1, 1, prec); + /* odd theta constants are known to be zero */ + easy = (is_zero[j] && !acb_theta_contains_even_zero(th, g)) + || !_acb_vec_contains_zero(th, n * n); + } + else + { + th = rts + j * (3 * n * nb_steps) + k * (3 * n); + acb_theta_sum(th, &vec[j], 1, ctx_tau, d, 1, 0, 1, prec); + easy = !_acb_vec_contains_zero(th, n); + } + + if (easy) + { + /* update easy_steps and duplicate context for k + 1 */ + easy_steps[j]++; + if (k < nb_steps - 1) + { + acb_theta_ctx_z_dupl(&vec[j], prec + ACB_THETA_LOW_PREC); + } + } + } + if (k < nb_steps - 1) + { + /* duplication on ctx_tau */ + acb_theta_ctx_tau_dupl(ctx_tau, prec + ACB_THETA_LOW_PREC); + } + } + + _arb_vec_clear(d, n); +} + +/* Find out if the given vector t is usable for hard steps */ +/* Input/output: + - rts, rts_all, all, nb_steps, nb, distances should be as in acb_theta_ql_setup + - prec should be small + - t should be an exact real vector of length g + - easy_steps is as output by acb_theta_ql_setup_easy + - ctx_tau should contain a valid context for tau; it will get duplicated + - initially, &vec[j] should contain a valid context for 2^k z where + k = easy_steps[j]. It will get duplicated, and we guarantee nothing on the + output. +*/ + +static int +acb_theta_ql_setup_hard(acb_ptr rts, acb_ptr rts_all, acb_ptr t, + acb_theta_ctx_z_struct * vec, slong nb, acb_theta_ctx_tau_t ctx_tau, + const slong * easy_steps, arb_srcptr distances, slong nb_steps, int all, slong prec) +{ + slong g = ctx_tau->g; + slong n = 1 << g; + flint_rand_t state; + acb_theta_ctx_z_struct * aux; + acb_theta_ctx_z_t ctxt; + acb_ptr th; + arb_ptr d; + slong j, k; + int res = 1; + + flint_rand_init(state); + aux = acb_theta_ctx_z_vec_init(2, g); + acb_theta_ctx_z_init(ctxt, g); + d = _arb_vec_init(n); + + /* Choose a random t and set context */ + for (k = 0; k < g; k++) + { + arb_urandom(acb_realref(&t[k]), state, prec); + acb_get_mid(&t[k], &t[k]); + } + acb_theta_ctx_z_set(ctxt, t, ctx_tau, prec + ACB_THETA_LOW_PREC); + + for (k = 0; (k < nb_steps) && res; k++) + { + for (j = 0; (j < nb) && res; j++) + { + if (easy_steps[j] > k) + { + /* nothing to be done yet for this z: we continue without + duplicating the context */ + continue; + } + /* we now have k >= easy_steps[j], and vec[j] currently + contains a valid context for 2^k z_j, while ctxt + contains a valid context for 2^k t */ + + /* set context vector aux at z + t and z + 2t */ + _arb_vec_scalar_mul_2exp_si(d, distances + j * n, n, k); + acb_theta_ctx_z_add_real(&aux[0], &vec[j], ctxt, prec + ACB_THETA_LOW_PREC); + acb_theta_ctx_z_add_real(&aux[1], &aux[0], ctxt, prec + ACB_THETA_LOW_PREC); + + if (k == 0 && all) + { + /* We just need roots for z + 2t */ + th = rts_all + j * n * n; + acb_theta_sum(th, &aux[1], 1, ctx_tau, d, 1, 1, 1, prec); + res = !_acb_vec_contains_zero(th, n * n); + } + else if (k == 0) + { + /* We just need roots for z + 2t */ + th = rts + j * (3 * n * nb_steps) + 2 * n; + acb_theta_sum(th, &aux[1], 1, ctx_tau, d, 1, 0, 1, prec); + res = !_acb_vec_contains_zero(th, n); + } + else + { + /* we need roots at z + t and z + 2t */ + th = rts + j * (3 * n * nb_steps) + k * (3 * n) + n; + acb_theta_sum(th, aux, 2, ctx_tau, d, 1, 0, 1, prec); + res = !_acb_vec_contains_zero(th, 2 * n); + } + + if (k < nb_steps - 1) + { + acb_theta_ctx_z_dupl(&vec[j], prec + ACB_THETA_LOW_PREC); + } + } + if (k < nb_steps - 1) + { + acb_theta_ctx_tau_dupl(ctx_tau, prec + ACB_THETA_LOW_PREC); + acb_theta_ctx_z_dupl(ctxt, prec + ACB_THETA_LOW_PREC); + } + } + + flint_rand_clear(state); + acb_theta_ctx_z_vec_clear(aux, 2); + acb_theta_ctx_z_clear(ctxt); + _arb_vec_clear(d, n); + return res; +} + +static slong +acb_theta_ql_setup_lost_bits(acb_srcptr rts, acb_srcptr rts_all, + const slong * easy_steps, const int * is_zero, slong nb, + arb_srcptr distances, slong nb_steps, int all, slong g) +{ + slong n = 1 << g; + slong lost_bits, total_lost_bits; + arb_t x; + arf_t y, z; + fmpz_t e; + slong lp = ACB_THETA_LOW_PREC + nb_steps; + slong j, k, a, b; + + arb_init(x); + arf_init(y); + arf_init(z); + fmpz_init(e); + + total_lost_bits = 0; + for (k = 0; k < nb_steps; k++) + { + lost_bits = 0; + for (j = 0; j < nb; j++) + { + for (a = 0; a < n; a++) + { + arb_zero(x); + arb_get_lbound_arf(arb_midref(x), &distances[j * n + a], lp); + arb_mul_2exp_si(x, x, k); + arb_exp(x, x, lp); + /* Set y to the minimum absolute value of the roots */ + if (k == 0 && all && easy_steps[j] > 0 && is_zero[j]) + { + arf_pos_inf(y); + for (b = 0; b < n; b++) + { + if (acb_theta_char_is_even(n * a + b, g)) + { + acb_get_abs_lbound_arf(z, &rts_all[j * n * n + a * n + b], lp); + arf_min(y, y, z); + } + } + } + else if (k == 0 && all) /* easy or hard step, all theta values */ + { + arf_pos_inf(y); + for (b = 0; b < n; b++) + { + acb_get_abs_lbound_arf(z, &rts_all[j * n * n + a * n + b], lp); + arf_min(y, y, z); + } + } + else if (k < easy_steps[j]) /* a generic easy step */ + { + acb_get_abs_lbound_arf(y, &rts[j * (3 * n * nb_steps) + k * (3 * n) + a], lp); + } + else /* a generic hard step */ + { + acb_get_abs_lbound_arf(y, &rts[j * (3 * n * nb_steps) + k * (3 * n) + n + a], lp); + acb_get_abs_lbound_arf(z, &rts[j * (3 * n * nb_steps) + k * (3 * n) + 2 * n + a], lp); + arf_min(y, y, z); + } + /* Find out how many bits of precision we approximately lose */ + arb_mul_arf(x, x, y, lp); + arb_get_lbound_arf(y, x, lp); + arf_frexp(y, e, y); + if (fmpz_fits_si(e)) + { + lost_bits = FLINT_MAX(lost_bits, - fmpz_get_si(e)); + } + } + } + total_lost_bits += lost_bits + g + 4; + } + + arb_clear(x); + arf_clear(y); + arf_clear(z); + fmpz_clear(e); + return total_lost_bits; +} + +/* Assume that zs always starts with zero. */ + +int +acb_theta_ql_setup(acb_ptr rts, acb_ptr rts_all, acb_ptr t, slong * guard, slong * easy_steps, + acb_srcptr zs, slong nb, const acb_mat_t tau, arb_srcptr distances, + slong nb_steps, int all, slong prec) +{ + slong g = acb_mat_nrows(tau); + acb_theta_ctx_tau_t ctx_tau_1, ctx_tau_2; + acb_theta_ctx_z_struct * vec; + slong lowprec, j; + int done = 0; + int * is_zero; + + FLINT_ASSERT(nb >= 1); + FLINT_ASSERT(_acb_vec_is_zero(zs, g)); + + for (j = 0; j < nb; j++) + { + easy_steps[j] = 0; + } + _acb_vec_zero(t, g); + if (nb_steps == 0) + { + *guard = 0; + return 1; + } + + acb_theta_ctx_tau_init(ctx_tau_1, 1, g); + acb_theta_ctx_tau_init(ctx_tau_2, 1, g); + vec = acb_theta_ctx_z_vec_init(nb, g); + is_zero = flint_malloc(nb * sizeof(int)); + + for (j = 0; j < nb; j++) + { + is_zero[j] = _acb_vec_is_zero(zs + j * g, g); + } + + for (lowprec = 8; (lowprec < prec) && !done; lowprec *= 2) + { + /* Set contexts at low precision, but with some additional guard bits */ + acb_theta_ctx_tau_set(ctx_tau_1, tau, lowprec + ACB_THETA_LOW_PREC); + acb_theta_ctx_tau_copy(ctx_tau_2, ctx_tau_1); + for (j = 0; j < nb; j++) + { + acb_theta_ctx_z_set(&vec[j], zs + j * g, ctx_tau_1, lowprec + ACB_THETA_LOW_PREC); + } + + /* Add possible easy steps compared to what we already know */ + acb_theta_ql_setup_easy(rts, rts_all, easy_steps, vec, nb, + is_zero, ctx_tau_1, distances, nb_steps, all, lowprec); + + /* Let easy_steps[0] be the minimal value (necessary for duplication + steps) */ + for (j = 1; j < nb; j++) + { + easy_steps[0] = FLINT_MIN(easy_steps[0], easy_steps[j]); + } + done = (easy_steps[0] == nb_steps); + + /* At this point, for every j such that easy_steps[j] = nb_steps, we + have computed all the roots we want. Pick an auxiliary t for the + other hard steps; if it doesn't work, then we restart with an + increased lowprec. */ + if (!done) + { + /* Reset ctx_tau_dupl; note that the input vec in + setup_hard is exactly as output by setup_easy */ + done = acb_theta_ql_setup_hard(rts, rts_all, t, vec, nb, + ctx_tau_2, easy_steps, distances, nb_steps, all, lowprec); + } + } + + if (done) + { + /* Estimate the number of bits lost in the duplication formulas */ + *guard = acb_theta_ql_setup_lost_bits(rts, rts_all, easy_steps, + is_zero, nb, distances, nb_steps, all, g); + } + + acb_theta_ctx_tau_clear(ctx_tau_1); + acb_theta_ctx_tau_clear(ctx_tau_2); + acb_theta_ctx_z_vec_clear(vec, nb); + flint_free(is_zero); + return done; +} diff --git a/src/acb_theta/reduce_tau.c b/src/acb_theta/reduce_tau.c new file mode 100644 index 0000000000..b706c5ad38 --- /dev/null +++ b/src/acb_theta/reduce_tau.c @@ -0,0 +1,67 @@ +/* + Copyright (C) 2024 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 3 of the License, or + (at your option) any later version. See . +*/ + +#include "fmpz_mat.h" +#include "acb.h" +#include "acb_mat.h" +#include "acb_theta.h" + +int +acb_theta_reduce_tau(acb_ptr new_zs, acb_mat_t new_tau, fmpz_mat_t mat, acb_mat_t N, + acb_mat_t ct, acb_ptr exps, acb_srcptr zs, slong nb, const acb_mat_t tau, slong prec) +{ + slong g = acb_mat_nrows(tau); + fmpz_mat_t gamma; + acb_mat_t c; + acb_ptr y; + acb_t ipi; + slong j; + int res; + + FLINT_ASSERT(nb >= 0); + + acb_mat_init(c, g, g); + y = _acb_vec_init(g); + acb_init(ipi); + + /* Set new_tau, mat, ct, res */ + acb_siegel_reduce(mat, tau, prec); + acb_siegel_transform_cocycle_inv(new_tau, c, ct, mat, tau, prec); + sp2gz_inv(mat, mat); + acb_mat_transpose(ct, ct); + res = acb_siegel_is_reduced(new_tau, -10, prec); + + if (res) + { + /* Set N */ + fmpz_mat_window_init(gamma, mat, g, 0, 2 * g, g); + acb_mat_set_fmpz_mat(N, gamma); + acb_mat_mul(N, N, ct, prec); + acb_const_pi(ipi, prec); + acb_mul_onei(ipi, ipi); + acb_mat_scalar_mul_acb(N, N, ipi, prec); + + /* Set exps and new_zs */ + for (j = 0; j < nb; j++) + { + acb_mat_vector_mul_col(y, N, zs + j * g, prec); + acb_dot(&exps[j], NULL, 0, zs + j * g, 1, y, 1, g, prec); + acb_exp(&exps[j], &exps[j], prec); + acb_mat_vector_mul_col(new_zs + j * g, ct, zs + j * g, prec); + } + } + + fmpz_mat_window_clear(gamma); + acb_mat_clear(c); + _acb_vec_clear(y, g); + acb_clear(ipi); + return res; +} diff --git a/src/acb_theta/reduce_z.c b/src/acb_theta/reduce_z.c new file mode 100644 index 0000000000..3686dd6160 --- /dev/null +++ b/src/acb_theta/reduce_z.c @@ -0,0 +1,124 @@ +/* + Copyright (C) 2024 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 3 of the License, or + (at your option) any later version. See . +*/ + +#include "acb.h" +#include "arb_mat.h" +#include "acb_mat.h" +#include "acb_theta.h" + +static int +acb_theta_round(arb_ptr a, arb_srcptr v, slong g) +{ + slong j; + fmpz_t m; + int res = 1; + + fmpz_init(m); + + for (j = 0; (j < g) && res; j++) + { + res = arb_is_finite(&v[j]) + && arf_cmpabs_2exp_si(arb_midref(&v[j]), 1000000) <= 0; + if (res) + { + arf_get_fmpz(m, arb_midref(&v[j]), ARF_RND_NEAR); + arb_set_fmpz(&a[j], m); + } + } + + fmpz_clear(m); + return res; +} + +int +acb_theta_reduce_z(acb_ptr new_zs, arb_ptr rs, acb_ptr cs, acb_srcptr zs, + slong nb, const acb_mat_t tau, slong prec) +{ + slong g = acb_mat_nrows(tau); + arb_mat_t cho, yinv; + arb_ptr y; + acb_ptr t, x; + arf_t e; + slong j, k; + int res = 1; + + FLINT_ASSERT(nb >= 0); + + arb_mat_init(cho, g, g); + arb_mat_init(yinv, g, g); + y = _arb_vec_init(g); + t = _acb_vec_init(g); + x = _acb_vec_init(g); + arf_init(e); + + acb_siegel_cho_yinv(cho, yinv, tau, prec); + + for (j = 0; j < nb; j++) + { + /* Round Yinv y to nearest vector r = 0 mod 2 */ + _acb_vec_get_imag(y, zs + j * g, g); + arb_mat_vector_mul_col(y, yinv, y, prec); + _arb_vec_scalar_mul_2exp_si(y, y, g, -1); + res = acb_theta_round(rs + j * g, y, g); + _arb_vec_scalar_mul_2exp_si(rs + j * g, rs + j * g, g, 1); + if (!res) + { + break; + } + + /* x = new_z is z - tau * r */ + _arb_vec_zero(y, g); + _acb_vec_set_real_imag(x, rs + j * g, y, g); + acb_mat_vector_mul_col(x, tau, x, prec); + _acb_vec_sub(x, zs + j * g, x, g, prec); + + /* c is exp(- i pi r^T (z + x)) */ + _acb_vec_add(t, x, zs + j * g, g, prec); + _acb_vec_get_real(y, t, g); + arb_dot(acb_realref(&cs[j]), NULL, 1, rs + j * g, 1, y, 1, g, prec); + _acb_vec_get_imag(y, t, g); + arb_dot(acb_imagref(&cs[j]), NULL, 1, rs + j * g, 1, y, 1, g, prec); + acb_exp_pi_i(&cs[j], &cs[j], prec); + + /* Further reduce real part of x modulo 2 */ + _acb_vec_get_real(y, x, g); + _arb_vec_scalar_mul_2exp_si(y, y, g, -1); + res = acb_theta_round(y, y, g); + if (res) + { + _arb_vec_scalar_mul_2exp_si(y, y, g, 1); + for (k = 0; k < g; k++) + { + acb_sub_arb(&x[k], &x[k], &y[k], prec); + } + } + + /* Set real part to [-1,1] if error is too large */ + for (k = 0; k < g; k++) + { + if (mag_cmp_2exp_si(arb_radref(acb_realref(&x[k])), 0) > 0) + { + arb_zero_pm_one(acb_realref(&x[k])); + } + } + + /* Set new_z */ + _acb_vec_set(new_zs + j * g, x, g); + } + + arb_mat_clear(cho); + arb_mat_clear(yinv); + _arb_vec_clear(y, g); + _acb_vec_clear(t, g); + _acb_vec_clear(x, g); + arf_clear(e); + return res; +} diff --git a/src/acb_theta/siegel_cho.c b/src/acb_theta/siegel_cho.c deleted file mode 100644 index 349d637960..0000000000 --- a/src/acb_theta/siegel_cho.c +++ /dev/null @@ -1,37 +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 3 of the License, or - (at your option) any later version. See . -*/ - -#include "arb.h" -#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_cho_yinv.c b/src/acb_theta/siegel_cho_yinv.c new file mode 100644 index 0000000000..ccf5bbacc0 --- /dev/null +++ b/src/acb_theta/siegel_cho_yinv.c @@ -0,0 +1,39 @@ +/* + Copyright (C) 2024 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 3 of the License, or + (at your option) any later version. See . +*/ + +#include "arb.h" +#include "arb_mat.h" +#include "acb_mat.h" +#include "acb_theta.h" + +void +acb_siegel_cho_yinv(arb_mat_t cho, arb_mat_t yinv, const acb_mat_t tau, slong prec) +{ + arb_t sqrtpi; + int res; + + arb_init(sqrtpi); + arb_const_sqrt_pi(sqrtpi, prec); + + acb_mat_get_imag(cho, tau); + res = arb_mat_cho(cho, cho, prec); + if (!res) + { + /* Should not happen in tests */ + arb_mat_indeterminate(cho); + } + arb_mat_inv_cho_precomp(yinv, cho, prec); + + arb_mat_transpose(cho, cho); + arb_mat_scalar_mul_arb(cho, cho, sqrtpi, prec); + + arb_clear(sqrtpi); +} diff --git a/src/acb_theta/siegel_cocycle.c b/src/acb_theta/siegel_cocycle.c index 8cb30adf74..462f3fdc26 100644 --- a/src/acb_theta/siegel_cocycle.c +++ b/src/acb_theta/siegel_cocycle.c @@ -9,6 +9,7 @@ (at your option) any later version. See . */ +#include "fmpz_mat.h" #include "acb_mat.h" #include "acb_theta.h" diff --git a/src/acb_theta/siegel_is_reduced.c b/src/acb_theta/siegel_is_reduced.c index 4717637db3..a13ef5c1bd 100644 --- a/src/acb_theta/siegel_is_reduced.c +++ b/src/acb_theta/siegel_is_reduced.c @@ -9,6 +9,7 @@ (at your option) any later version. See . */ +#include "fmpz_mat.h" #include "arb_mat.h" #include "acb.h" #include "acb_mat.h" diff --git a/src/acb_theta/siegel_kappa.c b/src/acb_theta/siegel_kappa.c new file mode 100644 index 0000000000..cc84591031 --- /dev/null +++ b/src/acb_theta/siegel_kappa.c @@ -0,0 +1,375 @@ +/* + 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 3 of the License, or + (at your option) any later version. See . +*/ + +#include "fmpz_mat.h" +#include "acb.h" +#include "acb_poly.h" +#include "acb_mat.h" +#include "acb_modular.h" +#include "acb_theta.h" + +static void +acb_siegel_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); +} + +static void +acb_siegel_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_rand_init(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++) + { + arb_abs(x, acb_realref(&rts[j])); + arb_sub_si(x, x, 1, prec); + success = !arb_contains_zero(acb_imagref(&rts[j])) || 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_siegel_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_siegel_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 + { + /* Should not happen in tests */ + acb_mat_det(res, tau, prec); + acb_sqrts(res, z, res, prec); + acb_union(res, res, z, prec); + } + + flint_rand_clear(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); +} + +static slong +acb_siegel_kappa_g1(acb_t sqrtdet, const fmpz_mat_t mat, const fmpz_mat_t x, + const acb_mat_t tau, int sqr, 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); + + if (!sqr) + { + 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_char_table(&ab, &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 +acb_siegel_kappa_j(acb_t sqrtdet, const fmpz_mat_t mat, const acb_mat_t tau, int sqr, 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} */ + if (!sqr) + { + acb_mat_window_init(tau0, tau, 0, 0, r, r); + acb_siegel_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_siegel_kappa(acb_t sqrtdet, const fmpz_mat_t mat, const acb_mat_t tau, int sqr, 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--) + { + /* c keeps track of sqrtdet; only meaningful when sqr is false */ + acb_one(c); + + 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; + if (!sqr) + { + acb_onei(c); + } + } + } + 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 += acb_siegel_kappa_g1(c, &dec[k], x, w, sqr, prec); + acb_div_onei(c, c); + res += 2; + } + else + { + res += acb_siegel_kappa_g1(c, &dec[k], x, w, sqr, prec); + } + } + else /* embedded j */ + { + res += acb_siegel_kappa_j(c, &dec[k], w, sqr, prec); + } + + if (!sqr) + { + acb_siegel_transform(w, &dec[k], w, prec); + acb_mul(sqrtdet, sqrtdet, c, prec); + } + } + + /* Compute det if sqr is true */ + if (sqr) + { + acb_siegel_cocycle(w, mat, tau, prec); + acb_mat_det(sqrtdet, w, prec); + } + + /* Adjust final sign based on transformation of coordinates */ + acb_theta_char_table(&ab, &e, mat, 0); + res -= e; + ab = 0; + for (k = 0; k < nb_dec; k++) + { + acb_theta_char_table(&ab, &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 & (sqr ? 3 : 7); +} diff --git a/src/acb_theta/siegel_yinv.c b/src/acb_theta/siegel_kappa2.c similarity index 57% rename from src/acb_theta/siegel_yinv.c rename to src/acb_theta/siegel_kappa2.c index 6f5ed05623..c581ae3c0c 100644 --- a/src/acb_theta/siegel_yinv.c +++ b/src/acb_theta/siegel_kappa2.c @@ -9,19 +9,25 @@ (at your option) any later version. See . */ -#include "arb_mat.h" +#include "acb.h" #include "acb_mat.h" #include "acb_theta.h" -void -acb_siegel_yinv(arb_mat_t Yinv, const acb_mat_t tau, slong prec) +slong +acb_siegel_kappa2(const fmpz_mat_t mat) { - int res; + slong g = sp2gz_dim(mat); + slong prec = 2; + acb_mat_t tau; + acb_t s; + slong res; - acb_mat_get_imag(Yinv, tau); - res = arb_mat_inv(Yinv, Yinv, prec); - if (!res) - { - arb_mat_indeterminate(Yinv); - } + acb_mat_init(tau, g, g); + acb_init(s); + + res = acb_siegel_kappa(s, mat, tau, 1, prec); + + acb_mat_clear(tau); + acb_clear(s); + return res; } diff --git a/src/acb_theta/siegel_randtest_compact.c b/src/acb_theta/siegel_randtest_compact.c new file mode 100644 index 0000000000..2f3be2fc38 --- /dev/null +++ b/src/acb_theta/siegel_randtest_compact.c @@ -0,0 +1,48 @@ +/* + Copyright (C) 2024 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 3 of the License, or + (at your option) any later version. See . +*/ + +#include "acb.h" +#include "acb_mat.h" +#include "acb_theta.h" + +void +acb_siegel_randtest_compact(acb_mat_t tau, flint_rand_t state, int exact, slong prec) +{ + slong g = acb_mat_nrows(tau); + slong bits = n_randint(state, 4); + arb_t y; + slong j, k; + int res; + + 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)), 4, prec); + res = arb_is_negative(y); + } + + if (exact) + { + for (j = 0; j < g; j++) + { + for (k = j; k < g; k++) + { + acb_get_mid(acb_mat_entry(tau, j, k), acb_mat_entry(tau, j, k)); + acb_set(acb_mat_entry(tau, k, j), acb_mat_entry(tau, j, k)); + } + } + } + + arb_clear(y); +} diff --git a/src/acb_theta/siegel_randtest_reduced.c b/src/acb_theta/siegel_randtest_reduced.c index 923febb853..ed60b956e1 100644 --- a/src/acb_theta/siegel_randtest_reduced.c +++ b/src/acb_theta/siegel_randtest_reduced.c @@ -9,6 +9,7 @@ (at your option) any later version. See . */ +#include "fmpz_mat.h" #include "acb.h" #include "acb_mat.h" #include "acb_theta.h" diff --git a/src/acb_theta/siegel_randtest_vec.c b/src/acb_theta/siegel_randtest_vec.c index 7f3af07e0d..26ee23405e 100644 --- a/src/acb_theta/siegel_randtest_vec.c +++ b/src/acb_theta/siegel_randtest_vec.c @@ -34,6 +34,9 @@ acb_siegel_randtest_vec(acb_ptr z, flint_rand_t state, slong g, slong prec) case 3: acb_randtest(&z[k], state, prec, 20); break; + case 4: + arb_urandom(acb_imagref(&z[k]), state, prec); + arb_randtest(acb_realref(&z[k]), state, prec, mag_bits); default: acb_urandom(&z[k], state, prec); } diff --git a/src/acb_theta/siegel_randtest_vec_reduced.c b/src/acb_theta/siegel_randtest_vec_reduced.c new file mode 100644 index 0000000000..e3596f8397 --- /dev/null +++ b/src/acb_theta/siegel_randtest_vec_reduced.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 3 of the License, or + (at your option) any later version. See . +*/ + +#include "acb.h" +#include "acb_mat.h" +#include "acb_theta.h" + +void +acb_siegel_randtest_vec_reduced(acb_ptr zs, flint_rand_t state, slong nb, + const acb_mat_t tau, int exact, slong prec) +{ + slong g = acb_mat_nrows(tau); + arb_t t; + slong j; + + /* Sample z in [-1,1] + tau[-1,1] */ + arb_init(t); + + _acb_vec_zero(zs, nb * g); + for (j = 0; j < nb * g; j++) + { + arb_urandom(acb_realref(&zs[j]), state, prec); + acb_mul_2exp_si(&zs[j], &zs[j], 1); + acb_sub_si(&zs[j], &zs[j], 1, prec); + } + for (j = 0; j < nb; j++) + { + acb_mat_vector_mul_col(zs + j * g, tau, zs + j * g, prec); + } + for (j = 0; j < nb * g; j++) + { + arb_urandom(t, state, prec); + arb_mul_2exp_si(t, t, 1); + arb_sub_si(t, t, 1, prec); + acb_add_arb(&zs[j], &zs[j], t, prec); + } + + if (exact) + { + for (j = 0; j < nb * g; j++) + { + acb_get_mid(&zs[j], &zs[j]); + } + } + + /* Sometimes, force zeros */ + for (j = 0; j < nb; j++) + { + if (n_randint(state, 5) == 0) + { + _acb_vec_zero(zs + j * g, g); + } + } + + arb_clear(t); +} diff --git a/src/acb_theta/siegel_reduce.c b/src/acb_theta/siegel_reduce.c index 732b5e9b7e..82a967ca7d 100644 --- a/src/acb_theta/siegel_reduce.c +++ b/src/acb_theta/siegel_reduce.c @@ -9,6 +9,7 @@ (at your option) any later version. See . */ +#include "fmpz_mat.h" #include "arb_mat.h" #include "acb.h" #include "acb_mat.h" diff --git a/src/acb_theta/siegel_transform_cocycle_inv.c b/src/acb_theta/siegel_transform_cocycle_inv.c index fa2ad2411a..946b858d3e 100644 --- a/src/acb_theta/siegel_transform_cocycle_inv.c +++ b/src/acb_theta/siegel_transform_cocycle_inv.c @@ -9,6 +9,7 @@ (at your option) any later version. See . */ +#include "fmpz_mat.h" #include "acb_mat.h" #include "acb_theta.h" diff --git a/src/acb_theta/siegel_transform_z.c b/src/acb_theta/siegel_transform_z.c deleted file mode 100644 index 45ddffd26d..0000000000 --- a/src/acb_theta/siegel_transform_z.c +++ /dev/null @@ -1,31 +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 3 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/sp2gz_block_diag.c b/src/acb_theta/sp2gz_block_diag.c index a27f86104d..ee6973b87c 100644 --- a/src/acb_theta/sp2gz_block_diag.c +++ b/src/acb_theta/sp2gz_block_diag.c @@ -10,6 +10,7 @@ */ #include "fmpz.h" +#include "fmpz_mat.h" #include "acb_theta.h" void diff --git a/src/acb_theta/sp2gz_decompose.c b/src/acb_theta/sp2gz_decompose.c index e6047f9b28..8e976bfbe8 100644 --- a/src/acb_theta/sp2gz_decompose.c +++ b/src/acb_theta/sp2gz_decompose.c @@ -10,6 +10,7 @@ */ #include "fmpz.h" +#include "fmpz_mat.h" #include "acb_theta.h" /* todo: move out? */ diff --git a/src/acb_theta/sp2gz_embed.c b/src/acb_theta/sp2gz_embed.c index 22ed24fa16..71c42d3557 100644 --- a/src/acb_theta/sp2gz_embed.c +++ b/src/acb_theta/sp2gz_embed.c @@ -10,6 +10,7 @@ */ #include "fmpz.h" +#include "fmpz_mat.h" #include "acb_theta.h" void diff --git a/src/acb_theta/sp2gz_fundamental.c b/src/acb_theta/sp2gz_fundamental.c index 9071f742c7..2679a7180e 100644 --- a/src/acb_theta/sp2gz_fundamental.c +++ b/src/acb_theta/sp2gz_fundamental.c @@ -10,6 +10,7 @@ */ #include "fmpz.h" +#include "fmpz_mat.h" #include "acb_theta.h" static void diff --git a/src/acb_theta/sp2gz_inv.c b/src/acb_theta/sp2gz_inv.c index a79804b5a7..74c3ccbb60 100644 --- a/src/acb_theta/sp2gz_inv.c +++ b/src/acb_theta/sp2gz_inv.c @@ -9,6 +9,7 @@ (at your option) any later version. See . */ +#include "fmpz_mat.h" #include "acb_theta.h" void diff --git a/src/acb_theta/sp2gz_is_block_diag.c b/src/acb_theta/sp2gz_is_block_diag.c index 8e8ebc6977..f2caeea36d 100644 --- a/src/acb_theta/sp2gz_is_block_diag.c +++ b/src/acb_theta/sp2gz_is_block_diag.c @@ -9,6 +9,7 @@ (at your option) any later version. See . */ +#include "fmpz_mat.h" #include "acb_theta.h" int diff --git a/src/acb_theta/sp2gz_is_correct.c b/src/acb_theta/sp2gz_is_correct.c index 4b353fabbf..3683b08353 100644 --- a/src/acb_theta/sp2gz_is_correct.c +++ b/src/acb_theta/sp2gz_is_correct.c @@ -9,6 +9,7 @@ (at your option) any later version. See . */ +#include "fmpz_mat.h" #include "acb_theta.h" int diff --git a/src/acb_theta/sp2gz_is_embedded.c b/src/acb_theta/sp2gz_is_embedded.c index e909d9fd8e..21186dde06 100644 --- a/src/acb_theta/sp2gz_is_embedded.c +++ b/src/acb_theta/sp2gz_is_embedded.c @@ -9,6 +9,7 @@ (at your option) any later version. See . */ +#include "fmpz_mat.h" #include "acb_theta.h" int diff --git a/src/acb_theta/sp2gz_is_j.c b/src/acb_theta/sp2gz_is_j.c index 9ff5b1f639..b69323d697 100644 --- a/src/acb_theta/sp2gz_is_j.c +++ b/src/acb_theta/sp2gz_is_j.c @@ -9,6 +9,7 @@ (at your option) any later version. See . */ +#include "fmpz_mat.h" #include "acb_theta.h" int diff --git a/src/acb_theta/sp2gz_is_trig.c b/src/acb_theta/sp2gz_is_trig.c index 87d1f6560a..3c60461b27 100644 --- a/src/acb_theta/sp2gz_is_trig.c +++ b/src/acb_theta/sp2gz_is_trig.c @@ -9,6 +9,7 @@ (at your option) any later version. See . */ +#include "fmpz_mat.h" #include "acb_theta.h" int diff --git a/src/acb_theta/sp2gz_j.c b/src/acb_theta/sp2gz_j.c index 621b4e256e..3e70085a1f 100644 --- a/src/acb_theta/sp2gz_j.c +++ b/src/acb_theta/sp2gz_j.c @@ -9,6 +9,7 @@ (at your option) any later version. See . */ +#include "fmpz_mat.h" #include "acb_theta.h" void diff --git a/src/acb_theta/sp2gz_nb_fundamental.c b/src/acb_theta/sp2gz_nb_fundamental.c deleted file mode 100644 index 8cf7cdf2a4..0000000000 --- a/src/acb_theta/sp2gz_nb_fundamental.c +++ /dev/null @@ -1,23 +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 3 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 index c1c81a7d0a..12591e79ba 100644 --- a/src/acb_theta/sp2gz_randtest.c +++ b/src/acb_theta/sp2gz_randtest.c @@ -9,6 +9,7 @@ (at your option) any later version. See . */ +#include "fmpz_mat.h" #include "acb_theta.h" static void diff --git a/src/acb_theta/sp2gz_restrict.c b/src/acb_theta/sp2gz_restrict.c index 50b047c0af..74db516807 100644 --- a/src/acb_theta/sp2gz_restrict.c +++ b/src/acb_theta/sp2gz_restrict.c @@ -9,6 +9,7 @@ (at your option) any later version. See . */ +#include "fmpz_mat.h" #include "acb_theta.h" void diff --git a/src/acb_theta/sp2gz_set_blocks.c b/src/acb_theta/sp2gz_set_blocks.c index 22e03d38b9..0d18549018 100644 --- a/src/acb_theta/sp2gz_set_blocks.c +++ b/src/acb_theta/sp2gz_set_blocks.c @@ -10,6 +10,7 @@ */ #include "fmpz.h" +#include "fmpz_mat.h" #include "acb_theta.h" void diff --git a/src/acb_theta/sp2gz_trig.c b/src/acb_theta/sp2gz_trig.c index 1f5c6cccb3..e298f379a1 100644 --- a/src/acb_theta/sp2gz_trig.c +++ b/src/acb_theta/sp2gz_trig.c @@ -9,6 +9,7 @@ (at your option) any later version. See . */ +#include "fmpz_mat.h" #include "acb_theta.h" void diff --git a/src/acb_theta/sum.c b/src/acb_theta/sum.c new file mode 100644 index 0000000000..31052b3003 --- /dev/null +++ b/src/acb_theta/sum.c @@ -0,0 +1,324 @@ +/* + Copyright (C) 2025 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 3 of the License, or + (at your option) any later version. See . +*/ + +#include "profiler.h" +#include "fmpz.h" +#include "acb.h" +#include "arb_mat.h" +#include "acb_mat.h" +#include "acb_modular.h" +#include "acb_theta.h" + +static void +acb_theta_sum_00_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_sum_0b_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 (dot) + { + acb_sub(&th[b], &th[b], + (acb_theta_char_bit(b, 0, g) ? sub : add), fullprec); + } + else + { + acb_add(&th[b], &th[b], + (acb_theta_char_bit(b, 0, g) ? sub : add), fullprec); + } + } + + acb_clear(s0); + acb_clear(s1); + acb_clear(add); + acb_clear(sub); +} + +void +acb_theta_sum_0x(acb_ptr th, const acb_theta_ctx_z_struct * vec, slong nb, + const acb_theta_ctx_tau_t ctx_tau, arb_srcptr distance, int all_b, slong prec) +{ + /* Call sum_work at the right precision */ + slong g = ctx_tau->g; + slong n = (all_b ? 1 << g : 1); + slong guard = ACB_THETA_LOW_PREC; + acb_theta_eld_t E; + acb_ptr * sqr_pow; + arf_t R2, eps; + arb_t err; + arb_ptr v; + int b; + slong j, k; + + acb_theta_eld_init(E, g, g); + arf_init(R2); + arf_init(eps); + arb_init(err); + v = _arb_vec_init(g); + sqr_pow = flint_malloc(g * sizeof(acb_ptr)); + + acb_theta_ctx_z_common_v(v, vec, nb, prec + guard); + acb_theta_sum_radius(R2, eps, &ctx_tau->cho, 0, + prec + FLINT_MAX(0, acb_theta_sum_addprec(distance))); + b = acb_theta_eld_set(E, &ctx_tau->cho, R2, v); + + if (b) + { + for (j = 0; j < g; j++) + { + sqr_pow[j] = _acb_vec_init(acb_theta_eld_box(E, j) + 1); + } + acb_theta_sum_sqr_pow(sqr_pow, ctx_tau->exp_tau, E, prec + guard); + + for (j = 0; j < nb; j++) + { + acb_theta_sum_work(th + j * n, n, (&vec[j])->exp_2z, + (&vec[j])->exp_2z_inv, ctx_tau->exp_tau, + ctx_tau->exp_tau_inv, sqr_pow, E, 0, prec, + (all_b ? acb_theta_sum_0b_worker : acb_theta_sum_00_worker)); + arb_mul_arf(err, &(&vec[j])->u, eps, guard); + for (k = 0; k < n; k++) + { + acb_add_error_arb(&th[j * n + k], err); + } + } + + for (j = 0; j < g; j++) + { + _acb_vec_clear(sqr_pow[j], acb_theta_eld_box(E, j) + 1); + } + } + else + { + /* Should not happen in tests */ + _acb_vec_indeterminate(th, nb * n); + } + + acb_theta_eld_clear(E); + arf_clear(R2); + arf_clear(eps); + arb_clear(err); + _arb_vec_clear(v, g); + flint_free(sqr_pow); +} + +static void +acb_theta_ctx_z_shift_a0(acb_theta_ctx_z_t res, acb_t c, const acb_theta_ctx_z_t ctx, + const acb_theta_ctx_tau_t ctx_tau, ulong a, slong prec) +{ + slong g = ctx_tau->g; + arb_ptr v_shift; + acb_t cinv; + arb_t abs; + slong j; + + v_shift = _arb_vec_init(g); + acb_init(cinv); + arb_init(abs); + + /* Do not set exp_z or exp_z_inv. */ + /* Replace exp_2z by analogs for z + tau a/2 */ + for (j = 0; j < g; j++) + { + acb_mul(&res->exp_2z[j], &ctx->exp_2z[j], + &ctx_tau->exp_tau_a[a * g + j], prec); + acb_mul(&res->exp_2z_inv[j], &ctx->exp_2z_inv[j], + &ctx_tau->exp_tau_a_inv[a * g + j], prec); + } + + /* Compute cofactor exp(pi i a^T z), and multiply by common cofactor + exp(pi i/4 a^T tau a) */ + acb_one(c); + for (j = 0; j < g; j++) + { + if (!acb_theta_char_bit(a, j, g)) + { + continue; + } + acb_mul(c, c, &ctx->exp_z[j], prec); + } + acb_mul(c, c, &ctx_tau->exp_a_tau_a_div_4[a], prec); + + /* Compute v; u and uinv must be multiplied by abs(c) */ + acb_abs(abs, c, prec); + arb_mul(&res->uinv, &ctx->uinv, abs, prec); + + arb_inv(abs, abs, prec); + if (acb_is_finite(c) && !arb_is_finite(abs)) + { + /* Recompute cinv by multiplications */ + acb_one(cinv); + for (j = 0; j < g; j++) + { + if (!acb_theta_char_bit(a, j, g)) + { + continue; + } + acb_mul(cinv, cinv, &ctx->exp_z_inv[j], prec); + } + acb_div(cinv, cinv, &ctx_tau->exp_a_tau_a_div_4[a], prec); + acb_abs(abs, cinv, prec); + } + arb_mul(&res->u, &ctx->u, abs, prec); + + acb_theta_char_get_arb(v_shift, a, g); + arb_mat_vector_mul_col(v_shift, &ctx_tau->cho, v_shift, prec); + _arb_vec_add(res->v, v_shift, ctx->v, g, prec); + + _arb_vec_clear(v_shift, g); + acb_clear(cinv); + arb_clear(abs); +} + +void +acb_theta_sum(acb_ptr th, const acb_theta_ctx_z_struct * vec, slong nb, + const acb_theta_ctx_tau_t ctx_tau, arb_srcptr distances, int all_a, + int all_b, int tilde, slong prec) +{ + slong g = ctx_tau->g; + slong n = 1 << g; + slong nba = (all_a ? n : 1); + slong nbb = (all_b ? n : 1); + slong nbab = nba * nbb; + slong guard = ACB_THETA_LOW_PREC; + acb_theta_ctx_z_struct * new_vec; + acb_ptr cs, res; + slong new_prec, dot; + slong j, a, b; + + FLINT_ASSERT(nb >= 0); + if (nb == 0) + { + return; + } + if (g == 1) + { + /* Find out if we want to use acb_modular_theta or acb_theta_sum_0x */ + new_prec = FLINT_MAX(prec, prec + acb_theta_sum_addprec(&distances[0])); + if (all_a) + { + new_prec = FLINT_MAX(new_prec, prec + acb_theta_sum_addprec(&distances[1])); + } + } + + if (g == 1 && new_prec <= 4 * prec) + { + /* Call acb_modular_theta_sum directly: we accept to run computations + at a slightly higher precision than necessary. */ + res = _acb_vec_init(4); + for (j = 0; j < nb; j++) + { + acb_modular_theta_sum(&res[0], &res[1], &res[2], &res[3], + (&vec[j])->exp_z, (&vec[j])->is_real, + acb_mat_entry(ctx_tau->exp_tau, 0, 0), 1, new_prec); + + acb_set(&th[nbab * j], &res[2]); + if (all_a && all_b) + { + acb_set(&th[nbab * j + 1], &res[3]); + acb_set(&th[nbab * j + 2], &res[1]); + acb_neg(&th[nbab * j + 3], &res[0]); + _acb_vec_scalar_mul(th + nbab * j + 2, th + nbab * j + 2, 2, + acb_mat_entry(ctx_tau->exp_tau_div_4, 0, 0), prec + guard); + } + else if (all_a) + { + acb_set(&th[nbab * j + 1], &res[1]); + acb_mul(&th[nbab * j + 1], &th[nbab * j + 1], + acb_mat_entry(ctx_tau->exp_tau_div_4, 0, 0), prec + guard); + } + else if (all_b) + { + acb_set(&th[nbab * j + 1], &res[3]); + } + } + _acb_vec_clear(res, 4); + } + else if (all_a) + { + new_vec = acb_theta_ctx_z_vec_init(nb, g); + res = _acb_vec_init(nbb * nb); + cs = _acb_vec_init(nb); + + for (a = 0; a < n; a++) + { + for (j = 0; j < nb; j++) + { + acb_theta_ctx_z_shift_a0(&new_vec[j], &cs[j], &vec[j], ctx_tau, a, prec + guard); + } + acb_theta_sum_0x(res, new_vec, nb, ctx_tau, &distances[a], all_b, prec); + for (j = 0; j < nb; j++) + { + _acb_vec_scalar_mul(th + nbab * j + nbb * a, res + nbb * j, + nbb, &cs[j], prec + guard); + } + for (b = 1; b < nbb; b++) /* No sign changes for b=0 */ + { + dot = acb_theta_char_dot(a, b, g); + for (j = 0; j < nb; j++) + { + acb_mul_i_pow_si(&th[nbab * j + nbb * a + b], &th[nbab * j + nbb * a + b], dot); + } + } + } + + acb_theta_ctx_z_vec_clear(new_vec, nb); + _acb_vec_clear(res, nbb * nb); + _acb_vec_clear(cs, nb); + } + else + { + acb_theta_sum_0x(th, vec, nb, ctx_tau, &distances[0], all_b, prec); + } + + if (tilde) + { + for (j = 0; j < nb; j++) + { + _acb_vec_scalar_mul_arb(th + nbab * j, th + nbab * j, nbab, + &(&vec[j])->uinv, prec + guard); + } + } +} diff --git a/src/acb_theta/dist_addprec.c b/src/acb_theta/sum_addprec.c similarity index 77% rename from src/acb_theta/dist_addprec.c rename to src/acb_theta/sum_addprec.c index 614a993557..8842591823 100644 --- a/src/acb_theta/dist_addprec.c +++ b/src/acb_theta/sum_addprec.c @@ -13,19 +13,23 @@ #include "acb_theta.h" slong -acb_theta_dist_addprec(const arb_t d2) +acb_theta_sum_addprec(const arb_t d2) { arb_t x; + arf_t b; slong prec = ACB_THETA_LOW_PREC; slong res; arb_init(x); + arf_init(b); + arb_const_log2(x, prec); arb_div(x, d2, x, prec); + arb_get_ubound_arf(b, x, prec); - if (arb_is_finite(x) && (arf_cmpabs_2exp_si(arb_midref(x), 30) <= 0)) + if (arf_is_finite(b) && (arf_cmpabs_2exp_si(b, 40) <= 0)) { - res = arf_get_si(arb_midref(x), prec); + res = arf_get_si(b, prec); } else /* should never happen */ { diff --git a/src/acb_theta/sum_jet.c b/src/acb_theta/sum_jet.c new file mode 100644 index 0000000000..145901001d --- /dev/null +++ b/src/acb_theta/sum_jet.c @@ -0,0 +1,414 @@ +/* + Copyright (C) 2025 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 3 of the License, or + (at your option) any later version. See . +*/ + +#include "acb.h" +#include "acb_mat.h" +#include "acb_modular.h" +#include "acb_theta.h" + +static void +acb_theta_sum_jet_0x_worker(acb_ptr th, acb_srcptr v1, acb_srcptr v2, + const slong * precs, slong len, const acb_t cofactor, const slong * coords, + slong ord, int all_b, slong g, slong prec, slong fullprec) +{ + slong nbth = (all_b ? (1 << g) : 1); + slong nbjet = acb_theta_jet_nb(ord, g); + slong * tups; + slong * dots; + acb_ptr v3, aux; + acb_t x, y; + fmpz_t num, t; + slong j, i, b; + + tups = flint_malloc(g * nbjet * sizeof(slong)); + dots = flint_malloc(nbth * sizeof(slong)); + v3 = _acb_vec_init(len); + aux = _acb_vec_init(nbth * nbjet); + acb_init(x); + acb_init(y); + fmpz_init(num); + fmpz_init(t); + + for (b = 0; b < nbth; 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 < nbjet; 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]); + for (b = 0; b < nbth; b++) + { + acb_mul_i_pow_si(y, x, 2 * ((dots[b] + i * acb_theta_char_bit(b, 0, g)) % 4)); + acb_add(&aux[b * nbjet + j], &aux[b * nbjet + j], y, prec); + } + } + + /* Multiply by cofactor * num */ + acb_mul_fmpz(x, cofactor, num, prec); + for (b = 0; b < nbth; b++) + { + acb_mul(&aux[b * nbjet + j], &aux[b * nbjet + j], x, prec); + } + } + _acb_vec_add(th, th, aux, nbth * nbjet, fullprec); + + flint_free(tups); + flint_free(dots); + _acb_vec_clear(v3, len); + _acb_vec_clear(aux, nbth * nbjet); + acb_clear(x); + acb_clear(y); + fmpz_clear(num); + fmpz_clear(t); + +} + +static void +acb_theta_sum_jet_00_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_theta_sum_jet_0x_worker(th, v1, v2, precs, len, cofactor, coords, ord, + 0, g, prec, fullprec); +} + +static void +acb_theta_sum_jet_0b_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_theta_sum_jet_0x_worker(th, v1, v2, precs, len, cofactor, coords, ord, + 1, g, prec, fullprec); +} + +/* To compute derivatives of all theta values, we use a big ellipsoid to avoid + complicated formulas for composing derivatives; this introduces powers of i + in the worker */ + +static 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] & 1; + } + + return a; +} + +static void +acb_theta_sum_jet_ax_worker(acb_ptr th, acb_srcptr v1, acb_srcptr v2, + const slong * precs, slong len, const acb_t cofactor, const slong * coords, + slong ord, int all_b, slong g, slong prec, slong fullprec) +{ + slong n = 1 << g; + slong nbb = (all_b ? n : 1); + slong nbjet = 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 * nbjet * sizeof(slong)); + dots = flint_malloc(nbb * sizeof(slong)); + v3 = _acb_vec_init(len); + aux = _acb_vec_init(nbjet * nbb * 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 < nbb; 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 < nbjet; 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 < nbb; b++) + { + acb_mul_i_pow_si(y, x, (dots[b] + i * acb_theta_char_bit(b, 0, g)) % 4); + if (i % 2 == 0) + { + acb_add(&aux[(nbb * a0 + b) * nbjet + j], + &aux[(nbb * a0 + b) * nbjet + j], y, prec); + } + else + { + acb_add(&aux[(nbb * a1 + b) * nbjet + j], + &aux[(nbb * a1 + b) * nbjet + j], y, prec); + } + } + } + + /* Multiply by cofactor * num */ + acb_mul_fmpz(x, cofactor, num, prec); + for (b = 0; b < nbb; b++) + { + acb_mul(&aux[(nbb * a0 + b) * nbjet + j], + &aux[(nbb * a0 + b) * nbjet + j], x, prec); + acb_mul(&aux[(nbb * a1 + b) * nbjet + j], + &aux[(nbb * a1 + b) * nbjet + j], x, prec); + } + } + + _acb_vec_add(th, th, aux, nbjet * nbb * n, fullprec); + + flint_free(tups); + flint_free(dots); + _acb_vec_clear(v3, len); + _acb_vec_clear(aux, nbjet * nbb * n); + acb_clear(x); + acb_clear(y); + fmpz_clear(num); + fmpz_clear(t); +} + + +static void +acb_theta_sum_jet_a0_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_theta_sum_jet_ax_worker(th, v1, v2, precs, len, cofactor, coords, ord, + 0, g, prec, fullprec); +} + +static void +acb_theta_sum_jet_all_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_theta_sum_jet_ax_worker(th, v1, v2, precs, len, cofactor, coords, ord, + 1, g, prec, fullprec); +} + +void +acb_theta_sum_jet(acb_ptr th, const acb_theta_ctx_z_struct * vec, slong nb, + const acb_theta_ctx_tau_t ctx_tau, slong ord, int all_a, int all_b, slong prec) +{ + slong g = ctx_tau->g; + slong n = 1 << g; + slong nbjet = acb_theta_jet_nb(ord, g); + slong nbth = (all_a ? n : 1) * (all_b ? n : 1); + slong guard = ACB_THETA_LOW_PREC; + slong j, k; + + FLINT_ASSERT(nb >= 0); + if (nb == 0) + { + return; + } + + if (g == 1) + { + acb_ptr res; + + res = _acb_vec_init(4 * nbjet); + for (j = 0; j < nb; j++) + { + /* acb_modular_theta_sum recomputes the inverse of exp_z */ + acb_modular_theta_sum(res, res + nbjet, res + 2 * nbjet, res + 3 * nbjet, + (&vec[j])->exp_z, (&vec[j])->is_real, + acb_mat_entry(ctx_tau->exp_tau, 0, 0), ord + 1, prec); + _acb_vec_set(th + j * nbth * nbjet, res + 2 * nbjet, nbjet); + if (all_a && all_b) + { + _acb_vec_set(th + (4 * j + 1) * nbjet, res + 3 * nbjet, nbjet); + _acb_vec_set(th + (4 * j + 2) * nbjet, res + nbjet, nbjet); + _acb_vec_neg(th + (4 * j + 3) * nbjet, res, nbjet); + _acb_vec_scalar_mul(th + (4 * j + 2) * nbjet, th + (4 * j + 2) * nbjet, 2 * nbjet, + acb_mat_entry(ctx_tau->exp_tau_div_4, 0, 0), prec); + } + else if (all_a) + { + _acb_vec_set(th + (2 * j + 1) * nbjet, res + nbjet, nbjet); + _acb_vec_scalar_mul(th + (2 * j + 1) * nbjet, th + (2 * j + 1) * nbjet, nbjet, + acb_mat_entry(ctx_tau->exp_tau_div_4, 0, 0), prec); + } + else if (all_b) + { + _acb_vec_set(th + (2 * j + 1) * nbjet, res + 3 * nbjet, nbjet); + } + } + _acb_vec_clear(res, 4 * nbjet); + } + else + { + acb_theta_eld_t E; + acb_ptr * sqr_pow; + arf_t R2, eps; + arb_t err; + arb_ptr v; + slong * tups; + fmpz_t m, t; + acb_t c; + int b; + + acb_theta_eld_init(E, g, g); + sqr_pow = flint_malloc(g * sizeof(acb_ptr)); + arf_init(R2); + arf_init(eps); + arb_init(err); + v = _arb_vec_init(g); + tups = flint_malloc(g * nbjet * sizeof(slong)); + acb_init(c); + fmpz_init(m); + fmpz_init(t); + + acb_theta_ctx_z_common_v(v, vec, nb, prec + guard); + acb_theta_sum_jet_radius(R2, eps, &ctx_tau->cho, v, ord, prec); + if (all_a) + { + /* Take into account that everything is duplicated in worker */ + _arb_vec_scalar_mul_2exp_si(v, v, g, 1); + arf_mul_2exp_si(R2, R2, 2); + } + b = acb_theta_eld_set(E, &ctx_tau->cho, R2, v); + + if (b) + { + for (j = 0; j < g; j++) + { + sqr_pow[j] = _acb_vec_init(acb_theta_eld_box(E, j) + 1); + } + /* If all_a, use exp_z, exp_tau_div_4 instead of exp_2z, exp_tau */ + acb_theta_sum_sqr_pow(sqr_pow, + (all_a ? ctx_tau->exp_tau_div_4 : ctx_tau->exp_tau), + E, prec + guard); + + /* Sum series, rescale by c factor */ + for (j = 0; j < nb; j++) + { + if (all_a) + { + acb_theta_sum_work(th + j * nbth * nbjet, nbth * nbjet, + (&vec[j])->exp_z, (&vec[j])->exp_z_inv, + ctx_tau->exp_tau_div_4, ctx_tau->exp_tau_div_4_inv, + sqr_pow, E, ord, prec, + (all_b ? acb_theta_sum_jet_all_worker : acb_theta_sum_jet_a0_worker)); + } + else + { + acb_theta_sum_work(th + j * nbth * nbjet, nbth * nbjet, + (&vec[j])->exp_2z,(&vec[j])->exp_2z_inv, + ctx_tau->exp_tau, ctx_tau->exp_tau_inv, + sqr_pow, E, ord, prec, + (all_b ? acb_theta_sum_jet_0b_worker : acb_theta_sum_jet_00_worker)); + } + arb_mul_arf(err, &(&vec[j])->u, eps, guard); + for (k = 0; k < nbth * nbjet; k++) + { + acb_add_error_arb(&th[j * nbth * nbjet + k], err); + } + } + + /* Rescale by factorials and powers of 2pi*i */ + acb_theta_jet_tuples(tups, ord, g); + for (k = 0; k < nbjet; k++) + { + acb_const_pi(c, prec); + if (!all_a) + { + 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); + for (j = 0; j < nb * nbth; j++) + { + acb_mul(&th[j * nbjet + k], &th[j * nbjet + k], c, prec); + } + } + + for (j = 0; j < g; j++) + { + _acb_vec_clear(sqr_pow[j], acb_theta_eld_box(E, j) + 1); + } + } + else + { + /* Should not happen in tests */ + _acb_vec_indeterminate(th, nb * nbth * nbjet); + } + + acb_theta_eld_clear(E); + flint_free(sqr_pow); + arf_clear(R2); + arf_clear(eps); + arb_clear(err); + _arb_vec_clear(v, g); + flint_free(tups); + acb_clear(c); + fmpz_clear(m); + fmpz_clear(t); + } +} diff --git a/src/acb_theta/jet_naive_radius.c b/src/acb_theta/sum_jet_radius.c similarity index 91% rename from src/acb_theta/jet_naive_radius.c rename to src/acb_theta/sum_jet_radius.c index 3a54ed0e93..d76d2c42e3 100644 --- a/src/acb_theta/jet_naive_radius.c +++ b/src/acb_theta/sum_jet_radius.c @@ -14,7 +14,7 @@ #include "acb_theta.h" void -acb_theta_jet_naive_radius(arf_t R2, arf_t eps, const arb_mat_t C, arb_srcptr v, +acb_theta_sum_jet_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); @@ -45,7 +45,7 @@ acb_theta_jet_naive_radius(arf_t R2, arf_t eps, const arb_mat_t C, arb_srcptr v, arf_set_mag(arb_midref(nx), norm); /* Get R2, eps assuming R <= nx/na */ - acb_theta_naive_radius(R2, eps, C, 0, prec); + acb_theta_sum_radius(R2, eps, C, 0, prec); arb_mul_2exp_si(t, nx, 1); arb_one(u); arb_max(t, t, u, lp); @@ -59,7 +59,7 @@ acb_theta_jet_naive_radius(arf_t R2, arf_t eps, const arb_mat_t C, arb_srcptr v, arb_get_lbound_arf(cmp, t, lp); if (arf_cmp(cmp, R2) <= 0) { - acb_theta_naive_radius(R2, eps, C, ord, prec); + acb_theta_sum_radius(R2, eps, C, ord, prec); arb_div(t, nx, na, lp); arb_get_ubound_arf(cmp, t, lp); arf_max(R2, R2, cmp); diff --git a/src/acb_theta/naive_radius.c b/src/acb_theta/sum_radius.c similarity index 80% rename from src/acb_theta/naive_radius.c rename to src/acb_theta/sum_radius.c index 47054cf206..eb6f00d873 100644 --- a/src/acb_theta/naive_radius.c +++ b/src/acb_theta/sum_radius.c @@ -76,22 +76,36 @@ invert_lin_plus_log(arf_t R2, slong a, const arb_t b, slong prec) } void -acb_theta_naive_radius(arf_t R2, arf_t eps, const arb_mat_t C, slong ord, slong prec) +acb_theta_sum_radius(arf_t R2, arf_t eps, const arb_mat_t cho, slong ord, slong prec) { - slong g = arb_mat_nrows(C); + slong g = arb_mat_nrows(cho); slong lp = ACB_THETA_LOW_PREC; - arb_t b, temp; + arb_t b, temp, sqrt2pi; arf_t cmp; slong k; arb_init(b); arb_init(temp); + arb_init(sqrt2pi); 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); + arb_const_pi(sqrt2pi, lp); + arb_mul_2exp_si(sqrt2pi, sqrt2pi, 1); + arb_sqrt(sqrt2pi, sqrt2pi, lp); + + /* Set b such that + (1 + 8/sqrt(pi)) * prod_j (1 + sqrt(2pi)/c_j) * b \leq 2^(-prec) */ + arb_set_si(b, 4); + arb_div(b, b, sqrt2pi, lp); + arb_add_si(b, b, 1, lp); + for (k = 0; k < g; k++) + { + arb_div(temp, sqrt2pi, arb_mat_entry(cho, k, k), lp); + arb_add_si(temp, temp, 1, lp); + arb_mul(b, b, temp, lp); + } + arb_inv(b, b, lp); + arb_mul_2exp_si(b, b, -prec); /* Solve R2^((g-1)/2+ord) exp(-R2) \leq b */ arb_log(b, b, lp); @@ -102,16 +116,9 @@ acb_theta_naive_radius(arf_t R2, arf_t eps, const arb_mat_t C, slong ord, slong 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); + /* Set error 2^(-prec) */ + arf_one(eps); + arf_mul_2exp_si(eps, eps, -prec); arb_clear(b); arb_clear(temp); diff --git a/src/acb_theta/sum_sqr_pow.c b/src/acb_theta/sum_sqr_pow.c new file mode 100644 index 0000000000..fcf0e1cd2c --- /dev/null +++ b/src/acb_theta/sum_sqr_pow.c @@ -0,0 +1,44 @@ +/* + Copyright (C) 2025 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 3 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_sum_sqr_pow(acb_ptr * sqr_pow, const acb_mat_t exp_tau, const acb_theta_eld_t E, slong prec) +{ + slong g = acb_mat_nrows(exp_tau); + acb_t c, dc, ddc; + slong k, j; + + acb_init(c); + acb_init(dc); + acb_init(ddc); + + /* 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); +} diff --git a/src/acb_theta/naive_term.c b/src/acb_theta/sum_term.c similarity index 95% rename from src/acb_theta/naive_term.c rename to src/acb_theta/sum_term.c index 483c37335d..4fd925aed2 100644 --- a/src/acb_theta/naive_term.c +++ b/src/acb_theta/sum_term.c @@ -14,7 +14,7 @@ #include "acb_theta.h" void -acb_theta_naive_term(acb_t res, acb_srcptr z, const acb_mat_t tau, +acb_theta_sum_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); diff --git a/src/acb_theta/naive_worker.c b/src/acb_theta/sum_work.c similarity index 60% rename from src/acb_theta/naive_worker.c rename to src/acb_theta/sum_work.c index 5ecf510549..01e4d6bdb9 100644 --- a/src/acb_theta/naive_worker.c +++ b/src/acb_theta/sum_work.c @@ -1,5 +1,5 @@ /* - Copyright (C) 2023 Jean Kieffer + Copyright (C) 2024 Jean Kieffer This file is part of FLINT. @@ -10,24 +10,25 @@ */ #include +#include "ulong_extras.h" #include "acb.h" #include "acb_mat.h" #include "acb_theta.h" static slong -acb_theta_naive_fullprec(const acb_theta_eld_t E, slong prec) +acb_theta_sum_fullprec(const acb_theta_eld_t E, slong prec) { - return prec + FLINT_MAX(prec + FLINT_FLOG2(1 + acb_theta_eld_nb_pts(E)), + return prec + FLINT_MAX(prec + ceil(n_flog(1 + acb_theta_eld_nb_pts(E), 2)), ACB_THETA_LOW_PREC); } -FLINT_FORCE_INLINE slong -acb_theta_naive_newprec(slong prec, slong coord, slong dist, slong max_dist, slong ord) +static slong +acb_theta_sum_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 * FLINT_CLOG2(1 + FLINT_ABS(coord)); + double pos = ord * n_clog(1 + FLINT_ABS(coord), 2); return FLINT_MAX(ACB_THETA_LOW_PREC, ceil((double) prec - neg + pos)); } @@ -35,30 +36,30 @@ acb_theta_naive_newprec(slong prec, slong coord, slong dist, slong max_dist, slo /* 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, +acb_theta_sum_work_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) + acb_theta_sum_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 g = E->ambient_dim; + slong min = E->min; + slong mid = E->mid; + slong max = E->max; + slong len = E->nb_pts; 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); + coords[k] = E->last_coords[k - 1]; } /* 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); + precs[k - min] = acb_theta_sum_newprec(prec, k, k - mid, max - mid, ord); if ((k == mid) && (k >= 0)) { acb_pow_ui(&v1[mid - min], lin, mid, prec); @@ -79,7 +80,7 @@ acb_theta_naive_call_dim1(acb_ptr th, acb_ptr v1, acb_ptr v2, slong * precs, } for (k = mid - 1; k >= min; k--) { - precs[k - min] = acb_theta_naive_newprec(prec, k, k - mid, max - mid, ord); + precs[k - min] = acb_theta_sum_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]); @@ -99,19 +100,15 @@ acb_theta_naive_call_dim1(acb_ptr th, acb_ptr v1, acb_ptr v2, slong * precs, /* 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_theta_sum_work_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 fullprec, acb_theta_sum_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); + slong d = E->dim; + slong g = E->ambient_dim; + slong mid = E->mid; 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; @@ -134,7 +131,7 @@ acb_theta_naive_worker_rec(acb_ptr th, acb_ptr v1, acb_ptr v2, slong * precs, 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, + acb_theta_sum_work_dim1(th, v1, v2, precs, lin_cf, lin_cf_inv, cf, sqr_pow[0], E, ord, prec, fullprec, worker); acb_clear(lin_cf); @@ -194,10 +191,10 @@ acb_theta_naive_worker_rec(acb_ptr th, acb_ptr v1, acb_ptr v2, slong * precs, 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++) + for (k = 0; k < (E->nr); k++) { c = mid + k; - newprec = acb_theta_naive_newprec(prec, c, c - mid, max - mid, ord); + newprec = acb_theta_sum_newprec(prec, c, c - mid, (E->max) - mid, ord); if (k > 0) /* Update lin_cf, lin_pow using diff */ { for (j = 0; j < d - 1; j++) @@ -211,8 +208,8 @@ acb_theta_naive_worker_rec(acb_ptr th, acb_ptr v1, acb_ptr v2, slong * precs, } 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), + acb_theta_sum_work_rec(th, v1, v2, precs, lin_pow, lin_pow_inv, full_cf, + exp_z, exp_z_inv, exp_tau, exp_tau_inv, sqr_pow, &E->rchildren[k], ord, newprec, fullprec, worker); } @@ -223,10 +220,10 @@ acb_theta_naive_worker_rec(acb_ptr th, acb_ptr v1, acb_ptr v2, slong * precs, 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++) + for (k = 0; k < (E->nl); k++) { c = mid - (k + 1); - newprec = acb_theta_naive_newprec(prec, c, mid - c, mid - min, ord); + newprec = acb_theta_sum_newprec(prec, c, mid - c, mid - (E->min), ord); for (j = 0; j < d - 1; j++) { acb_mul(acb_mat_entry(lin_pow, j, d - 1), @@ -237,8 +234,8 @@ acb_theta_naive_worker_rec(acb_ptr th, acb_ptr v1, acb_ptr v2, slong * precs, 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), + acb_theta_sum_work_rec(th, v1, v2, precs, lin_pow, lin_pow_inv, full_cf, + exp_z, exp_z_inv, exp_tau, exp_tau_inv, sqr_pow, &E->lchildren[k], ord, newprec, fullprec, worker); } @@ -253,124 +250,52 @@ acb_theta_naive_worker_rec(acb_ptr th, acb_ptr v1, acb_ptr v2, slong * precs, _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 */ +/* Main 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) +acb_theta_sum_work(acb_ptr th, slong len, 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, acb_theta_sum_worker_t worker) { - slong g = acb_theta_eld_ambient_dim(E); - slong fullprec = acb_theta_naive_fullprec(E, prec); + slong g = E->ambient_dim; + slong fullprec = acb_theta_sum_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; + + acb_mat_t lin_pow, lin_pow_inv; + acb_ptr v1, v2, res; slong * precs; acb_t cf; - slong j, k; + slong j; 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); + res = _acb_vec_init(len); 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_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_theta_sum_work_rec(res, 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); - 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_vec_clear(res, len); acb_clear(cf); flint_free(precs); } diff --git a/src/acb_theta/test/main.c b/src/acb_theta/test/main.c index dae3eb11cc..52de9eac6d 100644 --- a/src/acb_theta/test/main.c +++ b/src/acb_theta/test/main.c @@ -11,149 +11,107 @@ /* 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-char_shuffle.c" +#include "t-char_table.c" +#include "t-ctx_exp_inv.c" +#include "t-ctx_sqr_inv.c" +#include "t-ctx_tau_dupl.c" +#include "t-ctx_z_add_real.c" +#include "t-ctx_z_dupl.c" #include "t-eld_border.c" +#include "t-eld_distances.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_chi35.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_even_weight.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.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_notransform.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-ql_exact.c" +#include "t-ql_jet_error.c" +#include "t-ql_jet_fd.c" +#include "t-ql_local_bound.c" +#include "t-ql_lower_dim.c" +#include "t-ql_setup.c" +#include "t-reduce_z.c" #include "t-siegel_cocycle.c" #include "t-siegel_is_reduced.c" +#include "t-siegel_kappa.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" +#include "t-sum.c" +#include "t-sum_jet.c" +#include "t-sum_jet_radius.c" +#include "t-sum_radius.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_char_shuffle), + TEST_FUNCTION(acb_theta_char_table), + TEST_FUNCTION(acb_theta_ctx_exp_inv), + TEST_FUNCTION(acb_theta_ctx_sqr_inv), + TEST_FUNCTION(acb_theta_ctx_tau_dupl), + TEST_FUNCTION(acb_theta_ctx_z_add_real), + TEST_FUNCTION(acb_theta_ctx_z_dupl), TEST_FUNCTION(acb_theta_eld_border), + TEST_FUNCTION(acb_theta_eld_distances), 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_chi35), 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_even_weight), 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), 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_notransform), 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_ql_exact), + TEST_FUNCTION(acb_theta_ql_jet_error), + TEST_FUNCTION(acb_theta_ql_jet_fd), + TEST_FUNCTION(acb_theta_ql_local_bound), + TEST_FUNCTION(acb_theta_ql_lower_dim), + TEST_FUNCTION(acb_theta_ql_setup), + TEST_FUNCTION(acb_theta_reduce_z), TEST_FUNCTION(acb_theta_siegel_cocycle), TEST_FUNCTION(acb_theta_siegel_is_reduced), + TEST_FUNCTION(acb_theta_siegel_kappa), 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) + TEST_FUNCTION(acb_theta_sum), + TEST_FUNCTION(acb_theta_sum_jet), + TEST_FUNCTION(acb_theta_sum_jet_radius), + TEST_FUNCTION(acb_theta_sum_radius), }; /* main function *************************************************************/ diff --git a/src/acb_theta/test/t-agm_hadamard.c b/src/acb_theta/test/t-agm_hadamard.c deleted file mode 100644 index 4e77f7ad17..0000000000 --- a/src/acb_theta/test/t-agm_hadamard.c +++ /dev/null @@ -1,58 +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 3 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 index 598706eb88..04836be464 100644 --- a/src/acb_theta/test/t-agm_mul.c +++ b/src/acb_theta/test/t-agm_mul.c @@ -10,6 +10,7 @@ */ #include "test_helpers.h" +#include "acb.h" #include "acb_mat.h" #include "acb_theta.h" @@ -17,72 +18,88 @@ TEST_FUNCTION_START(acb_theta_agm_mul, state) { slong iter; - /* Test: duplication formula */ + /* Test: duplication formula using sum */ 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; - + slong mprec = 100 + n_randint(state, 100); + slong prec = mprec + 50; + slong bits = n_randint(state, 2); + int all = iter % 2; + slong nbth = (all ? n * n : n); 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_theta_ctx_tau_t ctx_tau; + acb_theta_ctx_z_t ctx0, ctx; + arb_ptr ds; + acb_ptr th2, th_dupl, test; + slong j; acb_mat_init(tau, g, g); - arf_init(rad); z = _acb_vec_init(2 * g); - th = _acb_vec_init(2 * n); + acb_theta_ctx_tau_init(ctx_tau, 1, g); + acb_theta_ctx_z_init(ctx0, g); + acb_theta_ctx_z_init(ctx, g); + th2 = _acb_vec_init(2 * nbth); th_dupl = _acb_vec_init(2 * n); - test = _acb_vec_init(2 * n); - arb_init(err); + test = _acb_vec_init(2 * nbth); + ds = _arb_vec_init(2 * n); + + acb_siegel_randtest_reduced(tau, state, prec, bits); + acb_siegel_randtest_vec_reduced(z + g, state, 1, tau, 0, prec); + + acb_theta_ctx_tau_set(ctx_tau, tau, prec); + acb_theta_ctx_z_set(ctx0, z, ctx_tau, prec); + acb_theta_ctx_z_set(ctx, z + g, ctx_tau, prec); + acb_theta_eld_distances(ds, z, 2, tau, prec); - 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++) + /* Make test vector using sum, squared */ + acb_theta_sum(test, ctx0, 1, ctx_tau, ds, 1, all, 1, prec); + acb_theta_sum(test + nbth, ctx, 1, ctx_tau, ds + n, 1, all, 1, prec); + for (j = 0; j < 2 * nbth; j++) { - acb_urandom(&z[k], state, prec); + acb_sqr(&test[j], &test[j], 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); + /* Duplicate to get input of agm_mul */ + acb_theta_ctx_tau_dupl(ctx_tau, prec); + acb_theta_ctx_z_dupl(ctx0, prec); + acb_theta_ctx_z_dupl(ctx, prec); + _arb_vec_scalar_mul_2exp_si(ds, ds, 2 * n, 1); + acb_theta_sum(th_dupl, ctx0, 1, ctx_tau, ds, 1, 0, 1, prec); + acb_theta_sum(th_dupl + n, ctx, 1, ctx_tau, ds + n, 1, 0, 1, prec); - acb_theta_agm_mul(test, th, th + n, g, prec); - acb_theta_agm_mul(test + n, th + n, th + n, g, prec); + /* Call agm_mul at precision mprec and compare with test */ + acb_theta_agm_mul(th2, th_dupl, th_dupl, g, all, mprec); + acb_theta_agm_mul(th2 + nbth, th_dupl, th_dupl + n, g, all, mprec); - if (!_acb_vec_overlaps(test, th_dupl, 2 * n)) + if (!_acb_vec_overlaps(test, th2, 2 * nbth) + || !_acb_vec_is_finite(test, 2 * nbth) + || !_acb_vec_is_finite(th2, 2 * nbth)) { - flint_printf("FAIL (overlap)\n"); + flint_printf("FAIL\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); + acb_mat_printd(tau, 5); + _acb_vec_printd(z, 2 * g, 5); + flint_printf("th2:\n"); + _acb_vec_printd(th2, 2 * nbth, 5); + flint_printf("th_dupl:\n"); + _acb_vec_printd(th_dupl, 2 * n, 5); flint_printf("test:\n"); - _acb_vec_printd(test, 2 * n, 10); - fflush(stdout); + _acb_vec_printd(test, 2 * nbth, 5); flint_abort(); } acb_mat_clear(tau); - arf_clear(rad); _acb_vec_clear(z, 2 * g); - _acb_vec_clear(th, 2 * n); + acb_theta_ctx_tau_clear(ctx_tau); + acb_theta_ctx_z_clear(ctx0); + acb_theta_ctx_z_clear(ctx); + _acb_vec_clear(th2, 2 * nbth); _acb_vec_clear(th_dupl, 2 * n); - _acb_vec_clear(test, 2 * n); - arb_clear(err); + _acb_vec_clear(test, 2 * nbth); + _arb_vec_clear(ds, 2 * n); } 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 index 757f816340..93c85998ee 100644 --- a/src/acb_theta/test/t-agm_mul_tight.c +++ b/src/acb_theta/test/t-agm_mul_tight.c @@ -10,6 +10,8 @@ */ #include "test_helpers.h" +#include "arb.h" +#include "acb.h" #include "acb_mat.h" #include "acb_theta.h" @@ -17,115 +19,149 @@ TEST_FUNCTION_START(acb_theta_agm_mul_tight, state) { slong iter; - /* Test: respects relative precision */ + /* Test: respects relative precision, and agrees with agm_mul at huge 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 mprec = 50 + n_randint(state, 200); + slong prec = mprec + 500; slong bits = n_randint(state, 3); slong delta = 25; + int all = iter % 2; + slong nbth = (all ? n * n : n); acb_mat_t tau; acb_ptr z; - acb_ptr th, th0, r; - arb_ptr d, d0; + acb_ptr th, r, test; + arb_ptr ds, exps; arb_t x, t; - arf_t eps; - slong k; + arf_t eps, u; + slong k, b; 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); + z = _acb_vec_init(2 * g); + r = _acb_vec_init(nbth); + test = _acb_vec_init(nbth); + th = _acb_vec_init(2 * n); + ds = _arb_vec_init(2 * n); + exps = _arb_vec_init(2 * n); arb_init(x); arb_init(t); arf_init(eps); + arf_init(u); /* 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); + acb_siegel_randtest_vec_reduced(z + g, state, 1, tau, 0, prec); + acb_theta_eld_distances(ds, z, 2, tau, prec); /* Generate values */ - for (k = 0; k < n; k++) + for (k = 0; k < 2 * n; k++) { - arb_neg(x, &d[k]); - arb_exp(x, x, prec); + arb_neg(&exps[k], &ds[k]); + arb_exp(&exps[k], &exps[k], prec); acb_urandom(&th[k], state, prec); - acb_mul_arb(&th[k], &th[k], x, prec); + acb_mul_arb(&th[k], &th[k], &exps[k], prec); + } + + acb_theta_agm_mul(test, th, th + n, g, all, 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); + /* Add error so that relative precision is roughly mprec */ + for (k = 0; k < 2 * n; k++) + { + arb_one(x); + arb_mul_2exp_si(x, x, -mprec); + arb_mul(x, x, &exps[k], prec); + acb_add_arb(&th[k], &th[k], x, prec); + acb_add_error_arb(&th[k], x); } - acb_theta_agm_mul_tight(r, th0, th, d0, d, g, mprec); + acb_theta_agm_mul_tight(r, th, th + n, ds, ds + n, g, all, mprec); + + if (!_acb_vec_overlaps(r, test, nbth) + || !_acb_vec_is_finite(r, nbth) + || !_acb_vec_is_finite(test, nbth)) + { + flint_printf("FAIL (overlap)\n"); + flint_abort(); + } 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)) + if (all) + { + arb_zero(x); + for (b = 0; b < n; b++) + { + acb_abs(t, &r[k * n + b], prec); + arb_max(x, x, t, prec); + } + } + else + { + acb_abs(x, &r[k], prec); + } + arb_mul_2exp_si(x, x, -g); + + if (arb_gt(x, &exps[n + k])) { 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); + _arb_vec_printd(ds, 2 * n, 5); flint_printf("values:\n"); - _acb_vec_printd(th0, n, 5); - _acb_vec_printd(th, n, 5); + _acb_vec_printd(th, 2 * n, 5); flint_printf("result:\n"); - _acb_vec_printd(r, n, 5); + _acb_vec_printd(r, nbth, 5); flint_abort(); } - acb_get_rad_ubound_arf(eps, &r[k], prec); + if (all) + { + arf_zero(eps); + for (b = 0; b < n; b++) + { + acb_get_rad_ubound_arf(u, &r[k * n + b], prec); + arf_max(eps, eps, u); + } + } + else + { + 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)) + arb_mul_2exp_si(x, x, mprec - delta); + + if (arb_gt(x, &exps[n + k])) { 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); + _arb_vec_printd(ds, 2 * n, 5); flint_printf("values:\n"); - _acb_vec_printd(th0, n, 5); - _acb_vec_printd(th, n, 5); + _acb_vec_printd(th, 2 * n, 5); flint_printf("result:\n"); - _acb_vec_printd(r, n, 5); - flint_printf("x, t:\n"); + _acb_vec_printd(r, nbth, 5); + flint_printf("x:\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); + _acb_vec_clear(z, 2 * g); + _acb_vec_clear(r, nbth); + _acb_vec_clear(test, nbth); + _acb_vec_clear(th, 2 * n); + _arb_vec_clear(ds, 2 * n); + _arb_vec_clear(exps, 2 * n); arb_clear(x); arb_clear(t); arf_clear(eps); + arf_clear(u); } TEST_FUNCTION_END(state); diff --git a/src/acb_theta/test/t-agm_sqrt.c b/src/acb_theta/test/t-agm_sqrt.c index 7900b02c38..9992aeadfb 100644 --- a/src/acb_theta/test/t-agm_sqrt.c +++ b/src/acb_theta/test/t-agm_sqrt.c @@ -19,7 +19,7 @@ TEST_FUNCTION_START(acb_theta_agm_sqrt, state) /* Test: - if nonzero, value of square root should agree; precision remains high - - no abort on wrong values of rt_low */ + - indeterminate on wrong values of rt_low */ for (iter = 0; iter < 1000 * flint_test_multiplier(); iter++) { slong prec = 100 + n_randint(state, 1000); @@ -42,11 +42,10 @@ TEST_FUNCTION_START(acb_theta_agm_sqrt, state) } acb_sqr(x, rt, prec); - acb_one(t); + arb_zero_pm_one(acb_realref(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); @@ -79,10 +78,20 @@ TEST_FUNCTION_START(acb_theta_agm_sqrt, state) flint_abort(); } - if (iter % 10 == 1) + acb_randtest_precise(x, state, prec, mag_bits); + acb_randtest_precise(rt_low, state, lowprec, mag_bits); + acb_theta_agm_sqrt(t, x, rt_low, 1, prec); + acb_sqr(rt_low, rt_low, lowprec); + if (!acb_overlaps(rt_low, x) && acb_is_finite(t)) { - acb_randtest(rt_low, state, prec, mag_bits); - acb_theta_agm_sqrt(t, x, rt_low, 1, prec); + flint_printf("FAIL (indeterminate)\n"); + acb_printd(x, 5); + flint_printf("\n"); + acb_printd(t, 5); + flint_printf("\n"); + acb_printd(rt_low, 5); + flint_printf("\n"); + flint_abort(); } acb_clear(rt); diff --git a/src/acb_theta/test/t-all.c b/src/acb_theta/test/t-all.c deleted file mode 100644 index 2725178e5c..0000000000 --- a/src/acb_theta/test/t-all.c +++ /dev/null @@ -1,79 +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 3 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 index 723e3523f0..bcee67ab40 100644 --- a/src/acb_theta/test/t-char_dot.c +++ b/src/acb_theta/test/t-char_dot.c @@ -27,7 +27,8 @@ TEST_FUNCTION_START(acb_theta_char_dot, state) acb_ptr v, w; fmpz_t m; slong x1, x2; - acb_t x3, x4; + acb_t x4; + slong j; int res; n = flint_malloc(g * sizeof(slong)); @@ -35,33 +36,30 @@ TEST_FUNCTION_START(acb_theta_char_dot, state) 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); + for (j = 0; j < g; j++) + { + n[j] = acb_theta_char_bit(b, j, 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)) + if (x1 != x2) { 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_printf("x1 = %wd, x2 = %wd\n", x1, x2); flint_abort(); } - acb_mul_2exp_si(x3, x3, 2); - res = acb_get_unique_fmpz(m, x3); + acb_theta_char_get_acb(v, b, g); + acb_theta_char_get_acb(w, a, g); + acb_dot(x4, NULL, 0, v, 1, w, 1, g, prec); + acb_mul_2exp_si(x4, x4, 2); + res = acb_get_unique_fmpz(m, x4); fmpz_sub_si(m, m, x1); + if (!res || !fmpz_divisible_si(m, 4)) { flint_printf("FAIL (mod 4)\n"); @@ -71,7 +69,6 @@ TEST_FUNCTION_START(acb_theta_char_dot, state) flint_free(n); _acb_vec_clear(v, g); _acb_vec_clear(w, g); - acb_clear(x3); acb_clear(x4); fmpz_clear(m); } diff --git a/src/acb_theta/test/t-char_get_a.c b/src/acb_theta/test/t-char_get_a.c deleted file mode 100644 index e1ddc4cb93..0000000000 --- a/src/acb_theta/test/t-char_get_a.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 3 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 deleted file mode 100644 index 32af1da53d..0000000000 --- a/src/acb_theta/test/t-char_is_even.c +++ /dev/null @@ -1,49 +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 3 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 deleted file mode 100644 index 23f7927494..0000000000 --- a/src/acb_theta/test/t-char_is_goepel.c +++ /dev/null @@ -1,52 +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 3 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 deleted file mode 100644 index 1eb23a5afe..0000000000 --- a/src/acb_theta/test/t-char_is_syzygous.c +++ /dev/null @@ -1,49 +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 3 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-transform_proj.c b/src/acb_theta/test/t-char_shuffle.c similarity index 89% rename from src/acb_theta/test/t-transform_proj.c rename to src/acb_theta/test/t-char_shuffle.c index 7ca84b57ba..094b1fa588 100644 --- a/src/acb_theta/test/t-transform_proj.c +++ b/src/acb_theta/test/t-char_shuffle.c @@ -10,9 +10,11 @@ */ #include "test_helpers.h" +#include "fmpz_mat.h" +#include "acb.h" #include "acb_theta.h" -TEST_FUNCTION_START(acb_theta_transform_proj, state) +TEST_FUNCTION_START(acb_theta_char_shuffle, state) { slong iter; @@ -43,8 +45,8 @@ TEST_FUNCTION_START(acb_theta_transform_proj, state) 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_theta_char_shuffle(aux, mat, test, sqr, prec); + acb_theta_char_shuffle(th, inv, aux, sqr, prec); acb_div(scal, &test[0], &th[0], prec); _acb_vec_scalar_mul(th, th, n2, scal, prec); diff --git a/src/acb_theta/test/t-transform_char.c b/src/acb_theta/test/t-char_table.c similarity index 59% rename from src/acb_theta/test/t-transform_char.c rename to src/acb_theta/test/t-char_table.c index 0145c736ec..0505eed411 100644 --- a/src/acb_theta/test/t-transform_char.c +++ b/src/acb_theta/test/t-char_table.c @@ -10,24 +10,29 @@ */ #include "test_helpers.h" +#include "fmpz.h" +#include "fmpz_mat.h" #include "acb_theta.h" -TEST_FUNCTION_START(acb_theta_transform_char, state) +TEST_FUNCTION_START(acb_theta_char_table, 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 g = 1 + n_randint(state, 4); + slong n2 = 1 << (2 * g); slong bits = 8; fmpz_mat_t mat; - slong e; - ulong ab = n_randint(state, 1 << (2 * g)); - ulong test; + ulong * chars; + slong * es; + ulong ab; slong j, k; fmpz_mat_init(mat, 2 * g, 2 * g); + chars = flint_malloc(n2 * sizeof(ulong)); + es = flint_malloc(n2 * sizeof(slong)); for (j = 0; j < g; j++) { @@ -39,17 +44,21 @@ TEST_FUNCTION_START(acb_theta_transform_char, state) } sp2gz_trig(mat, mat); - test = acb_theta_transform_char(&e, mat, ab); - - if ((test >> g) != (ab >> g)) + acb_theta_char_table(chars, es, mat, -1); + for (ab = 0; ab < n2; ab++) { - flint_printf("FAIL\n"); - flint_printf("ab = %wd, test = %wd, matrix:\n", ab, test); - fmpz_mat_print_pretty(mat); - flint_abort(); + if ((chars[ab] >> g) != (ab >> g)) + { + flint_printf("FAIL\n"); + flint_printf("ab = %wd, test = %wd, matrix:\n", ab, chars[ab]); + fmpz_mat_print_pretty(mat); + flint_abort(); + } } fmpz_mat_clear(mat); + flint_free(chars); + flint_free(es); } TEST_FUNCTION_END(state); diff --git a/src/acb_theta/test/t-ctx_exp_inv.c b/src/acb_theta/test/t-ctx_exp_inv.c new file mode 100644 index 0000000000..0eb515375e --- /dev/null +++ b/src/acb_theta/test/t-ctx_exp_inv.c @@ -0,0 +1,69 @@ +/* + Copyright (C) 2024 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 3 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_ctx_exp_inv, state) +{ + slong iter; + + /* Test: correct value, never indeterminate */ + for (iter = 0; iter < 1000 * flint_test_multiplier(); iter++) + { + acb_t z, exp, exp_inv, test; + slong prec = 2 + n_randint(state, 200); + slong mag_bits = n_randint(state, 4); + int is_real = iter % 2; + + acb_init(z); + acb_init(exp); + acb_init(exp_inv); + acb_init(test); + + acb_randtest(z, state, prec, mag_bits); + if (is_real) + { + arb_zero(acb_imagref(z)); + } + acb_exp_pi_i(exp, z, prec); + acb_inv(exp_inv, exp, prec); + acb_theta_ctx_exp_inv(test, exp, z, is_real, prec); + + if (!acb_overlaps(test, exp_inv) + || !acb_is_finite(test)) + { + flint_printf("FAIL\n"); + acb_printd(test, 5); + flint_printf("\n"); + acb_printd(exp_inv, 5); + flint_printf("\n"); + flint_abort(); + } + + acb_mul(test, test, exp, prec); + acb_sub_si(test, test, 1, prec); + + if (!acb_contains_zero(test)) + { + flint_printf("FAIL\n"); + flint_abort(); + } + + acb_clear(z); + acb_clear(exp); + acb_clear(exp_inv); + acb_clear(test); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-ctx_sqr_inv.c b/src/acb_theta/test/t-ctx_sqr_inv.c new file mode 100644 index 0000000000..848153f944 --- /dev/null +++ b/src/acb_theta/test/t-ctx_sqr_inv.c @@ -0,0 +1,79 @@ +/* + Copyright (C) 2024 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 3 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_ctx_sqr_inv, state) +{ + slong iter; + + /* Test: correct value, never indeterminate */ + for (iter = 0; iter < 1000 * flint_test_multiplier(); iter++) + { + acb_t z, exp, sqr, inv, sqr_inv, test; + slong prec = 2 + n_randint(state, 200); + slong mag_bits = n_randint(state, 4); + int is_real = iter % 2; + + acb_init(z); + acb_init(exp); + acb_init(sqr); + acb_init(inv); + acb_init(sqr_inv); + acb_init(test); + + acb_randtest(z, state, prec, mag_bits); + if (is_real) + { + arb_zero(acb_imagref(z)); + } + acb_exp_pi_i(exp, z, prec); + acb_sqr(sqr, exp, prec); + + acb_neg(z, z); + acb_exp_pi_i(inv, z, prec); + acb_mul_2exp_si(z, z, 1); + acb_exp_pi_i(sqr_inv, z, prec); + + acb_theta_ctx_sqr_inv(test, inv, sqr, is_real, prec); + + if (!acb_overlaps(test, sqr_inv) + || !acb_is_finite(test)) + { + flint_printf("FAIL\n"); + acb_printd(test, 5); + flint_printf("\n"); + acb_printd(sqr_inv, 5); + flint_printf("\n"); + flint_abort(); + } + + acb_mul(test, test, sqr, prec); + acb_sub_si(test, test, 1, prec); + + if (!acb_contains_zero(test)) + { + flint_printf("FAIL\n"); + flint_abort(); + } + + acb_clear(z); + acb_clear(exp); + acb_clear(sqr); + acb_clear(inv); + acb_clear(sqr_inv); + acb_clear(test); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-ctx_tau_dupl.c b/src/acb_theta/test/t-ctx_tau_dupl.c new file mode 100644 index 0000000000..2db0e2ccca --- /dev/null +++ b/src/acb_theta/test/t-ctx_tau_dupl.c @@ -0,0 +1,53 @@ +/* + Copyright (C) 2024 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 3 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_ctx_tau_dupl, state) +{ + slong iter; + + /* Test: matches with acb_theta_ctx_tau_set with doubled input */ + for (iter = 0; iter < 50 * flint_test_multiplier(); iter++) + { + slong g = 1 + n_randint(state, 3); + slong prec = 100 + n_randint(state, 100); + slong mag_bits = n_randint(state, 5); + int allow_shift = iter % 2; + acb_mat_t tau; + acb_theta_ctx_tau_t ctx1, ctx2; + + acb_mat_init(tau, g, g); + acb_theta_ctx_tau_init(ctx1, allow_shift, g); + acb_theta_ctx_tau_init(ctx2, allow_shift, g); + + acb_siegel_randtest_reduced(tau, state, prec, mag_bits); + acb_theta_ctx_tau_set(ctx1, tau, prec); + acb_theta_ctx_tau_dupl(ctx1, prec); + + acb_mat_scalar_mul_2exp_si(tau, tau, 1); + acb_theta_ctx_tau_set(ctx2, tau, prec); + + if (!acb_theta_ctx_tau_overlaps(ctx1, ctx2)) + { + flint_printf("FAIL\n"); + flint_abort(); + } + + acb_mat_clear(tau); + acb_theta_ctx_tau_clear(ctx1); + acb_theta_ctx_tau_clear(ctx2); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-ctx_z_add_real.c b/src/acb_theta/test/t-ctx_z_add_real.c new file mode 100644 index 0000000000..4d2df0a141 --- /dev/null +++ b/src/acb_theta/test/t-ctx_z_add_real.c @@ -0,0 +1,72 @@ +/* + Copyright (C) 2024 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 3 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "acb.h" +#include "acb_mat.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_ctx_z_add_real, state) +{ + slong iter; + + /* Test: corresponds to acb_theta_ctx_set_z for z + t */ + for (iter = 0; iter < 50 * flint_test_multiplier(); iter++) + { + slong g = 1 + n_randint(state, 3); + slong prec = 100 + n_randint(state, 100); + slong mag_bits = n_randint(state, 5); + acb_mat_t tau; + acb_ptr z, t, w; + acb_theta_ctx_tau_t ctx_tau; + acb_theta_ctx_z_t ctx1, ctx2; + slong k; + + acb_mat_init(tau, g, g); + z = _acb_vec_init(g); + t = _acb_vec_init(g); + w = _acb_vec_init(g); + acb_theta_ctx_tau_init(ctx_tau, 0, g); + acb_theta_ctx_z_init(ctx1, g); + acb_theta_ctx_z_init(ctx2, g); + + acb_siegel_randtest_reduced(tau, state, prec, mag_bits); + acb_siegel_randtest_vec(z, state, g, prec); + for (k = 0; k < g; k++) + { + arb_urandom(acb_realref(&t[k]), state, prec); + } + + acb_theta_ctx_tau_set(ctx_tau, tau, prec); + acb_theta_ctx_z_set(ctx1, z, ctx_tau, prec); + acb_theta_ctx_z_set(ctx2, t, ctx_tau, prec); + acb_theta_ctx_z_add_real(ctx2, ctx1, ctx2, prec); + + _acb_vec_add(w, z, t, g, prec); + acb_theta_ctx_z_set(ctx1, w, ctx_tau, prec); + + if (!acb_theta_ctx_z_overlaps(ctx1, ctx2)) + { + flint_printf("FAIL\n"); + flint_abort(); + } + + acb_mat_clear(tau); + _acb_vec_clear(z, g); + _acb_vec_clear(t, g); + _acb_vec_clear(w, g); + acb_theta_ctx_tau_clear(ctx_tau); + acb_theta_ctx_z_clear(ctx1); + acb_theta_ctx_z_clear(ctx2); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-ctx_z_dupl.c b/src/acb_theta/test/t-ctx_z_dupl.c new file mode 100644 index 0000000000..322522efd0 --- /dev/null +++ b/src/acb_theta/test/t-ctx_z_dupl.c @@ -0,0 +1,63 @@ +/* + Copyright (C) 2024 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 3 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "acb.h" +#include "acb_mat.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_ctx_z_dupl, state) +{ + slong iter; + + /* Test: matches with acb_theta_ctx_z_set with doubled input */ + for (iter = 0; iter < 50 * flint_test_multiplier(); iter++) + { + slong g = 1 + n_randint(state, 3); + slong prec = 100 + n_randint(state, 100); + slong mag_bits = n_randint(state, 5); + acb_mat_t tau; + acb_ptr z; + acb_theta_ctx_tau_t ctx_tau; + acb_theta_ctx_z_t ctx1, ctx2; + + acb_mat_init(tau, g, g); + z = _acb_vec_init(g); + acb_theta_ctx_tau_init(ctx_tau, 0, g); + acb_theta_ctx_z_init(ctx1, g); + acb_theta_ctx_z_init(ctx2, g); + + acb_siegel_randtest_reduced(tau, state, prec, mag_bits); + acb_siegel_randtest_vec(z, state, g, prec); + + acb_theta_ctx_tau_set(ctx_tau, tau, prec); + acb_theta_ctx_z_set(ctx1, z, ctx_tau, prec); + acb_theta_ctx_z_dupl(ctx1, prec); + + acb_theta_ctx_tau_dupl(ctx_tau, prec); + _acb_vec_scalar_mul_2exp_si(z, z, g, 1); + acb_theta_ctx_z_set(ctx2, z, ctx_tau, prec); + + if (!acb_theta_ctx_z_overlaps(ctx1, ctx2)) + { + flint_printf("FAIL\n"); + flint_abort(); + } + + acb_mat_clear(tau); + _acb_vec_clear(z, g); + acb_theta_ctx_tau_clear(ctx_tau); + acb_theta_ctx_z_clear(ctx1); + acb_theta_ctx_z_clear(ctx2); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-dist_lat.c b/src/acb_theta/test/t-dist_lat.c deleted file mode 100644 index e5c3eeb3d1..0000000000 --- a/src/acb_theta/test/t-dist_lat.c +++ /dev/null @@ -1,121 +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 3 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 deleted file mode 100644 index 0f6a8102ca..0000000000 --- a/src/acb_theta/test/t-dist_pt.c +++ /dev/null @@ -1,85 +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 3 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 index 2261b797dc..ee5509b111 100644 --- a/src/acb_theta/test/t-eld_border.c +++ b/src/acb_theta/test/t-eld_border.c @@ -10,6 +10,7 @@ */ #include "test_helpers.h" +#include "arb.h" #include "arb_mat.h" #include "acb_theta.h" @@ -17,8 +18,7 @@ TEST_FUNCTION_START(acb_theta_eld_border, state) { slong iter; - /* Test: border points are not contained in the ellipsoid, - nor any children */ + /* Test: border points are not contained in the ellipsoid */ for (iter = 0; iter < 500 * flint_test_multiplier(); iter++) { slong g = 1 + n_randint(state, 4); @@ -29,8 +29,8 @@ TEST_FUNCTION_START(acb_theta_eld_border, state) arb_t x; arf_t R2; arb_ptr v; - slong k, j; - slong *all_pts; + slong k; + slong * all_pts; int r; acb_theta_eld_init(E, g, g); @@ -61,30 +61,10 @@ TEST_FUNCTION_START(acb_theta_eld_border, state) 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)) { - 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(); - } + flint_printf("FAIL: point inside ellipsoid\n"); + flint_abort(); } } diff --git a/src/acb_theta/test/t-dist_a0.c b/src/acb_theta/test/t-eld_distances.c similarity index 88% rename from src/acb_theta/test/t-dist_a0.c rename to src/acb_theta/test/t-eld_distances.c index 4617563700..42135aeaee 100644 --- a/src/acb_theta/test/t-dist_a0.c +++ b/src/acb_theta/test/t-eld_distances.c @@ -10,10 +10,12 @@ */ #include "test_helpers.h" +#include "arb.h" +#include "acb.h" #include "acb_mat.h" #include "acb_theta.h" -TEST_FUNCTION_START(acb_theta_dist_a0, state) +TEST_FUNCTION_START(acb_theta_eld_distances, state) { slong iter; @@ -46,9 +48,10 @@ TEST_FUNCTION_START(acb_theta_dist_a0, state) acb_add_arb(&z[k], &z[k], c, prec); } - acb_theta_dist_a0(d, z, tau, prec); + acb_theta_eld_distances(d, z, 1, tau, prec); - if (!arb_contains_zero(&d[a])) + if (!arb_contains_zero(&d[a]) + || !_arb_vec_is_finite(d, n)) { flint_printf("FAIL\n"); flint_printf("g = %wd, a = %wd, tau:\n", g, a); diff --git a/src/acb_theta/test/t-eld_points.c b/src/acb_theta/test/t-eld_points.c index 95b214d7a1..743b5dedbb 100644 --- a/src/acb_theta/test/t-eld_points.c +++ b/src/acb_theta/test/t-eld_points.c @@ -10,6 +10,7 @@ */ #include "test_helpers.h" +#include "arb.h" #include "arb_mat.h" #include "acb_theta.h" @@ -114,6 +115,20 @@ TEST_FUNCTION_START(acb_theta_eld_points, state) } flint_abort(); } + res = 0; + for (j = 0; j < E->nr; j++) + { + res += acb_theta_eld_contains(&(E->rchildren)[j], pt); + } + for (j = 0; j < E->nl; j++) + { + res += acb_theta_eld_contains(&(E->lchildren)[j], pt); + } + if (g >= 2 && res != 1) + { + flint_printf("FAIL: not contained in exactly 1 child\n"); + flint_abort(); + } } if (!acb_theta_eld_contains(E, pt)) diff --git a/src/acb_theta/test/t-g2_character.c b/src/acb_theta/test/t-g2_character.c index 4df1e355be..e2d6e2cbdb 100644 --- a/src/acb_theta/test/t-g2_character.c +++ b/src/acb_theta/test/t-g2_character.c @@ -10,6 +10,7 @@ */ #include "test_helpers.h" +#include "fmpz_mat.h" #include "acb_theta.h" TEST_FUNCTION_START(acb_theta_g2_character, state) @@ -21,20 +22,24 @@ TEST_FUNCTION_START(acb_theta_g2_character, state) { fmpz_mat_t mat; slong bits = n_randint(state, 10); - slong ab, eps, u, test; + ulong * chars; + slong * es; + slong ab, eps, test; fmpz_mat_init(mat, 4, 4); sp2gz_randtest(mat, state, bits); + chars = flint_malloc(16 * sizeof(ulong)); + es = flint_malloc(16 * sizeof(slong)); eps = acb_theta_g2_character(mat); - test = 10 * acb_theta_transform_kappa2(mat); /* 10 theta constants */ + test = 10 * acb_siegel_kappa2(mat); /* 10 theta constants */ + acb_theta_char_table(chars, es, mat, -1); for (ab = 0; ab < 16; ab++) { if (acb_theta_char_is_even(ab, 2)) { - acb_theta_transform_char(&u, mat, ab); - test += u; + test += es[ab]; } } if (test % 4 != 0) @@ -55,6 +60,8 @@ TEST_FUNCTION_START(acb_theta_g2_character, state) } fmpz_mat_clear(mat); + flint_free(chars); + flint_free(es); } TEST_FUNCTION_END(state); diff --git a/src/acb_theta/test/t-g2_chi10.c b/src/acb_theta/test/t-g2_chi10.c deleted file mode 100644 index 35597de95e..0000000000 --- a/src/acb_theta/test/t-g2_chi10.c +++ /dev/null @@ -1,69 +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 3 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_chi35.c b/src/acb_theta/test/t-g2_chi35.c index 6e9c857a86..dbfcbcbc82 100644 --- a/src/acb_theta/test/t-g2_chi35.c +++ b/src/acb_theta/test/t-g2_chi35.c @@ -10,6 +10,8 @@ */ #include "test_helpers.h" +#include "fmpz_mat.h" +#include "acb.h" #include "acb_mat.h" #include "acb_theta.h" @@ -41,11 +43,13 @@ TEST_FUNCTION_START(acb_theta_g2_chi35, state) 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_char_shuffle(th, mat, th, 0, prec); acb_theta_g2_chi35(s, th, prec); - acb_mul_i_pow_si(s, s, -acb_theta_transform_kappa2(mat)); + acb_mul_i_pow_si(s, s, -acb_siegel_kappa2(mat)); - if (!acb_overlaps(r, s)) + if (!acb_overlaps(r, s) + || !acb_is_finite(r) + || !acb_is_finite(s)) { flint_printf("FAIL\n"); acb_printd(r, 10); diff --git a/src/acb_theta/test/t-g2_chi3_6.c b/src/acb_theta/test/t-g2_chi3_6.c index f535d681ac..4d673f638a 100644 --- a/src/acb_theta/test/t-g2_chi3_6.c +++ b/src/acb_theta/test/t-g2_chi3_6.c @@ -10,6 +10,7 @@ */ #include "test_helpers.h" +#include "fmpz_mat.h" #include "acb_poly.h" #include "acb_mat.h" #include "acb_theta.h" @@ -25,7 +26,7 @@ acb_theta_g2_chi8_6(acb_poly_t res, const acb_mat_t tau, slong prec) z = _acb_vec_init(2); acb_init(c); - acb_theta_jet_all(dth, z, tau, 1, prec); + acb_theta_jet(dth, z, 1, tau, 1, 1, 0, prec); acb_theta_g2_chi3_6(res, dth, prec); for (k = 0; k < 16; k++) { @@ -52,6 +53,8 @@ TEST_FUNCTION_START(acb_theta_g2_chi3_6, state) fmpz_mat_t mat; acb_mat_t tau, w, c, cinv; acb_poly_t r, s; + slong k; + int res = 1; fmpz_mat_init(mat, 2 * g, 2 * g); acb_mat_init(tau, g, g); @@ -69,7 +72,16 @@ TEST_FUNCTION_START(acb_theta_g2_chi3_6, state) 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)) + for (k = 0; k <= acb_poly_degree(r); k++) + { + res = res && acb_is_finite(acb_poly_get_coeff_ptr(r, k)); + } + for (k = 0; k <= acb_poly_degree(r); k++) + { + res = res && acb_is_finite(acb_poly_get_coeff_ptr(s, k)); + } + + if (!acb_poly_overlaps(r, s) || !res) { flint_printf("FAIL\n"); acb_mat_printd(tau, 5); diff --git a/src/acb_theta/test/t-g2_chi5.c b/src/acb_theta/test/t-g2_chi5.c index eb34ea00a2..1aebaa7bd8 100644 --- a/src/acb_theta/test/t-g2_chi5.c +++ b/src/acb_theta/test/t-g2_chi5.c @@ -24,13 +24,13 @@ TEST_FUNCTION_START(acb_theta_g2_chi5, state) slong n2 = 1 << (2 * g); slong prec = 100 + n_randint(state, 500); slong mag_bits = n_randint(state, 10); - acb_ptr th; + acb_ptr th, mf; slong k; - acb_t r, s; + acb_t r; th = _acb_vec_init(n2); + mf = _acb_vec_init(4); acb_init(r); - acb_init(s); for (k = 0; k < n2; k++) { @@ -40,21 +40,23 @@ TEST_FUNCTION_START(acb_theta_g2_chi5, state) 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); + acb_theta_g2_even_weight(&mf[0], &mf[1], &mf[2], &mf[3], th, prec); - if (!acb_overlaps(r, s)) + if (!acb_overlaps(r, &mf[2]) + || !_acb_vec_is_finite(mf, 4) + || !acb_is_finite(r)) { flint_printf("FAIL\n"); acb_printd(r, 10); flint_printf("\n"); - acb_printd(s, 10); + acb_printd(&mf[2], 10); flint_printf("\n"); flint_abort(); } _acb_vec_clear(th, n2); + _acb_vec_clear(mf, 4); 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 index 892a015537..a1866191a0 100644 --- a/src/acb_theta/test/t-g2_covariants.c +++ b/src/acb_theta/test/t-g2_covariants.c @@ -11,6 +11,7 @@ #include "test_helpers.h" #include "fmpz_poly.h" +#include "fmpz_mat.h" #include "acb_poly.h" #include "acb_mat.h" #include "acb_theta.h" @@ -36,12 +37,12 @@ TEST_FUNCTION_START(acb_theta_g2_covariants, state) slong klist[26] = ACB_THETA_G2_COV_K; fmpz_mat_t mat; acb_mat_t tau, w, c; - acb_ptr z, th2; + acb_ptr z, th2, mf; acb_poly_struct * cov1; acb_poly_struct * cov2; acb_poly_t u, v; fmpz_poly_t pol; - acb_t psi4, test; + acb_t test, chi5; slong k; fmpz_mat_init(mat, 2 * g, 2 * g); @@ -50,6 +51,7 @@ TEST_FUNCTION_START(acb_theta_g2_covariants, state) acb_mat_init(c, g, g); z = _acb_vec_init(g); th2 = _acb_vec_init(n); + mf = _acb_vec_init(4); cov1 = flint_malloc(26 * sizeof(acb_poly_struct)); cov2 = flint_malloc(26 * sizeof(acb_poly_struct)); for (k = 0; k < 26; k++) @@ -60,21 +62,21 @@ TEST_FUNCTION_START(acb_theta_g2_covariants, state) acb_poly_init(u); acb_poly_init(v); fmpz_poly_init(pol); - acb_init(psi4); acb_init(test); + acb_init(chi5); 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_theta_g2_even_weight(&mf[0], &mf[1], &mf[2], &mf[3], th2, prec); + acb_theta_g2_sextic_chi5(u, chi5, tau, prec); + acb_theta_g2_covariants(cov1, u, 0, 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); + acb_theta_g2_sextic_chi5(u, test, w, prec); + acb_theta_g2_covariants(cov2, u, 0, prec); /* Test psi4 */ acb_poly_set_si(u, -3); @@ -84,12 +86,15 @@ TEST_FUNCTION_START(acb_theta_g2_covariants, state) acb_poly_get_coeff_acb(test, u, 0); acb_div_si(test, test, -20, prec); - if (!acb_overlaps(psi4, test)) + if (!acb_overlaps(&mf[0], test) + || !_acb_vec_is_finite(mf, 4) + || !acb_is_finite(chi5) + || (!acb_is_finite(test) && !acb_contains_zero(chi5))) { flint_printf("FAIL (psi4)\n"); acb_mat_printd(tau, 5); flint_printf("psi4, test:\n"); - acb_printd(psi4, 10); + acb_printd(&mf[0], 10); flint_printf("\n"); acb_printd(test, 10); flint_printf("\nu:\n"); @@ -127,7 +132,7 @@ TEST_FUNCTION_START(acb_theta_g2_covariants, state) { acb_poly_set_coeff_si(u, k, n_randint(state, 10)); } - acb_theta_g2_covariants(cov1, u, prec); + acb_theta_g2_covariants(cov1, u, 0, prec); for (k = 0; k < 26; k++) { if (!acb_poly_get_unique_fmpz_poly(pol, &cov1[k])) @@ -139,12 +144,39 @@ TEST_FUNCTION_START(acb_theta_g2_covariants, state) } } + /* Test leading coefficients */ + acb_theta_g2_covariants(cov2, u, 1, prec); + for (k = 0; k < 26; k++) + { + if (acb_poly_degree(&cov2[k]) > 0) + { + flint_printf("FAIL (degree, k = %wd)\n", k); + acb_poly_printd(&cov2[k], 5); + flint_printf("\n"); + flint_abort(); + } + acb_poly_get_coeff_acb(&mf[0], &cov2[k], 0); + acb_poly_get_coeff_acb(test, &cov1[k], jlist[k]); + if (!acb_overlaps(&mf[0], test) + || !acb_is_finite(&mf[0]) + || !acb_is_finite(test)) + { + flint_printf("FAIL (leading coefficient, k = %wd)\n", k); + acb_poly_printd(&cov1[k], 5); + flint_printf("\n"); + acb_poly_printd(&cov2[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); + _acb_vec_clear(mf, 4); for (k = 0; k < 26; k++) { acb_poly_clear(&cov1[k]); @@ -155,8 +187,8 @@ TEST_FUNCTION_START(acb_theta_g2_covariants, state) acb_poly_clear(u); acb_poly_clear(v); fmpz_poly_clear(pol); - acb_clear(psi4); acb_clear(test); + acb_clear(chi5); } 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 deleted file mode 100644 index d5b9bda82a..0000000000 --- a/src/acb_theta/test/t-g2_covariants_lead.c +++ /dev/null @@ -1,77 +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 3 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 index ab83f9386b..5feefaef74 100644 --- a/src/acb_theta/test/t-g2_detk_symj.c +++ b/src/acb_theta/test/t-g2_detk_symj.c @@ -28,6 +28,8 @@ TEST_FUNCTION_START(acb_theta_g2_detk_symj, state) slong j = n_randint(state, 10); slong bits = 2; slong prec = 100 + n_randint(state, 200); + slong i; + int res = 1; acb_mat_init(c1, g, g); acb_mat_init(c2, g, g); @@ -45,7 +47,16 @@ TEST_FUNCTION_START(acb_theta_g2_detk_symj, state) 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)) + for (i = 0; i <= acb_poly_degree(t); i++) + { + res = res && acb_is_finite(acb_poly_get_coeff_ptr(t, i)); + } + for (i = 0; i <= acb_poly_degree(r); i++) + { + res = res && acb_is_finite(acb_poly_get_coeff_ptr(r, i)); + } + + if (!acb_poly_overlaps(t, r) || !res) { flint_printf("FAIL\n"); acb_mat_printd(c1, 5); diff --git a/src/acb_theta/test/t-g2_chi12.c b/src/acb_theta/test/t-g2_even_weight.c similarity index 58% rename from src/acb_theta/test/t-g2_chi12.c rename to src/acb_theta/test/t-g2_even_weight.c index 485b896584..af2926bfe6 100644 --- a/src/acb_theta/test/t-g2_chi12.c +++ b/src/acb_theta/test/t-g2_even_weight.c @@ -10,10 +10,11 @@ */ #include "test_helpers.h" +#include "fmpz_mat.h" #include "acb.h" #include "acb_theta.h" -TEST_FUNCTION_START(acb_theta_g2_chi12, state) +TEST_FUNCTION_START(acb_theta_g2_even_weight, state) { slong iter; @@ -25,14 +26,13 @@ TEST_FUNCTION_START(acb_theta_g2_chi12, state) 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; + acb_ptr th2, mf, test; slong k; fmpz_mat_init(mat, 2 * g, 2 * g); th2 = _acb_vec_init(n2); - acb_init(r); - acb_init(s); + mf = _acb_vec_init(4); + test = _acb_vec_init(4); sp2gz_randtest(mat, state, mag_bits); for (k = 0; k < n2; k++) @@ -40,25 +40,31 @@ TEST_FUNCTION_START(acb_theta_g2_chi12, state) 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); + acb_theta_g2_even_weight(&mf[0], &mf[1], &mf[2], &mf[3], th2, prec); + acb_theta_char_shuffle(th2, mat, th2, 1, prec); + acb_theta_g2_even_weight(&test[0], &test[1], &test[2], &test[3], th2, prec); + if (acb_siegel_kappa2(mat) % 2 == 1) + { + /* Negate weights 2 mod 4 */ + acb_neg(&test[1], &test[1]); + acb_neg(&test[3], &test[3]); + } - if (!acb_overlaps(r, s)) + if (!_acb_vec_overlaps(test, mf, 4) + || !_acb_vec_is_finite(test, 4) + || !_acb_vec_is_finite(mf, 4)) { flint_printf("FAIL\n"); fmpz_mat_print_pretty(mat); - acb_printd(r, 10); - flint_printf("\n"); - acb_printd(s, 10); - flint_printf("\n"); + _acb_vec_printd(test, 4, 5); + _acb_vec_printd(mf, 4, 5); flint_abort(); } fmpz_mat_clear(mat); _acb_vec_clear(th2, n2); - acb_clear(r); - acb_clear(s); + _acb_vec_clear(mf, 4); + _acb_vec_clear(test, 4); } 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 deleted file mode 100644 index 57fad34aa3..0000000000 --- a/src/acb_theta/test/t-g2_jet_naive_1.c +++ /dev/null @@ -1,76 +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 3 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 deleted file mode 100644 index 1ae9251134..0000000000 --- a/src/acb_theta/test/t-g2_psi4.c +++ /dev/null @@ -1,65 +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 3 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 deleted file mode 100644 index ce6e5c3e32..0000000000 --- a/src/acb_theta/test/t-g2_psi6.c +++ /dev/null @@ -1,69 +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 3 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 deleted file mode 100644 index ca88d0aae2..0000000000 --- a/src/acb_theta/test/t-g2_sextic.c +++ /dev/null @@ -1,96 +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 3 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 index 68a1a32c07..cadb282c3a 100644 --- a/src/acb_theta/test/t-g2_sextic_chi5.c +++ b/src/acb_theta/test/t-g2_sextic_chi5.c @@ -18,62 +18,100 @@ 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++) + /* Test: discriminant of sextic is chi10 = chi5^2 */ + 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 prec = 100 + n_randint(state, 1000); slong bits = n_randint(state, 4); acb_mat_t tau; - acb_ptr z, th; - acb_poly_t s1, s2; - acb_t c1, c2; + acb_ptr z, th2, roots, mf; + acb_poly_t f; + acb_t chi5, d, t; + slong nb, k, j; + int res = 1; 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); + th2 = _acb_vec_init(n); + roots = _acb_vec_init(6); + mf = _acb_vec_init(4); + acb_poly_init(f); + acb_init(chi5); + 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_chi5(s1, c1, tau, prec); - acb_theta_g2_sextic(s2, tau, prec); + acb_theta_g2_sextic_chi5(f, chi5, tau, prec); - if (!acb_poly_overlaps(s1, s2)) + for (k = 0; k <= 6; k++) { - flint_printf("FAIL (sextic)\n"); - acb_poly_printd(s1, 5); - flint_printf("\n"); - acb_poly_printd(s2, 5); + acb_poly_get_coeff_acb(t, f, k); + res = res && acb_is_finite(t); + } + + if ((!res && !acb_contains_zero(chi5)) + || !acb_is_finite(chi5)) + { + flint_printf("FAIL (finite)\n"); + acb_poly_printd(f, 5); flint_printf("\n"); flint_abort(); } - acb_theta_all(th, z, tau, 0, prec); - acb_theta_g2_chi5(c2, th, prec); + nb = acb_poly_find_roots(roots, f, NULL, 0, prec); - if (!acb_overlaps(c1, c2)) + if (nb == 6) { - flint_printf("FAIL (chi5)\n"); - acb_printd(c1, 10); - flint_printf("\n"); - acb_printd(c2, 10); - flint_printf("\n"); - flint_abort(); + 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_even_weight(&mf[0], &mf[1], &mf[2], &mf[3], th2, prec); + acb_sqr(chi5, chi5, prec); + + if (!acb_overlaps(d, &mf[2]) + || !acb_overlaps(chi5, &mf[2]) + || !_acb_vec_is_finite(mf, 4) + || !acb_is_finite(d)) + { + flint_printf("FAIL\n"); + flint_printf("roots, discr, chi10, chi5^2:\n"); + _acb_vec_printd(roots, 6, 5); + acb_printd(d, 5); + flint_printf("\n"); + acb_printd(&mf[2], 5); + flint_printf("\n"); + acb_printd(chi5, 5); + 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); + _acb_vec_clear(th2, n); + _acb_vec_clear(roots, 6); + _acb_vec_clear(mf, 4); + acb_poly_clear(f); + acb_clear(chi5); + acb_clear(d); + acb_clear(t); } TEST_FUNCTION_END(state); diff --git a/src/acb_theta/test/t-g2_transvectant.c b/src/acb_theta/test/t-g2_transvectant.c index 091e143e6c..83e92b65e4 100644 --- a/src/acb_theta/test/t-g2_transvectant.c +++ b/src/acb_theta/test/t-g2_transvectant.c @@ -22,6 +22,7 @@ TEST_FUNCTION_START(acb_theta_g2_transvectant, state) { slong prec = 200; slong bits = 2; + int lead = iter % 2; acb_poly_t f, g; acb_t c, test; @@ -33,7 +34,7 @@ TEST_FUNCTION_START(acb_theta_g2_transvectant, state) 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); + acb_theta_g2_transvectant(g, f, f, 6, 6, 6, lead, prec); if (acb_poly_degree(g) > 0) { @@ -57,7 +58,9 @@ TEST_FUNCTION_START(acb_theta_g2_transvectant, state) acb_poly_get_coeff_acb(c, g, 0); acb_mul_si(c, c, 60, prec); - if (!acb_overlaps(test, c)) + if (!acb_overlaps(test, c) + || !acb_is_finite(test) + || !acb_is_finite(c)) { flint_printf("FAIL (value)\n"); acb_printd(test, 5); diff --git a/src/acb_theta/test/t-g2_transvectant_lead.c b/src/acb_theta/test/t-g2_transvectant_lead.c deleted file mode 100644 index c4513866e5..0000000000 --- a/src/acb_theta/test/t-g2_transvectant_lead.c +++ /dev/null @@ -1,68 +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 3 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.c b/src/acb_theta/test/t-jet.c new file mode 100644 index 0000000000..f9285a22d7 --- /dev/null +++ b/src/acb_theta/test/t-jet.c @@ -0,0 +1,76 @@ +/* + Copyright (C) 2024 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 3 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "acb.h" +#include "acb_mat.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_jet, state) +{ + slong iter; + + /* Test: agrees with jet_notransform */ + for (iter = 0; iter < 25 * flint_test_multiplier(); iter++) + { + slong g = 1 + n_randint(state, 2); + slong n = 1 << g; + slong nb = n_randint(state, 3); + slong ord = n_randint(state, 3); + slong mprec = 100 + n_randint(state, 400); + slong prec = mprec + 50; + slong bits = n_randint(state, 4); + slong nbjet = acb_theta_jet_nb(ord, g); + int all = iter % 2; + slong nbth = (all ? n * n : 1); + int sqr = n_randint(state, 2); + acb_mat_t tau; + acb_ptr z; + acb_ptr th, test; + + acb_mat_init(tau, g, g); + z = _acb_vec_init(nb * g); + th = _acb_vec_init(nb * nbth * nbjet); + test = _acb_vec_init(nb * nbth * nbjet); + + /* 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_reduced(z, state, nb, tau, 0, prec); + _acb_vec_scalar_mul_2exp_si(z, z, nb * g, 1); + + /* Call jet at precision mprec, test against jet_notransform */ + acb_theta_jet(th, z, nb, tau, ord, all, sqr, mprec); + acb_theta_jet_notransform(test, z, nb, tau, ord, 0, all, sqr, prec); + + if (!_acb_vec_overlaps(th, test, nb * nbth * nbjet) + || !_acb_vec_is_finite(th, nb * nbth * nbjet) + || !_acb_vec_is_finite(test, nb * nbth * nbjet)) + { + flint_printf("FAIL\n"); + flint_printf("g = %wd, prec = %wd, nb = %wd, ord = %wd, all = %wd, sqr = %wd, tau, z:\n", + g, prec, nb, ord, all, sqr); + acb_mat_printd(tau, 5); + _acb_vec_printd(z, nb * g, 5); + flint_printf("th, test:\n"); + _acb_vec_printd(th, nb * nbth * nbjet, 5); + _acb_vec_printd(test, nb * nbth * nbjet, 5); + flint_abort(); + } + + acb_mat_clear(tau); + _acb_vec_clear(z, nb * g); + _acb_vec_clear(th, nb * nbth * nbjet); + _acb_vec_clear(test, nb * nbth * nbjet); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-jet_all.c b/src/acb_theta/test/t-jet_all.c deleted file mode 100644 index bf6230a537..0000000000 --- a/src/acb_theta/test/t-jet_all.c +++ /dev/null @@ -1,75 +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 3 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 index 59c516c803..169c0aa2b1 100644 --- a/src/acb_theta/test/t-jet_compose.c +++ b/src/acb_theta/test/t-jet_compose.c @@ -10,6 +10,7 @@ */ #include "test_helpers.h" +#include "acb.h" #include "acb_mat.h" #include "acb_theta.h" @@ -49,7 +50,9 @@ TEST_FUNCTION_START(acb_theta_jet_compose, state) 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)) + if (!_acb_vec_overlaps(test, v1, nb) + || !_acb_vec_is_finite(test, nb) + || !_acb_vec_is_finite(v1, nb)) { flint_printf("FAIL (g = %wd, ord = %wd)\n", g, ord); _acb_vec_printd(v3, nb, 5); diff --git a/src/acb_theta/test/t-jet_naive_00.c b/src/acb_theta/test/t-jet_naive_00.c deleted file mode 100644 index aa15250297..0000000000 --- a/src/acb_theta/test/t-jet_naive_00.c +++ /dev/null @@ -1,63 +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 3 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 deleted file mode 100644 index c6dc5c6bba..0000000000 --- a/src/acb_theta/test/t-jet_naive_all.c +++ /dev/null @@ -1,102 +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 3 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 deleted file mode 100644 index 81316133d5..0000000000 --- a/src/acb_theta/test/t-jet_naive_fixed_ab.c +++ /dev/null @@ -1,64 +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 3 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_notransform.c b/src/acb_theta/test/t-jet_notransform.c new file mode 100644 index 0000000000..e2db946274 --- /dev/null +++ b/src/acb_theta/test/t-jet_notransform.c @@ -0,0 +1,96 @@ +/* + Copyright (C) 2025 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 3 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "acb.h" +#include "acb_mat.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_jet_notransform, state) +{ + slong iter; + + /* Test: coincides with sum_jet */ + for (iter = 0; iter < 20 * flint_test_multiplier(); iter++) + { + slong g = 1 + n_randint(state, 3); + slong n = 1 << g; + int all = iter % 2; + ulong ab = n_randint(state, n); + slong nbth = (all ? n * n : 1); + slong mprec = 50 + n_randint(state, 100); + slong prec = mprec + 25; + slong bits = n_randint(state, 4); + slong nb = n_randint(state, 3); + slong ord = n_randint(state, 3); + slong nbjet = acb_theta_jet_nb(ord, g); + int sqr = n_randint(state, 2); + acb_mat_t tau; + acb_ptr zs, th, test; + acb_theta_ctx_tau_t ctx_tau; + acb_theta_ctx_z_struct * vec; + slong j; + + acb_mat_init(tau, g, g); + zs = _acb_vec_init(nb * g); + th = _acb_vec_init(nb * nbth * nbjet); + test = _acb_vec_init(nb * n * n * nbjet); + acb_theta_ctx_tau_init(ctx_tau, 0, g); + vec = acb_theta_ctx_z_vec_init(nb, g); + + acb_siegel_randtest_reduced(tau, state, prec, bits); + acb_siegel_randtest_vec_reduced(zs, state, nb, tau, 0, prec); + + acb_theta_ctx_tau_set(ctx_tau, tau, prec); + for (j = 0; j < nb; j++) + { + acb_theta_ctx_z_set(&vec[j], zs + j * g, ctx_tau, prec); + } + + acb_theta_jet_notransform(th, zs, nb, tau, ord, ab, all, sqr, mprec); + acb_theta_sum_jet(test, vec, nb, ctx_tau, ord, 1, 1, prec); + if (!all) + { + for (j = 0; j < nb; j++) + { + _acb_vec_set(test + j * nbjet, test + j * n * n * nbjet + ab * nbjet, nbjet); + } + } + if (ord == 0 && sqr) + { + _acb_vec_sqr(test, test, nb * nbth, prec); + } + + if (!_acb_vec_overlaps(th, test, nb * nbth * nbjet) + || (_acb_vec_is_finite(test, nb * nbth * nbjet) + && !_acb_vec_is_finite(th, nb * nbth * nbjet))) + { + flint_printf("FAIL\n"); + flint_printf("g = %wd, nb = %wd, ord = %wd, all = %wd, ab = %wd, sqr = %wd, mprec = %wd, prec = %wd\n", + g, nb, ord, all, ab, sqr, mprec, prec); + _acb_vec_printd(th, nb * nbth * nbjet, 5); + _acb_vec_printd(test, nb * nbth * nbjet, 5); + flint_printf("difference:\n"); + _acb_vec_sub(th, th, test, nb * nbth * nbjet, prec); + _acb_vec_printd(th, nb * nbth * nbjet, 5); + flint_abort(); + } + + acb_mat_clear(tau); + _acb_vec_clear(zs, nb * g); + _acb_vec_clear(th, nb * nbth * nbjet); + _acb_vec_clear(test, nb * n * n * nbjet); + acb_theta_ctx_tau_clear(ctx_tau); + acb_theta_ctx_z_vec_clear(vec, nb); + } + + 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 deleted file mode 100644 index f5c16f2797..0000000000 --- a/src/acb_theta/test/t-jet_ql_all.c +++ /dev/null @@ -1,63 +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 3 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_finite_diff.c b/src/acb_theta/test/t-jet_ql_finite_diff.c deleted file mode 100644 index f9bf0a6e39..0000000000 --- a/src/acb_theta/test/t-jet_ql_finite_diff.c +++ /dev/null @@ -1,117 +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 3 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 deleted file mode 100644 index 725b6ec771..0000000000 --- a/src/acb_theta/test/t-jet_ql_radius.c +++ /dev/null @@ -1,90 +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 3 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-naive_00.c b/src/acb_theta/test/t-naive_00.c deleted file mode 100644 index 875379a7a9..0000000000 --- a/src/acb_theta/test/t-naive_00.c +++ /dev/null @@ -1,72 +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 3 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 deleted file mode 100644 index e91a45f764..0000000000 --- a/src/acb_theta/test/t-naive_all.c +++ /dev/null @@ -1,98 +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 3 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 deleted file mode 100644 index b344dcf530..0000000000 --- a/src/acb_theta/test/t-naive_fixed_a.c +++ /dev/null @@ -1,76 +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 3 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 deleted file mode 100644 index c117da8a65..0000000000 --- a/src/acb_theta/test/t-naive_fixed_ab.c +++ /dev/null @@ -1,77 +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 3 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_reduce.c b/src/acb_theta/test/t-naive_reduce.c deleted file mode 100644 index d2ac5ad7ff..0000000000 --- a/src/acb_theta/test/t-naive_reduce.c +++ /dev/null @@ -1,169 +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 3 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 deleted file mode 100644 index f7258a1fbd..0000000000 --- a/src/acb_theta/test/t-naive_term.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 3 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 deleted file mode 100644 index 5f087b075c..0000000000 --- a/src/acb_theta/test/t-ql_a0.c +++ /dev/null @@ -1,102 +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 3 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 deleted file mode 100644 index 4f03a3ae24..0000000000 --- a/src/acb_theta/test/t-ql_a0_split.c +++ /dev/null @@ -1,94 +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 3 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 deleted file mode 100644 index 9407ef1889..0000000000 --- a/src/acb_theta/test/t-ql_a0_steps.c +++ /dev/null @@ -1,92 +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 3 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 deleted file mode 100644 index 71da1130aa..0000000000 --- a/src/acb_theta/test/t-ql_all.c +++ /dev/null @@ -1,71 +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 3 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_exact.c b/src/acb_theta/test/t-ql_exact.c new file mode 100644 index 0000000000..c2b712ea7d --- /dev/null +++ b/src/acb_theta/test/t-ql_exact.c @@ -0,0 +1,107 @@ +/* + Copyright (C) 2024 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 3 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "acb.h" +#include "acb_mat.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_ql_exact, state) +{ + slong iter; + + /* Test: coincides with sum */ + for (iter = 0; iter < 50 * flint_test_multiplier(); iter++) + { + slong g = 1 + n_randint(state, 2); + slong n = 1 << g; + slong prec = 100 + n_randint(state, 500); + slong nb = 1 + n_randint(state, 4); + slong * pattern; + int all = n_randint(state, 2); + int shifted_prec = n_randint(state, 2); + slong nbth = (all ? n * n : n); + acb_mat_t tau; + acb_ptr zs, th, test; + acb_theta_ctx_tau_t ctx_tau; + acb_theta_ctx_z_struct * vec; + arb_ptr distances; + slong j; + + acb_mat_init(tau, g, g); + zs = _acb_vec_init(nb * g); + th = _acb_vec_init(nb * nbth); + test = _acb_vec_init(nb * nbth); + acb_theta_ctx_tau_init(ctx_tau, 1, g); + vec = acb_theta_ctx_z_vec_init(nb, g); + distances = _arb_vec_init(nb * n); + pattern = flint_malloc(g * sizeof(slong)); + + /* Sample reduced and exact tau, z; sample random pattern */ + acb_siegel_randtest_compact(tau, state, 1, prec); + acb_siegel_randtest_vec_reduced(zs + g, state, nb - 1, tau, 1, prec); + for (j = 0; j < g; j++) + { + pattern[j] = n_randint(state, 4); + } + + if (shifted_prec) + { + acb_theta_eld_distances(distances, zs, nb, tau, prec); + } + acb_theta_ctx_tau_set(ctx_tau, tau, prec); + for (j = 0; j < nb; j++) + { + acb_theta_ctx_z_set(&vec[j], zs + j * g, ctx_tau, prec); + acb_theta_sum(test + j * nbth, &vec[j], 1, ctx_tau, distances + j * n, 1, all, 1, prec); + } + if (!shifted_prec) + { + /* distances are set to zero */ + acb_theta_sum(test, vec, nb, ctx_tau, distances, 1, all, 1, prec); + } + + acb_theta_ql_exact(th, zs, nb, tau, pattern, all, shifted_prec, prec); + + if (!_acb_vec_overlaps(th, test, nb * nbth) + || !_acb_vec_is_finite(test, nb * nbth) + || !_acb_vec_is_finite(th, nb * nbth)) + { + flint_printf("FAIL\n"); + flint_printf("\n\ng = %wd, prec = %wd, nb = %wd, all = %wd, shifted_prec = %wd\n", + g, prec, nb, all, shifted_prec); + acb_mat_printd(tau, 5); + _acb_vec_printd(zs, nb * g, 5); + flint_printf("result of sum:\n"); + _acb_vec_printd(test, nbth * nb, 5); + flint_printf("pattern:\n"); + for (j = 0; j < g; j++) + { + flint_printf("%wd -> %wd\n", j, pattern[j]); + } + flint_printf("difference:\n"); + _acb_vec_sub(th, th, test, nb * nbth, prec); + _acb_vec_printd(th, nb * nbth, 5); + flint_abort(); + } + + acb_mat_clear(tau); + _acb_vec_clear(zs, nb * g); + _acb_vec_clear(th, nbth * nb); + _acb_vec_clear(test, nbth * nb); + acb_theta_ctx_tau_clear(ctx_tau); + acb_theta_ctx_z_vec_clear(vec, nb); + _arb_vec_clear(distances, nb * n); + flint_free(pattern); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-jet_error_bounds.c b/src/acb_theta/test/t-ql_jet_error.c similarity index 78% rename from src/acb_theta/test/t-jet_error_bounds.c rename to src/acb_theta/test/t-ql_jet_error.c index 2c598cabc4..2a28ef8bd2 100644 --- a/src/acb_theta/test/t-jet_error_bounds.c +++ b/src/acb_theta/test/t-ql_jet_error.c @@ -10,10 +10,11 @@ */ #include "test_helpers.h" +#include "acb.h" #include "acb_mat.h" #include "acb_theta.h" -TEST_FUNCTION_START(acb_theta_jet_error_bounds, state) +TEST_FUNCTION_START(acb_theta_ql_jet_error, state) { slong iter; @@ -31,6 +32,8 @@ TEST_FUNCTION_START(acb_theta_jet_error_bounds, state) slong hprec = mprec + n_randint(state, 50); acb_mat_t tau1, tau2, tau3; acb_ptr z1, z2, z3, dth; + acb_theta_ctx_tau_t ctx_tau; + acb_theta_ctx_z_t ctx_z; arb_ptr err; acb_ptr d1, d2, test; acb_t x; @@ -42,6 +45,8 @@ TEST_FUNCTION_START(acb_theta_jet_error_bounds, state) z1 = _acb_vec_init(g); z2 = _acb_vec_init(g); z3 = _acb_vec_init(g); + acb_theta_ctx_tau_init(ctx_tau, 0, g); + acb_theta_ctx_z_init(ctx_z, g); dth = _acb_vec_init(n * nb_der); err = _arb_vec_init(n * nb); d1 = _acb_vec_init(n * nb); @@ -50,7 +55,7 @@ TEST_FUNCTION_START(acb_theta_jet_error_bounds, state) acb_init(x); acb_siegel_randtest_reduced(tau1, state, hprec, bits); - acb_siegel_randtest_vec(z1, state, g, hprec); + acb_siegel_randtest_vec_reduced(z1, state, 1, tau1, 0, hprec); for (j = 0; j < g; j++) { @@ -85,12 +90,21 @@ TEST_FUNCTION_START(acb_theta_jet_error_bounds, state) 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); + acb_theta_ctx_tau_set(ctx_tau, tau1, hprec); + acb_theta_ctx_z_set(ctx_z, z1, ctx_tau, hprec); + acb_theta_sum_jet(d1, ctx_z, 1, ctx_tau, ord, 1, 1, hprec); + + acb_theta_ctx_tau_set(ctx_tau, tau2, hprec); + acb_theta_ctx_z_set(ctx_z, z2, ctx_tau, hprec); + acb_theta_sum_jet(d2, ctx_z, 1, ctx_tau, ord, 1, 1, hprec); + + acb_theta_ctx_tau_set(ctx_tau, tau3, lprec); + acb_theta_ctx_z_set(ctx_z, z3, ctx_tau, lprec); + acb_theta_sum_jet(dth, ctx_z, 1, ctx_tau, ord + 2, 1, 1, lprec); + for (k = 0; k < n; k++) { - acb_theta_jet_error_bounds(err + k * nb, z3, tau3, dth + k * nb_der, ord, lprec); + acb_theta_ql_jet_error(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); @@ -101,7 +115,9 @@ TEST_FUNCTION_START(acb_theta_jet_error_bounds, state) acb_add_error_arb(&test[k], &err[k]); } - if (!_acb_vec_overlaps(test, d2, n * nb)) + if (!_acb_vec_overlaps(test, d2, n * nb) + || !_acb_vec_is_finite(test, n * nb) + || !_acb_vec_is_finite(d2, n * nb)) { flint_printf("FAIL (bounds)\n"); flint_printf("values:\n"); @@ -121,6 +137,8 @@ TEST_FUNCTION_START(acb_theta_jet_error_bounds, state) _acb_vec_clear(z1, g); _acb_vec_clear(z2, g); _acb_vec_clear(z3, g); + acb_theta_ctx_tau_clear(ctx_tau); + acb_theta_ctx_z_clear(ctx_z); _acb_vec_clear(dth, n * nb_der); _arb_vec_clear(err, n * nb); _acb_vec_clear(d1, n * nb); diff --git a/src/acb_theta/test/t-ql_jet_fd.c b/src/acb_theta/test/t-ql_jet_fd.c new file mode 100644 index 0000000000..44bf55c102 --- /dev/null +++ b/src/acb_theta/test/t-ql_jet_fd.c @@ -0,0 +1,83 @@ +/* + Copyright (C) 2024 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 3 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "acb.h" +#include "acb_mat.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_ql_jet_fd, state) +{ + slong iter; + + /* Test: coincides with sum_jet */ + for (iter = 0; iter < 20 * flint_test_multiplier(); iter++) + { + slong g = 1 + n_randint(state, 3); + int all = iter % 2; + slong n = 1 << g; + slong nbth = (all ? n * n : n); + slong mprec = 100 + n_randint(state, 100); + slong prec = mprec + 50; + slong bits = n_randint(state, 4); + slong nb = n_randint(state, 3); + slong ord = n_randint(state, 3); + slong nbjet = acb_theta_jet_nb(ord, g); + acb_mat_t tau; + acb_ptr zs, th, test; + acb_theta_ctx_tau_t ctx_tau; + acb_theta_ctx_z_struct * vec; + slong j; + + acb_mat_init(tau, g, g); + zs = _acb_vec_init(nb * g); + th = _acb_vec_init(nb * nbth * nbjet); + test = _acb_vec_init(nb * nbth * nbjet); + acb_theta_ctx_tau_init(ctx_tau, 0, g); + vec = acb_theta_ctx_z_vec_init(nb, g); + + acb_siegel_randtest_reduced(tau, state, prec, bits); + acb_siegel_randtest_vec_reduced(zs, state, nb, tau, 0, prec); + + acb_theta_ctx_tau_set(ctx_tau, tau, prec); + for (j = 0; j < nb; j++) + { + acb_theta_ctx_z_set(&vec[j], zs + j * g, ctx_tau, prec); + } + + acb_theta_sum_jet(test, vec, nb, ctx_tau, ord, 1, all, prec); + acb_theta_ql_jet_fd(th, zs, nb, tau, ord, all, mprec); + + if (!_acb_vec_overlaps(th, test, nb * nbth * nbjet) + || !_acb_vec_is_finite(test, nb * nbth * nbjet) + || !_acb_vec_is_finite(th, nb * nbth * nbjet)) + { + flint_printf("FAIL\n"); + flint_printf("g = %wd, nb = %wd, ord = %wd, all = %wd, mprec = %wd, prec = %wd\n", + g, nb, ord, all, mprec, prec); + _acb_vec_printd(th, nb * nbth * nbjet, 5); + _acb_vec_printd(test, nb * nbth * nbjet, 5); + flint_printf("difference:\n"); + _acb_vec_sub(th, th, test, nb * nbth * nbjet, prec); + _acb_vec_printd(th, nb * nbth * nbjet, 5); + flint_abort(); + } + + acb_mat_clear(tau); + _acb_vec_clear(zs, nb * g); + _acb_vec_clear(th, nb * nbth * nbjet); + _acb_vec_clear(test, nb * nbth * nbjet); + acb_theta_ctx_tau_clear(ctx_tau); + acb_theta_ctx_z_vec_clear(vec, nb); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-jet_ql_bounds.c b/src/acb_theta/test/t-ql_local_bound.c similarity index 74% rename from src/acb_theta/test/t-jet_ql_bounds.c rename to src/acb_theta/test/t-ql_local_bound.c index 5295a449d7..e4e1393c71 100644 --- a/src/acb_theta/test/t-jet_ql_bounds.c +++ b/src/acb_theta/test/t-ql_local_bound.c @@ -10,24 +10,30 @@ */ #include "test_helpers.h" +#include "arb.h" +#include "acb.h" #include "acb_mat.h" #include "acb_theta.h" -TEST_FUNCTION_START(acb_theta_jet_ql_bounds, state) +TEST_FUNCTION_START(acb_theta_ql_local_bound, state) { slong iter; /* Test: bounds are finite, theta values correctly bounded */ for (iter = 0; iter < 20 * flint_test_multiplier(); iter++) { + slong g = 1 + n_randint(state, 3); + slong n = 1 << g; 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; + acb_theta_ctx_tau_t ctx_tau; + acb_theta_ctx_z_t ctx; + arb_ptr d; arb_t c, rho, abs, t; slong k; @@ -35,6 +41,9 @@ TEST_FUNCTION_START(acb_theta_jet_ql_bounds, state) z = _acb_vec_init(g); x = _acb_vec_init(g); th = _acb_vec_init(n2); + acb_theta_ctx_tau_init(ctx_tau, 1, g); + acb_theta_ctx_z_init(ctx, g); + d = _arb_vec_init(n); arb_init(c); arb_init(rho); arb_init(abs); @@ -47,9 +56,11 @@ TEST_FUNCTION_START(acb_theta_jet_ql_bounds, state) acb_urandom(&z[k], state, prec); } - acb_theta_jet_ql_bounds(c, rho, z, tau, ord); + acb_theta_ql_local_bound(c, rho, z, tau, ord); - if (!arb_is_finite(rho) || !arb_is_finite(c)) + if (!arb_is_finite(rho) + || !arb_is_finite(c) + || arb_contains_zero(rho)) { flint_printf("FAIL (infinite)\n"); acb_mat_printd(tau, 5); @@ -68,7 +79,12 @@ TEST_FUNCTION_START(acb_theta_jet_ql_bounds, state) } _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); + + acb_theta_ctx_tau_set(ctx_tau, tau, lp); + acb_theta_ctx_z_set(ctx, x, ctx_tau, lp); + acb_theta_eld_distances(d, x, 1, tau, lp); + acb_theta_sum(th, ctx, 1, ctx_tau, d, 1, 1, 1, lp); + _acb_vec_scalar_mul_arb(th, th, n2, &ctx->u, lp); arb_zero(abs); for (k = 0; k < n2; k++) @@ -77,7 +93,7 @@ TEST_FUNCTION_START(acb_theta_jet_ql_bounds, state) arb_max(abs, abs, t, lp); } - if (arb_gt(abs, c)) + if (arb_gt(abs, c) || !arb_is_finite(abs)) { flint_printf("FAIL (bound)\n"); acb_mat_printd(tau, 5); @@ -96,6 +112,9 @@ TEST_FUNCTION_START(acb_theta_jet_ql_bounds, state) _acb_vec_clear(z, g); _acb_vec_clear(x, g); _acb_vec_clear(th, n2); + acb_theta_ctx_tau_clear(ctx_tau); + acb_theta_ctx_z_clear(ctx); + _arb_vec_clear(d, n); arb_clear(c); arb_clear(rho); arb_clear(abs); diff --git a/src/acb_theta/test/t-ql_lower_dim.c b/src/acb_theta/test/t-ql_lower_dim.c new file mode 100644 index 0000000000..ab00ca6aa1 --- /dev/null +++ b/src/acb_theta/test/t-ql_lower_dim.c @@ -0,0 +1,147 @@ +/* + 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 3 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "arb.h" +#include "acb.h" +#include "acb_mat.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_ql_lower_dim, state) +{ + slong iter; + + /* Test: agrees with sum */ + for (iter = 0; iter < 25 * flint_test_multiplier(); iter++) + { + slong g = 2 + n_randint(state, 2); + slong n = 1 << g; + slong s = 1 + n_randint(state, g - 1); + slong n0 = 1 << s; + slong nba = 1 << (g - s); + ulong a = n_randint(state, nba); + slong prec = 100 + n_randint(state, 100); + int all = n_randint(state, 2); + slong nbth = (all ? n * n : n); + slong nbth0 = (all ? n0 * n0 : n0); + acb_mat_t tau, tau0; + acb_ptr z; + arb_ptr d, d0; + acb_ptr z0s, cofactors; + slong * pts; + arf_t err; + acb_theta_ctx_tau_t ctx_tau, ctx_tau0; + acb_theta_ctx_z_t ctx_z, ctx_z0; + acb_ptr th, th0s, test; + slong nb, fullprec; + slong j; + int res; + + acb_mat_init(tau, g, g); + z = _acb_vec_init(g); + d = _arb_vec_init(n); + acb_theta_ctx_tau_init(ctx_tau, 1, g); + acb_theta_ctx_tau_init(ctx_tau0, 1, s); + acb_theta_ctx_z_init(ctx_z, g); + acb_theta_ctx_z_init(ctx_z0, s); + th = _acb_vec_init(nbth); + test = _acb_vec_init(nbth); + arf_init(err); + + acb_siegel_randtest_compact(tau, state, 0, prec); + acb_siegel_randtest_vec_reduced(z, state, 1, tau, 0, prec); + + acb_theta_ctx_tau_set(ctx_tau, tau, prec); + acb_theta_ctx_z_set(ctx_z, z, ctx_tau, prec); + acb_theta_eld_distances(d, z, 1, tau, ACB_THETA_LOW_PREC); + acb_theta_sum(th, ctx_z, 1, ctx_tau, d, 1, all, 1, prec); + + res = acb_theta_ql_lower_dim(&z0s, &cofactors, &pts, &nb, err, + &fullprec, z, tau, d, s, a, prec); + + if (!res) + { + flint_printf("FAIL (ql_lower_dim)\n"); + flint_printf("\n\ng = %wd, s = %wd, all = %wd, a = %wd, tau, z:\n", g, s, all, a); + acb_mat_printd(tau, 5); + _acb_vec_printd(z, g, 5); + flint_abort(); + } + + th0s = _acb_vec_init(nb * nbth0); + d0 = _arb_vec_init(nb * n0); + acb_mat_window_init(tau0, tau, 0, 0, s, s); + + acb_theta_ctx_tau_set(ctx_tau0, tau0, prec); + acb_theta_eld_distances(d0, z0s, nb, tau0, ACB_THETA_LOW_PREC); + for (j = 0; j < nb; j++) + { + acb_theta_ctx_z_set(ctx_z0, z0s + j * s, ctx_tau0, prec); + acb_theta_sum(th0s + j * nbth0, ctx_z0, 1, ctx_tau0, d0 + j * n0, 1, all, 1, prec); + } + + acb_theta_ql_recombine(test, th0s, cofactors, pts, nb, err, fullprec, + s, a, all, g, prec); + if (all) + { + for (j = 0; j < n * n; j++) + { + if ((j >> g) % (1 << (g - s)) == a + && !acb_overlaps(&test[j], &th[j])) + { + res = 0; + break; + } + } + } + else + { + for (j = 0; j < n; j++) + { + if (j % (1 << (g - s)) == a + && !acb_overlaps(&test[j], &th[j])) + { + res = 0; + break; + } + } + } + + if (!res + || !_acb_vec_is_finite(th, nbth) + || !_acb_vec_is_finite(test, nbth)) + { + flint_printf("FAIL (overlap)\n"); + _acb_vec_printd(th, nbth, 5); + _acb_vec_printd(test, nbth, 5); + flint_abort(); + } + + acb_mat_clear(tau); + acb_mat_window_clear(tau0); + _acb_vec_clear(z, g); + _arb_vec_clear(d, n); + _arb_vec_clear(d0, nb * n0); + _acb_vec_clear(z0s, nb * s); + _acb_vec_clear(cofactors, nb); + arf_clear(err); + acb_theta_ctx_tau_clear(ctx_tau); + acb_theta_ctx_tau_clear(ctx_tau0); + acb_theta_ctx_z_clear(ctx_z); + acb_theta_ctx_z_clear(ctx_z0); + _acb_vec_clear(th, nbth); + _acb_vec_clear(test, nbth); + _acb_vec_clear(th0s, nb * nbth0); + flint_free(pts); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-ql_reduce.c b/src/acb_theta/test/t-ql_reduce.c deleted file mode 100644 index 80bafda922..0000000000 --- a/src/acb_theta/test/t-ql_reduce.c +++ /dev/null @@ -1,158 +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 3 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-ql_setup.c b/src/acb_theta/test/t-ql_setup.c new file mode 100644 index 0000000000..f0e365c55f --- /dev/null +++ b/src/acb_theta/test/t-ql_setup.c @@ -0,0 +1,146 @@ +/* + Copyright (C) 2024 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 3 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "arb.h" +#include "acb.h" +#include "acb_mat.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_ql_setup, state) +{ + slong iter; + + /* Test: roots are indeed nonzero */ + for (iter = 0; iter < 50 * flint_test_multiplier(); iter++) + { + slong g = 1 + n_randint(state, 3); + slong n = 1 << g; + slong prec = 100 + n_randint(state, 200); + slong nb = 1 + n_randint(state, 4); + slong nb_steps = n_randint(state, 6); + int all = n_randint(state, 2); + acb_mat_t tau; + acb_ptr zs, t, rts, rts_all; + arb_ptr distances; + slong * easy_steps; + slong guard = 0; + slong j, k, a; + int res; + + acb_mat_init(tau, g, g); + zs = _acb_vec_init(nb * g); + t = _acb_vec_init(g); + rts = _acb_vec_init(nb * 3 * n * nb_steps); + rts_all = _acb_vec_init(nb * n * n); + distances = _arb_vec_init(nb * n); + easy_steps = flint_malloc(nb * sizeof(slong)); + + acb_siegel_randtest_compact(tau, state, 1, prec); + acb_siegel_randtest_vec_reduced(zs + g, state, nb - 1, tau, 1, prec); + + /* Compute distances */ + acb_theta_eld_distances(distances, zs, nb, tau, prec); + + res = acb_theta_ql_setup(rts, rts_all, t, &guard, easy_steps, zs, nb, tau, + distances, nb_steps, all, prec); + + if (!res) + { + flint_printf("FAIL (ql_setup)\n"); + flint_printf("prec = %wd, nb = %wd, nb_steps = %wd, all = %wd, tau, z, distances:\n", + prec, nb, nb_steps, all); + acb_mat_printd(tau, 5); + _acb_vec_printd(zs, nb * g, 5); + _arb_vec_printd(distances, nb * n, 5); + flint_abort(); + } + + /* Check easy_steps[0] is minimal */ + for (j = 1; j < nb; j++) + { + if (easy_steps[j] < easy_steps[0]) + { + flint_printf("FAIL (easy steps)\n"); + flint_abort(); + } + } + + /* Check that roots are finite and all nonzero */ + if (!_acb_vec_is_finite(rts, nb * 3 * n * nb_steps) + || !_acb_vec_is_finite(rts_all, nb * n * n)) + { + flint_printf("FAIL (finiteness)\n"); + flint_abort(); + } + + for (j = 0; j < nb; j++) + { + for (k = 0; k < nb_steps; k++) + { + if (all && (k == 0)) + { + for (a = 0; a < n * n; a++) + { + if (acb_contains_zero(&rts_all[j * n * n + a]) + && (acb_theta_char_is_even(a, g) + || !_acb_vec_is_zero(zs + j * g, g))) + { + flint_printf("FAIL (rts_all)\n"); + flint_printf("j = %wd, k = %wd, a = %wd, all = %wd\n", j, k, a, all); + acb_printd(&rts_all[j * n * n + a], 5); + flint_printf("\n"); + flint_abort(); + } + } + } + else + { + for (a = 0; a < n; a++) + { + if ((k < easy_steps[j]) + && acb_contains_zero(&rts[j * (3 * n * nb_steps) + k * (3 * n) + a])) + { + flint_printf("FAIL (root 1)\n"); + flint_abort(); + } + if (k >= easy_steps[j] + && acb_contains_zero(&rts[j * (3 * n * nb_steps) + k * (3 * n) + n + a]) + && k > 0) + { + flint_printf("FAIL (root 2)\n"); + flint_printf("j = %wd, k = %wd, a = %wd\n", j, k, a); + acb_printd(&rts[j * (3 * n * nb_steps) + k * (3 * n) + n + a], 5); + flint_printf("\n"); + flint_abort(); + } + if (k >= easy_steps[j] + && acb_contains_zero(&rts[j * (3 * n * nb_steps) + k * (3 * n) + 2 * n + a])) + { + flint_printf("FAIL (root 3)\n"); + flint_abort(); + } + } + } + } + } + + acb_mat_clear(tau); + _acb_vec_clear(zs, nb * g); + _acb_vec_clear(t, g); + _acb_vec_clear(rts, nb * 3 * n * nb_steps); + _acb_vec_clear(rts_all, nb * n * n); + _arb_vec_clear(distances, nb * n); + flint_free(easy_steps); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-reduce_z.c b/src/acb_theta/test/t-reduce_z.c new file mode 100644 index 0000000000..3de71ecf04 --- /dev/null +++ b/src/acb_theta/test/t-reduce_z.c @@ -0,0 +1,95 @@ +/* + Copyright (C) 2025 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 3 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "fmpz.h" +#include "arb.h" +#include "acb.h" +#include "arb_mat.h" +#include "acb_mat.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_reduce_z, state) +{ + slong iter; + + /* Test: entries of r are even integers, im(new_zs) is im(zs - tau * r) */ + for (iter = 0; iter < 100 * flint_test_multiplier(); iter++) + { + slong g = 1 + n_randint(state, 3); + slong nb = n_randint(state, 3); + slong prec = 100 + n_randint(state, 200); + slong bits = n_randint(state, 4); + acb_ptr zs, new_zs, cs; + acb_mat_t tau; + arb_mat_t im; + arb_ptr rs, test, y; + fmpz_t x; + slong j; + int res; + + acb_mat_init(tau, g, g); + arb_mat_init(im, g, g); + zs = _acb_vec_init(nb * g); + new_zs = _acb_vec_init(nb * g); + cs = _acb_vec_init(nb); + test = _arb_vec_init(g); + y = _arb_vec_init(g); + rs = _arb_vec_init(nb * g); + fmpz_init(x); + + acb_siegel_randtest_reduced(tau, state, prec, bits); + acb_siegel_randtest_vec(zs, state, nb * g, prec); + acb_mat_get_imag(im, tau); + + res = acb_theta_reduce_z(new_zs, rs, cs, zs, nb, tau, prec); + + if (res) + { + for (j = 0; j < g * nb; j++) + { + res = arb_get_unique_fmpz(x, &rs[j]); + if (!res || !arb_is_exact(&rs[j]) || fmpz_mod_ui(x, x, 2) != 0) + { + flint_printf("FAIL (integers)\n"); + flint_abort(); + } + } + + for (j = 0; j < nb; j++) + { + arb_mat_vector_mul_col(test, im, rs + j * g, prec); + _acb_vec_get_imag(y, zs + j * g, g); + _arb_vec_sub(test, y, test, g, prec); + _acb_vec_get_imag(y, new_zs + j * g, g); + if (!_arb_vec_overlaps(test, y, g)) + { + flint_printf("FAIL (overlap)\n"); + _arb_vec_printd(y, g, 5); + _arb_vec_printd(test, g, 5); + flint_abort(); + } + } + } + + acb_mat_clear(tau); + arb_mat_clear(im); + _acb_vec_clear(zs, nb * g); + _acb_vec_clear(new_zs, nb * g); + _acb_vec_clear(cs, nb); + _arb_vec_clear(test, g); + _arb_vec_clear(y, g); + _arb_vec_clear(rs, nb * g); + fmpz_clear(x); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-siegel_cocycle.c b/src/acb_theta/test/t-siegel_cocycle.c index feab92b5b7..ab5f14c0df 100644 --- a/src/acb_theta/test/t-siegel_cocycle.c +++ b/src/acb_theta/test/t-siegel_cocycle.c @@ -10,6 +10,7 @@ */ #include "test_helpers.h" +#include "fmpz_mat.h" #include "acb_mat.h" #include "acb_theta.h" diff --git a/src/acb_theta/test/t-siegel_is_reduced.c b/src/acb_theta/test/t-siegel_is_reduced.c index ded4a46969..d03f0a4646 100644 --- a/src/acb_theta/test/t-siegel_is_reduced.c +++ b/src/acb_theta/test/t-siegel_is_reduced.c @@ -10,6 +10,7 @@ */ #include "test_helpers.h" +#include "acb.h" #include "acb_mat.h" #include "acb_theta.h" diff --git a/src/acb_theta/test/t-siegel_kappa.c b/src/acb_theta/test/t-siegel_kappa.c new file mode 100644 index 0000000000..f1ba28362c --- /dev/null +++ b/src/acb_theta/test/t-siegel_kappa.c @@ -0,0 +1,112 @@ +/* + 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 3 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "fmpz_mat.h" +#include "acb.h" +#include "acb_mat.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_siegel_kappa, state) +{ + slong iter; + + /* Test: compatibility with matrix multiplication, and compatible with sqr */ + for (iter = 0; iter < 100 * flint_test_multiplier(); iter++) + { + slong g = 1 + n_randint(state, 3); + slong bits = n_randint(state, 4); + slong prec = 200; + fmpz_mat_t m1, m2, m; + acb_mat_t tau; + acb_t s1, s2, s, t; + slong kappa1, kappa2, kappa, e1, e2, e; + ulong c1, c2, c; + + fmpz_mat_init(m1, 2 * g, 2 * g); + fmpz_mat_init(m2, 2 * g, 2 * g); + fmpz_mat_init(m, 2 * g, 2 * g); + acb_mat_init(tau, g, g); + acb_init(s1); + acb_init(s2); + acb_init(s); + acb_init(t); + + sp2gz_randtest(m1, state, bits); + sp2gz_randtest(m2, state, bits); + acb_siegel_randtest_reduced(tau, state, prec, bits); + + fmpz_mat_mul(m, m2, m1); + kappa = acb_siegel_kappa(s, m, tau, 0, prec); + + kappa1 = acb_siegel_kappa(s1, m1, tau, 0, prec); + acb_siegel_transform(tau, m1, tau, prec); + kappa2 = acb_siegel_kappa(s2, m2, tau, 0, prec); + + acb_theta_char_table(&c2, &e2, m2, 0); + acb_theta_char_table(&c1, &e1, m1, c2); + acb_theta_char_table(&c, &e, m, 0); + + if (c1 != c + || ((kappa1 + e1 + kappa2 + e2) % 4 != (kappa + e) % 4)) + { + flint_printf("FAIL (characteristics)\n"); + flint_printf("c1 = %wd, c2 = %wd, c = %wd, e1 = %wd, e2 = %wd, e = %wd, kappa1 = %wd, kappa2 = %wd, kappa = %wd\n", + c1, c2, c, e1, e2, e, kappa1, kappa2, kappa); + flint_abort(); + } + + acb_mul(t, s1, s2, prec); + if ((kappa1 + e1 + kappa2 + e2) % 8 != (kappa + e) % 8) + { + acb_neg(t, t); + } + if (!acb_overlaps(t, s) + || !acb_is_finite(t) + || !acb_is_finite(s)) + { + flint_printf("FAIL (square roots)\n"); + flint_printf("s, t:\n"); + acb_printd(t, 5); + flint_printf("\n"); + acb_printd(s, 5); + flint_printf("\n"); + flint_abort(); + } + + acb_sqr(t, s2, prec); + kappa = acb_siegel_kappa(s2, m2, tau, 1, prec); + if ((kappa != kappa2 % 4) + || !acb_overlaps(t, s2) + || !acb_is_finite(t) + || !acb_is_finite(s2)) + { + flint_printf("FAIL (sqr)\n"); + flint_printf("kappa2 = %wd, kappa = %wd, s2, t:\n", kappa2, kappa); + acb_printd(s2, 5); + flint_printf("\n"); + acb_printd(t, 5); + flint_printf("\n"); + flint_abort(); + } + + fmpz_mat_clear(m1); + fmpz_mat_clear(m2); + fmpz_mat_clear(m); + acb_mat_clear(tau); + acb_clear(s1); + acb_clear(s2); + acb_clear(s); + acb_clear(t); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-siegel_reduce.c b/src/acb_theta/test/t-siegel_reduce.c index 368cbac19f..2fd476e5dc 100644 --- a/src/acb_theta/test/t-siegel_reduce.c +++ b/src/acb_theta/test/t-siegel_reduce.c @@ -10,6 +10,7 @@ */ #include "test_helpers.h" +#include "fmpz_mat.h" #include "acb_mat.h" #include "acb_theta.h" diff --git a/src/acb_theta/test/t-siegel_transform.c b/src/acb_theta/test/t-siegel_transform.c index d1fc0e5134..b30a2d9159 100644 --- a/src/acb_theta/test/t-siegel_transform.c +++ b/src/acb_theta/test/t-siegel_transform.c @@ -10,6 +10,7 @@ */ #include "test_helpers.h" +#include "fmpz_mat.h" #include "acb_mat.h" #include "acb_theta.h" diff --git a/src/acb_theta/test/t-siegel_transform_z.c b/src/acb_theta/test/t-siegel_transform_z.c deleted file mode 100644 index 52a9062cad..0000000000 --- a/src/acb_theta/test/t-siegel_transform_z.c +++ /dev/null @@ -1,83 +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 3 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 index 4d558f3fd8..325937143e 100644 --- a/src/acb_theta/test/t-sp2gz_decompose.c +++ b/src/acb_theta/test/t-sp2gz_decompose.c @@ -10,6 +10,7 @@ */ #include "test_helpers.h" +#include "fmpz_mat.h" #include "acb_theta.h" static int diff --git a/src/acb_theta/test/t-sp2gz_inv.c b/src/acb_theta/test/t-sp2gz_inv.c index 7f0e1e5435..ef51f2c788 100644 --- a/src/acb_theta/test/t-sp2gz_inv.c +++ b/src/acb_theta/test/t-sp2gz_inv.c @@ -11,6 +11,7 @@ #include "test_helpers.h" #include "fmpz.h" +#include "fmpz_mat.h" #include "acb_theta.h" TEST_FUNCTION_START(acb_theta_sp2gz_inv, state) diff --git a/src/acb_theta/test/t-sp2gz_is_correct.c b/src/acb_theta/test/t-sp2gz_is_correct.c index d8d757a04b..db8969671a 100644 --- a/src/acb_theta/test/t-sp2gz_is_correct.c +++ b/src/acb_theta/test/t-sp2gz_is_correct.c @@ -10,6 +10,7 @@ */ #include "test_helpers.h" +#include "fmpz_mat.h" #include "acb_theta.h" TEST_FUNCTION_START(acb_theta_sp2gz_is_correct, state) diff --git a/src/acb_theta/test/t-sp2gz_set_blocks.c b/src/acb_theta/test/t-sp2gz_set_blocks.c index 298f99f65d..1bcc32ddae 100644 --- a/src/acb_theta/test/t-sp2gz_set_blocks.c +++ b/src/acb_theta/test/t-sp2gz_set_blocks.c @@ -10,6 +10,7 @@ */ #include "test_helpers.h" +#include "fmpz_mat.h" #include "acb_theta.h" TEST_FUNCTION_START(acb_theta_sp2gz_set_blocks, state) diff --git a/src/acb_theta/test/t-sum.c b/src/acb_theta/test/t-sum.c new file mode 100644 index 0000000000..377423dfad --- /dev/null +++ b/src/acb_theta/test/t-sum.c @@ -0,0 +1,119 @@ +/* + Copyright (C) 2024 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 3 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "acb.h" +#include "acb_mat.h" +#include "acb_modular.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_sum, state) +{ + slong iter; + + /* Test: matches acb_modular_theta on diagonal matrices */ + for (iter = 0; iter < 50 * flint_test_multiplier(); iter++) + { + slong g = 1 + n_randint(state, 3); + slong n = 1 << g; + slong mprec = 100 + n_randint(state, 100); + slong prec = mprec + 50; + int all_a = n_randint(state, 2); + int all_b = n_randint(state, 2); + slong nbth = (all_a ? n : 1) * (all_b ? n : 1); + acb_mat_t tau, tau11; + acb_ptr z; + acb_theta_ctx_tau_t ctx_tau; + acb_theta_ctx_z_t ctx; + arb_ptr d; + acb_ptr th, test, aux; + slong j, ab, a1b1; + + acb_mat_init(tau, g, g); + acb_mat_init(tau11, 1, 1); + z = _acb_vec_init(g); + acb_theta_ctx_tau_init(ctx_tau, 1, g); + acb_theta_ctx_z_init(ctx, g); + d = _arb_vec_init(n); + th = _acb_vec_init(nbth); + test = _acb_vec_init(nbth); + aux = _acb_vec_init(4); + + for (j = 0; j < g; j++) + { + acb_siegel_randtest_compact(tau11, state, 0, prec); + acb_set(acb_mat_entry(tau, j, j), acb_mat_entry(tau11, 0, 0)); + } + acb_theta_ctx_tau_set(ctx_tau, tau, prec); + + acb_siegel_randtest_vec_reduced(z, state, 1, tau, 0, prec); + acb_theta_ctx_z_set(ctx, z, ctx_tau, prec); + acb_theta_eld_distances(d, z, 1, tau, prec); + + /* Call sum at precision mprec, test against modular_theta */ + acb_theta_sum(th, ctx, 1, ctx_tau, d, all_a, all_b, 0, mprec); + + for (j = 0; j < nbth; j++) + { + acb_one(&test[j]); + } + for (j = 0; j < g; j++) + { + acb_modular_theta(&aux[3], &aux[2], &aux[0], &aux[1], + &z[j], acb_mat_entry(tau, j, j), prec); + acb_neg(&aux[3], &aux[3]); + for (ab = 0; ab < nbth; ab++) + { + if (all_a && !all_b) + { + a1b1 = 2 * acb_theta_char_bit(ab, j, g); + } + else + { + a1b1 = 2 * acb_theta_char_bit(ab, j, 2 * g) + + acb_theta_char_bit(ab, g + j, 2 * g); + } + acb_mul(&test[ab], &test[ab], &aux[a1b1], prec); + } + } + + if (!_acb_vec_overlaps(th, test, nbth) + || !_acb_vec_is_finite(th, nbth) + || !_acb_vec_is_finite(test, nbth)) + { + flint_printf("FAIL\n"); + flint_printf("g=%wd\n", g); + acb_mat_printd(tau, 5); + _acb_vec_printd(z, g, 5); + flint_printf("th: "); + _acb_vec_printd(th, nbth, 5); + flint_printf("test: "); + _acb_vec_printd(test, nbth, 5); + flint_printf("Difference: "); + _acb_vec_sub(th, th, test, nbth, prec); + _acb_vec_printd(th, nbth, 5); + flint_printf("\n"); + flint_abort(); + } + + acb_mat_clear(tau); + acb_mat_clear(tau11); + _acb_vec_clear(z, g); + acb_theta_ctx_tau_clear(ctx_tau); + acb_theta_ctx_z_clear(ctx); + _arb_vec_clear(d, n); + _acb_vec_clear(th, nbth); + _acb_vec_clear(test, nbth); + _acb_vec_clear(aux, 4); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-sum_jet.c b/src/acb_theta/test/t-sum_jet.c new file mode 100644 index 0000000000..e67498a5cf --- /dev/null +++ b/src/acb_theta/test/t-sum_jet.c @@ -0,0 +1,173 @@ +/* + Copyright (C) 2024 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 3 of the License, or + (at your option) any later version. See . +*/ + +#include "test_helpers.h" +#include "acb.h" +#include "arb_mat.h" +#include "acb_mat.h" +#include "acb_modular.h" +#include "acb_theta.h" + +TEST_FUNCTION_START(acb_theta_sum_jet, state) +{ + slong iter; + + /* Test: matches acb_modular_theta_jet in genus 1; + agrees with product of dim 1 case on diagonal matrices */ + for (iter = 0; iter < 50 * flint_test_multiplier(); iter++) + { + slong g = 1 + n_randint(state, 3); + slong n = 1 << g; + slong mprec = 100 + n_randint(state, 100); + slong prec = mprec + 50; + slong ord = n_randint(state, 3); + slong bits = n_randint(state, 4); + slong nbz = n_randint(state, 3); + slong nbjet = acb_theta_jet_nb(ord, g); + int all_a = n_randint(state, 2); + int all_b = n_randint(state, 2); + slong nbth = (all_a ? n : 1) * (all_b ? n : 1); + acb_mat_t tau, tau11; + acb_ptr zs; + acb_theta_ctx_tau_t ctx_tau; + acb_theta_ctx_z_struct * vec; + acb_ptr th, aux, test; + slong j, k, l; + + acb_mat_init(tau, g, g); + acb_mat_init(tau11, 1, 1); + zs = _acb_vec_init(nbz * g); + acb_theta_ctx_tau_init(ctx_tau, 0, g); + vec = acb_theta_ctx_z_vec_init(nbz, g); + th = _acb_vec_init(nbz * nbjet * nbth); + aux = _acb_vec_init(nbz * 4 * (ord + 1)); + test = _acb_vec_init(nbz * nbjet * nbth); + + /* Sample diagonal matrix tau */ + for (j = 0; j < g; j++) + { + acb_siegel_randtest_reduced(tau11, state, prec, bits); + acb_set(acb_mat_entry(tau, j, j), acb_mat_entry(tau11, 0, 0)); + } + acb_siegel_randtest_vec_reduced(zs, state, nbz, tau, 0, prec); + + /* Call sum_jet at precision mprec */ + acb_theta_ctx_tau_set(ctx_tau, tau, prec); + for (j = 0; j < nbz; j++) + { + acb_theta_ctx_z_set(&vec[j], zs + j * g, ctx_tau, prec); + } + acb_theta_sum_jet(th, vec, nbz, ctx_tau, ord, all_a, all_b, mprec); + + if (g == 1) + { + /* Make test vector using acb_modular_theta_jet */ + for (j = 0; j < nbz; j++) + { + acb_modular_theta_jet(aux + 3 * nbjet, aux + 2 * nbjet, aux, + aux + nbjet, &zs[j], acb_mat_entry(tau, 0, 0), nbjet, prec); + _acb_vec_neg(aux + 3 * nbjet, aux + 3 * nbjet, nbjet); + if (all_a && !all_b) + { + _acb_vec_set(test + 2 * j * nbjet, aux, nbjet); + _acb_vec_set(test + (2 * j + 1) * nbjet, aux + 2 * nbjet, nbjet); + } + else + { + _acb_vec_set(test + j * nbth * nbjet, aux, nbth * nbjet); + } + } + } + else + { + /* Make test vector using products of derivatives wrt each variable */ + slong * tups; + acb_theta_ctx_tau_t ctx_tau11; + acb_theta_ctx_z_struct * vec_g1; + ulong ab, a1b1; + + tups = flint_malloc(nbjet * g * sizeof(slong)); + acb_theta_ctx_tau_init(ctx_tau11, 0, 1); + vec_g1 = acb_theta_ctx_z_vec_init(nbz, 1); + + acb_theta_jet_tuples(tups, ord, g); + for (j = 0; j < nbth * nbjet * nbz; j++) + { + acb_one(&test[j]); + } + + for (k = 0; k < g; k++) + { + acb_set(acb_mat_entry(tau11, 0, 0), acb_mat_entry(tau, k, k)); + acb_theta_ctx_tau_set(ctx_tau11, tau11, prec); + for (j = 0; j < nbz; j++) + { + acb_theta_ctx_z_set(&vec_g1[j], &zs[j * g + k], ctx_tau11, prec); + } + + acb_theta_sum_jet(aux, vec_g1, nbz, ctx_tau11, ord, 1, 1, prec); + + for (j = 0; j < nbz; j++) + { + for (ab = 0; ab < nbth; ab++) + { + if (all_a && !all_b) + { + /* ab actually encodes the characteristic n * ab */ + a1b1 = 2 * acb_theta_char_bit(ab, k, g); + } + else + { + a1b1 = 2 * acb_theta_char_bit(ab, k, 2 * g) + + acb_theta_char_bit(ab, g + k, 2 * g); + } + for (l = 0; l < nbjet; l++) + { + acb_mul(&test[j * nbth * nbjet + ab * nbjet + l], + &test[j * nbth * nbjet + ab * nbjet + l], + &aux[j * 4 * (ord + 1) + a1b1 * (ord + 1) + tups[l * g + k]], prec); + } + } + } + } + + flint_free(tups); + acb_theta_ctx_tau_clear(ctx_tau11); + acb_theta_ctx_z_vec_clear(vec_g1, nbz); + } + + if (!_acb_vec_overlaps(th, test, nbth * nbz * nbjet) + || !_acb_vec_is_finite(th, nbth * nbz * nbjet) + || !_acb_vec_is_finite(test, nbth * nbz * nbjet)) + { + flint_printf("FAIL (g = %wd, ord = %wd, nbz = %wd, all_a = %wd, all_b = %wd, mprec = %wd, prec = %wd)\n", + g, ord, nbz, all_a, all_b, mprec, prec); + acb_mat_printd(tau, 5); + _acb_vec_printd(zs, nbz * g, 5); + flint_printf("th: "); + _acb_vec_printd(th, nbz * nbth * nbjet, 5); + flint_printf("test: "); + _acb_vec_printd(test, nbz * nbth * nbjet, 5); + flint_abort(); + } + + acb_mat_clear(tau); + acb_mat_clear(tau11); + _acb_vec_clear(zs, nbz * g); + acb_theta_ctx_tau_clear(ctx_tau); + acb_theta_ctx_z_vec_clear(vec, nbz); + _acb_vec_clear(th, nbz * nbth * nbjet); + _acb_vec_clear(aux, nbz * 4 * (ord + 1)); + _acb_vec_clear(test, nbz * nbth * nbjet); + } + + TEST_FUNCTION_END(state); +} diff --git a/src/acb_theta/test/t-jet_naive_radius.c b/src/acb_theta/test/t-sum_jet_radius.c similarity index 72% rename from src/acb_theta/test/t-jet_naive_radius.c rename to src/acb_theta/test/t-sum_jet_radius.c index bbb6d69149..789496cc1a 100644 --- a/src/acb_theta/test/t-jet_naive_radius.c +++ b/src/acb_theta/test/t-sum_jet_radius.c @@ -10,11 +10,12 @@ */ #include "test_helpers.h" +#include "acb.h" #include "arb_mat.h" #include "acb_mat.h" #include "acb_theta.h" -TEST_FUNCTION_START(acb_theta_jet_naive_radius, state) +TEST_FUNCTION_START(acb_theta_sum_jet_radius, state) { slong iter; @@ -29,12 +30,12 @@ TEST_FUNCTION_START(acb_theta_jet_naive_radius, state) slong nb = acb_theta_jet_nb(ord, g); acb_theta_eld_t E; acb_mat_t tau; - arb_mat_t C; + arb_mat_t cho, yinv; arf_t R2, eps; - acb_ptr z, new_z; - arb_ptr v, a; - acb_t c, term; - arb_t u, abs, sum; + acb_ptr z; + arb_ptr y, v; + acb_t term; + arb_t u, pi, abs, sum; slong nb_pts; slong * pts; slong * tups; @@ -42,34 +43,38 @@ TEST_FUNCTION_START(acb_theta_jet_naive_radius, state) int res; acb_mat_init(tau, g, g); - arb_mat_init(C, g, g); + arb_mat_init(cho, g, g); + arb_mat_init(yinv, 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); + y = _arb_vec_init(g); acb_init(term); arb_init(u); + arb_init(pi); 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_siegel_randtest_vec_reduced(z, state, 1, tau, 0, prec); - acb_theta_jet_naive_radius(R2, eps, C, v, ord, mprec); + acb_siegel_cho_yinv(cho, yinv, tau, prec); + _acb_vec_get_imag(y, z, g); + arb_mat_vector_mul_col(v, yinv, y, prec); + arb_dot(u, NULL, 0, v, 1, y, 1, g, prec); + arb_const_pi(pi, prec); + arb_mul(u, u, pi, prec); + arb_exp(u, u, prec); + arb_mat_vector_mul_col(v, cho, v, prec); + + acb_theta_sum_jet_radius(R2, eps, cho, 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); + res = acb_theta_eld_set(E, cho, R2, v); if (!res) { flint_printf("FAIL (ellipsoid)\n"); @@ -86,16 +91,14 @@ TEST_FUNCTION_START(acb_theta_jet_naive_radius, state) 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_theta_sum_term(term, 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)) + if (arb_gt(sum, u) + || !arb_is_finite(sum) + || !arb_is_finite(u)) { flint_printf("FAIL\n"); flint_printf("sum, bound:\n"); @@ -112,17 +115,17 @@ TEST_FUNCTION_START(acb_theta_jet_naive_radius, state) } acb_mat_clear(tau); - arb_mat_clear(C); + arb_mat_clear(cho); + arb_mat_clear(yinv); 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_vec_clear(y, g); acb_clear(term); arb_clear(u); + arb_clear(pi); arb_clear(abs); arb_clear(sum); flint_free(pts); diff --git a/src/acb_theta/test/t-naive_radius.c b/src/acb_theta/test/t-sum_radius.c similarity index 68% rename from src/acb_theta/test/t-naive_radius.c rename to src/acb_theta/test/t-sum_radius.c index 136c66f527..ad765d11ba 100644 --- a/src/acb_theta/test/t-naive_radius.c +++ b/src/acb_theta/test/t-sum_radius.c @@ -10,11 +10,12 @@ */ #include "test_helpers.h" +#include "acb.h" #include "arb_mat.h" #include "acb_mat.h" #include "acb_theta.h" -TEST_FUNCTION_START(acb_theta_naive_radius, state) +TEST_FUNCTION_START(acb_theta_sum_radius, state) { slong iter; @@ -27,50 +28,54 @@ TEST_FUNCTION_START(acb_theta_naive_radius, state) slong bits = n_randint(state, 4); acb_theta_eld_t E; acb_mat_t tau; - arb_mat_t C; + arb_mat_t cho, yinv; arf_t R2, eps; - acb_ptr z, new_z; - arb_ptr v, a; - acb_t c, term; - arb_t u, abs, sum; + acb_ptr z; + arb_ptr y, v; + acb_t term; + arb_t u, pi, abs, sum; slong nb_pts; slong * pts; slong k; int res; acb_mat_init(tau, g, g); - arb_mat_init(C, g, g); + arb_mat_init(cho, g, g); + arb_mat_init(yinv, 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); + y = _arb_vec_init(g); v = _arb_vec_init(g); - a = _arb_vec_init(g); - acb_init(c); arb_init(u); + arb_init(pi); 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_siegel_randtest_vec_reduced(z, state, 1, tau, 0, prec); + + acb_siegel_cho_yinv(cho, yinv, tau, prec); + _acb_vec_get_imag(y, z, g); + arb_mat_vector_mul_col(v, yinv, y, prec); + arb_dot(u, NULL, 0, v, 1, y, 1, g, prec); + arb_const_pi(pi, prec); + arb_mul(u, u, pi, prec); + arb_exp(u, u, prec); + arb_mat_vector_mul_col(v, cho, v, prec); - acb_theta_naive_radius(R2, eps, C, 0, mprec); + acb_theta_sum_radius(R2, eps, cho, 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); + res = acb_theta_eld_set(E, cho, R2, v); if (!res) { flint_printf("FAIL (ellipsoid)\n"); acb_mat_printd(tau, 5); - arb_mat_printd(C, 5); + arb_mat_printd(cho, 5); _acb_vec_printd(z, g, 5); _arb_vec_printd(v, g, 5); flint_abort(); @@ -83,15 +88,14 @@ TEST_FUNCTION_START(acb_theta_naive_radius, state) arb_zero(sum); for (k = 0; k < nb_pts; k++) { - acb_theta_naive_term(term, new_z, tau, NULL, pts + k * g, prec); + acb_theta_sum_term(term, 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)) + if (arb_gt(sum, u) + || !arb_is_finite(sum) + || !arb_is_finite(u)) { flint_printf("FAIL\n"); flint_printf("sum, bound:\n"); @@ -100,24 +104,24 @@ TEST_FUNCTION_START(acb_theta_naive_radius, state) 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); + 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); + arb_mat_clear(cho); + arb_mat_clear(yinv); 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_vec_clear(y, g); arb_clear(u); acb_clear(term); + arb_clear(pi); arb_clear(abs); arb_clear(sum); flint_free(pts); diff --git a/src/acb_theta/test/t-transform_kappa.c b/src/acb_theta/test/t-transform_kappa.c deleted file mode 100644 index 17d1f36911..0000000000 --- a/src/acb_theta/test/t-transform_kappa.c +++ /dev/null @@ -1,61 +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 3 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_sqrtdet.c b/src/acb_theta/test/t-transform_sqrtdet.c deleted file mode 100644 index 281a1b5d8d..0000000000 --- a/src/acb_theta/test/t-transform_sqrtdet.c +++ /dev/null @@ -1,53 +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 3 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 deleted file mode 100644 index 7628443e62..0000000000 --- a/src/acb_theta/transform_char.c +++ /dev/null @@ -1,142 +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 3 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 deleted file mode 100644 index b85260c6a4..0000000000 --- a/src/acb_theta/transform_kappa.c +++ /dev/null @@ -1,186 +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 3 of the License, or - (at your option) any later version. See . -*/ - -#include "acb.h" -#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 deleted file mode 100644 index b2bc5a0b7b..0000000000 --- a/src/acb_theta/transform_kappa2.c +++ /dev/null @@ -1,153 +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 3 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_sqrtdet.c b/src/acb_theta/transform_sqrtdet.c deleted file mode 100644 index f982ede10b..0000000000 --- a/src/acb_theta/transform_sqrtdet.c +++ /dev/null @@ -1,185 +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 3 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_rand_init(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_rand_clear(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); -}