Example #1
0
def djtheta(n, z, q, nd=1):
    r"""
    For an integer `nd \ge 1`, computes the `nd`:th derivative with
    respect to `z` of the Jacobi theta function `\vartheta_n(z,q)`::

        >>> from mpmath import *
        >>> mp.dps = 15; mp.pretty = True
        >>> djtheta(3, 7, 0.2)
        -0.795947847483158
        >>> diff(lambda x: jtheta(3, x, 0.2), 7)
        -0.795947847483158

    For additional details, see :func:`jtheta`.
    """

    z = mpmathify(z)
    q = mpmathify(q)

    if abs(q) > Q_LIM:
        raise ValueError('abs(q) > Q_LIM = %f' % Q_LIM)
    extra = 10 + mp.prec * nd // 10
    if z:
        M = mp.mag(z)
        if M > 5 or (n != 1 and M < -5):
            extra += 2 * abs(M)
    cz = 0.5
    extra2 = 50
    prec0 = mp.prec
    try:
        mp.prec += extra
        if n == 1:
            if abs(z.imag) != 0:
                if abs(z.imag) < cz * abs(log(q).real):
                    mp.dps += extra2
                    res = _djacobi_theta2(z - pi / 2, q, nd)
                else:
                    mp.dps += 10
                    res = _djacobi_theta2a(z - pi / 2, q, nd)
            else:
                res = _djacobi_theta2(z - pi / 2, q, nd)
        elif n == 2:
            if abs(z.imag) != 0:
                if abs(z.imag) < cz * abs(log(q).real):
                    mp.dps += extra2
                    res = _djacobi_theta2(z, q, nd)
                else:
                    mp.dps += 10
                    res = _djacobi_theta2a(z, q, nd)
            else:
                res = _djacobi_theta2(z, q, nd)
        elif n == 3:
            if abs(z.imag) != 0:
                if abs(z.imag) < cz * abs(log(q).real):
                    mp.dps += extra2
                    res = _djacobi_theta3(z, q, nd)
                else:
                    mp.dps += 10
                    res = _djacobi_theta3a(z, q, nd)
            else:
                res = _djacobi_theta3(z, q, nd)
        elif n == 4:
            if abs(z.imag) != 0:
                if abs(z.imag) < cz * abs(log(q).real):
                    mp.dps += extra2
                    res = _djacobi_theta3(z, -q, nd)
                else:
                    mp.dps += 10
                    res = _djacobi_theta3a(z, -q, nd)
            else:
                res = _djacobi_theta3(z, -q, nd)
        else:
            raise ValueError
    finally:
        mp.prec = prec0
    return res
Example #2
0
def jtheta(n, z, q):
    r"""
    Computes the Jacobi theta function `\vartheta_n(z, q)`, where
    `n = 1, 2, 3, 4`. The theta functions are functions of two
    variables:

    * `z` is the *argument*, an arbitrary real or complex number

    * `q` is the *nome*, which must be a real or complex number
      in the unit disk (i.e. `|q| < 1`)

    One also commonly encounters the notation `\vartheta_n(z, \tau)`
    in the literature. The variable `\tau` is called the *parameter*
    and can be converted to a nome using the formula
    `q = \exp(i \pi \tau)`. Note the condition `|q| < 1` requires
    `\Im(\tau) > 0`; i.e. Jacobi theta functions are defined for
    `\tau` in the upper half plane.

    Other notations are also in use. For example, some authors use
    the single-argument form `\vartheta_n(x)`. Depending on context,
    this can mean ``jtheta(n, 0, x)``, ``jtheta(n, x, q)``, or possibly
    something else. Needless to say, it is a good idea to cross-check
    the definitions when working with theta functions.

    **Definition**

    The four Jacobi theta functions as implemented by :func:`jtheta`
    are defined by the following infinite series:

    .. math ::

      \vartheta_1(z,q) = 2 q^{1/4} \sum_{n=0}^{\infty}
        (-1)^n q^{n^2+n\,} \sin((2n+1)z)

      \vartheta_2(z,q) = 2 q^{1/4} \sum_{n=0}^{\infty}
        q^{n^{2\,} + n} \cos((2n+1)z)

      \vartheta_3(z,q) = 1 + 2 \sum_{n=0}^{\infty}
        q^{n^2\,} \cos(2 n z)

      \vartheta_4(z,q) = 1 + 2 \sum_{n=0}^{\infty}
        (-q)^{n^2\,} \cos(2 n z)

    For `|q| \ll 1`, these series converge very quickly, so the
    Jacobi theta functions can efficiently be evaluated to high
    precision.

    **Examples and basic properties**

    Considered as functions of `z`, the Jacobi theta functions may be
    viewed as generalizations of the ordinary trigonometric functions
    cos and sin. They are periodic functions::

        >>> from mpmath import *
        >>> mp.dps = 15; mp.pretty = True
        >>> jtheta(1, 0.1, 1/5.)
        0.117756191842059
        >>> jtheta(1, 0.1 + 2*pi, 1/5.)
        0.117756191842059

    Indeed, the series defining the theta functions are essentially
    trigonometric Fourier series. The coefficients can be retrieved
    using :func:`fourier`::

        >>> nprint(fourier(lambda x: jtheta(2, x, 0.5), [-pi, pi], 4))
        ([0.0, 1.68179, 0.0, 0.420448, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0])

    The Jacobi theta functions are also so-called quasiperiodic
    functions of `z` and `\tau`, meaning that for fixed `\tau`,
    `\vartheta_n(z, q)` and `\vartheta_n(z+\pi \tau, q)` are the same
    except for an exponential factor::

        >>> tau = 0.3*j
        >>> q = exp(pi*j*tau)
        >>> z = 10
        >>> jtheta(4, z+tau*pi, q)
        (-0.682420280786035 + 1.5266839997214j)
        >>> -exp(-2*j*z)/q * jtheta(4, z, q)
        (-0.682420280786035 + 1.5266839997214j)

    The Jacobi theta functions satisfy a huge number of other
    functional equations, such as the following identity (valid for
    any `q`)::

        >>> q = 0.3
        >>> jtheta(3,0,q)**4
        6.82374408935276
        >>> jtheta(2,0,q)**4 + jtheta(4,0,q)**4
        6.82374408935276

    Extensive listings of identities satisfied by the Jacobi theta
    functions can be found in standard reference works.

    The Jacobi theta functions are related to the gamma function
    for special arguments::

        >>> jtheta(3, 0, exp(-pi))
        1.08643481121331
        >>> pi**(1/4.) / gamma(3/4.)
        1.08643481121331

    :func:`jtheta` supports arbitrary precision evaluation and complex
    arguments::

        >>> mp.dps = 50
        >>> jtheta(4, sqrt(2), 0.5)
        2.0549510717571539127004115835148878097035750653737
        >>> mp.dps = 25
        >>> jtheta(4, 1+2j, (1+j)/5)
        (7.180331760146805926356634 - 1.634292858119162417301683j)

    **Possible issues**

    For `|q| \ge 1` or `\Im(\tau) \le 0`, :func:`jtheta` raises
    ``ValueError``. This exception is also raised for `|q|` extremely
    close to 1 (or equivalently `\tau` very close to 0), since the
    series would converge too slowly::

        >>> jtheta(1, 10, 0.99999999 * exp(0.5*j))
        Traceback (most recent call last):
          ...
        ValueError: abs(q) > Q_LIM = 1.000000

    """
    z = mpmathify(z)
    q = mpmathify(q)

    # Implementation note
    # If z.imag is close to zero, _jacobi_theta2 and _jacobi_theta3
    # are used,
    # which compute the series starting from n=0 using fixed precision
    # numbers;
    # otherwise  _jacobi_theta2a and _jacobi_theta3a are used, which compute
    # the series starting from n=n0, which is the largest term.

    # TODO write _jacobi_theta2a and _jacobi_theta3a using fixed precision.

    if abs(q) > Q_LIM:
        raise ValueError('abs(q) > Q_LIM = %f' % Q_LIM)

    extra = 10
    if z:
        M = mp.mag(z)
        if M > 5 or (n == 1 and M < -5):
            extra += 2*abs(M)
    cz = 0.5
    extra2 = 50
    prec0 = mp.prec
    try:
        mp.prec += extra
        if n == 1:
            if abs(z.imag) != 0:
                if abs(z.imag) < cz * abs(log(q).real):
                    mp.dps += extra2
                    res = _jacobi_theta2(z - pi/2, q)
                else:
                    mp.dps += 10
                    res = _jacobi_theta2a(z - pi/2, q)
            else:
                res = _jacobi_theta2(z - pi/2, q)
        elif n == 2:
            if abs(z.imag) != 0:
                if abs(z.imag) < cz * abs(log(q).real):
                    mp.dps += extra2
                    res = _jacobi_theta2(z, q)
                else:
                    mp.dps += 10
                    res = _jacobi_theta2a(z, q)
            else:
                res = _jacobi_theta2(z, q)
        elif n == 3:
            if abs(z.imag) != 0:
                if abs(z.imag) < cz * abs(log(q).real):
                    mp.dps += extra2
                    res = _jacobi_theta3(z, q)
                else:
                    mp.dps += 10
                    res = _jacobi_theta3a(z, q)
            else:
                res = _jacobi_theta3(z, q)
        elif n == 4:
            if abs(z.imag) != 0:
                if abs(z.imag) < cz * abs(log(q).real):
                    mp.dps += extra2
                    res = _jacobi_theta3(z, -q)
                else:
                    mp.dps += 10
                    res = _jacobi_theta3a(z, -q)
            else:
                res = _jacobi_theta3(z, -q)
        else:
            raise ValueError
    finally:
        mp.prec = prec0
    return res
Example #3
0
def jtheta(n, z, q):
    r"""
    Computes the Jacobi theta function `\vartheta_n(z, q)`, where
    `n = 1, 2, 3, 4`. The theta functions are functions of two
    variables:

    * `z` is the *argument*, an arbitrary real or complex number

    * `q` is the *nome*, which must be a real or complex number
      in the unit disk (i.e. `|q| < 1`)

    One also commonly encounters the notation `\vartheta_n(z, \tau)`
    in the literature. The variable `\tau` is called the *parameter*
    and can be converted to a nome using the formula
    `q = \exp(i \pi \tau)`. Note the condition `|q| < 1` requires
    `\Im(\tau) > 0`; i.e. Jacobi theta functions are defined for
    `\tau` in the upper half plane.

    Other notations are also in use. For example, some authors use
    the single-argument form `\vartheta_n(x)`. Depending on context,
    this can mean ``jtheta(n, 0, x)``, ``jtheta(n, x, q)``, or possibly
    something else. Needless to say, it is a good idea to cross-check
    the definitions when working with theta functions.

    **Definition**

    The four Jacobi theta functions as implemented by :func:`jtheta`
    are defined by the following infinite series:

    .. math ::

      \vartheta_1(z,q) = 2 q^{1/4} \sum_{n=0}^{\infty}
        (-1)^n q^{n^2+n\,} \sin((2n+1)z)

      \vartheta_2(z,q) = 2 q^{1/4} \sum_{n=0}^{\infty}
        q^{n^{2\,} + n} \cos((2n+1)z)

      \vartheta_3(z,q) = 1 + 2 \sum_{n=0}^{\infty}
        q^{n^2\,} \cos(2 n z)

      \vartheta_4(z,q) = 1 + 2 \sum_{n=0}^{\infty}
        (-q)^{n^2\,} \cos(2 n z)

    For `|q| \ll 1`, these series converge very quickly, so the
    Jacobi theta functions can efficiently be evaluated to high
    precision.

    **Examples and basic properties**

    Considered as functions of `z`, the Jacobi theta functions may be
    viewed as generalizations of the ordinary trigonometric functions
    cos and sin. They are periodic functions::

        >>> from mpmath import *
        >>> mp.dps = 15; mp.pretty = True
        >>> jtheta(1, 0.1, 1/5.)
        0.117756191842059
        >>> jtheta(1, 0.1 + 2*pi, 1/5.)
        0.117756191842059

    Indeed, the series defining the theta functions are essentially
    trigonometric Fourier series. The coefficients can be retrieved
    using :func:`fourier`::

        >>> nprint(fourier(lambda x: jtheta(2, x, 0.5), [-pi, pi], 4))
        ([0.0, 1.68179, 0.0, 0.420448, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0])

    The Jacobi theta functions are also so-called quasiperiodic
    functions of `z` and `\tau`, meaning that for fixed `\tau`,
    `\vartheta_n(z, q)` and `\vartheta_n(z+\pi \tau, q)` are the same
    except for an exponential factor::

        >>> tau = 0.3*j
        >>> q = exp(pi*j*tau)
        >>> z = 10
        >>> jtheta(4, z+tau*pi, q)
        (-0.682420280786035 + 1.5266839997214j)
        >>> -exp(-2*j*z)/q * jtheta(4, z, q)
        (-0.682420280786035 + 1.5266839997214j)

    The Jacobi theta functions satisfy a huge number of other
    functional equations, such as the following identity (valid for
    any `q`)::

        >>> q = 0.3
        >>> jtheta(3,0,q)**4
        6.82374408935276
        >>> jtheta(2,0,q)**4 + jtheta(4,0,q)**4
        6.82374408935276

    Extensive listings of identities satisfied by the Jacobi theta
    functions can be found in standard reference works.

    The Jacobi theta functions are related to the gamma function
    for special arguments::

        >>> jtheta(3, 0, exp(-pi))
        1.08643481121331
        >>> pi**(1/4.) / gamma(3/4.)
        1.08643481121331

    :func:`jtheta` supports arbitrary precision evaluation and complex
    arguments::

        >>> mp.dps = 50
        >>> jtheta(4, sqrt(2), 0.5)
        2.0549510717571539127004115835148878097035750653737
        >>> mp.dps = 25
        >>> jtheta(4, 1+2j, (1+j)/5)
        (7.180331760146805926356634 - 1.634292858119162417301683j)

    **Possible issues**

    For `|q| \ge 1` or `\Im(\tau) \le 0`, :func:`jtheta` raises
    ``ValueError``. This exception is also raised for `|q|` extremely
    close to 1 (or equivalently `\tau` very close to 0), since the
    series would converge too slowly::

        >>> jtheta(1, 10, 0.99999999 * exp(0.5*j))
        Traceback (most recent call last):
          ...
        ValueError: abs(q) > Q_LIM = 1.000000

    """
    z = mpmathify(z)
    q = mpmathify(q)

    # Implementation note
    # If z.imag is close to zero, _jacobi_theta2 and _jacobi_theta3
    # are used,
    # which compute the series starting from n=0 using fixed precision
    # numbers;
    # otherwise  _jacobi_theta2a and _jacobi_theta3a are used, which compute
    # the series starting from n=n0, which is the largest term.

    # TODO write _jacobi_theta2a and _jacobi_theta3a using fixed precision.

    if abs(q) > Q_LIM:
        raise ValueError('abs(q) > Q_LIM = %f' % Q_LIM)

    extra = 10
    if z:
        M = mp.mag(z)
        if M > 5 or (n == 1 and M < -5):
            extra += 2 * abs(M)
    cz = 0.5
    extra2 = 50
    prec0 = mp.prec
    try:
        mp.prec += extra
        if n == 1:
            if abs(z.imag) != 0:
                if abs(z.imag) < cz * abs(log(q).real):
                    mp.dps += extra2
                    res = _jacobi_theta2(z - pi / 2, q)
                else:
                    mp.dps += 10
                    res = _jacobi_theta2a(z - pi / 2, q)
            else:
                res = _jacobi_theta2(z - pi / 2, q)
        elif n == 2:
            if abs(z.imag) != 0:
                if abs(z.imag) < cz * abs(log(q).real):
                    mp.dps += extra2
                    res = _jacobi_theta2(z, q)
                else:
                    mp.dps += 10
                    res = _jacobi_theta2a(z, q)
            else:
                res = _jacobi_theta2(z, q)
        elif n == 3:
            if abs(z.imag) != 0:
                if abs(z.imag) < cz * abs(log(q).real):
                    mp.dps += extra2
                    res = _jacobi_theta3(z, q)
                else:
                    mp.dps += 10
                    res = _jacobi_theta3a(z, q)
            else:
                res = _jacobi_theta3(z, q)
        elif n == 4:
            if abs(z.imag) != 0:
                if abs(z.imag) < cz * abs(log(q).real):
                    mp.dps += extra2
                    res = _jacobi_theta3(z, -q)
                else:
                    mp.dps += 10
                    res = _jacobi_theta3a(z, -q)
            else:
                res = _jacobi_theta3(z, -q)
        else:
            raise ValueError
    finally:
        mp.prec = prec0
    return res
Example #4
0
def djtheta(n, z, q, nd=1):
    r"""
    For an integer `nd \ge 1`, computes the `nd`:th derivative with
    respect to `z` of the Jacobi theta function `\vartheta_n(z,q)`::

        >>> from mpmath import *
        >>> mp.dps = 15; mp.pretty = True
        >>> djtheta(3, 7, 0.2)
        -0.795947847483158
        >>> diff(lambda x: jtheta(3, x, 0.2), 7)
        -0.795947847483158

    For additional details, see :func:`jtheta`.
    """

    z = mpmathify(z)
    q = mpmathify(q)

    if abs(q) > Q_LIM:
        raise ValueError('abs(q) > Q_LIM = %f' % Q_LIM)
    extra = 10 + mp.prec * nd // 10
    if z:
        M = mp.mag(z)
        if M > 5 or (n != 1 and M < -5):
            extra += 2*abs(M)
    cz = 0.5
    extra2 = 50
    prec0 = mp.prec
    try:
        mp.prec += extra
        if n == 1:
            if abs(z.imag) != 0:
                if abs(z.imag) < cz * abs(log(q).real):
                    mp.dps += extra2
                    res = _djacobi_theta2(z - pi/2, q, nd)
                else:
                    mp.dps += 10
                    res = _djacobi_theta2a(z - pi/2, q, nd)
            else:
                res = _djacobi_theta2(z - pi/2, q, nd)
        elif n == 2:
            if abs(z.imag) != 0:
                if abs(z.imag) < cz * abs(log(q).real):
                    mp.dps += extra2
                    res = _djacobi_theta2(z, q, nd)
                else:
                    mp.dps += 10
                    res = _djacobi_theta2a(z, q, nd)
            else:
                res = _djacobi_theta2(z, q, nd)
        elif n == 3:
            if abs(z.imag) != 0:
                if abs(z.imag) < cz * abs(log(q).real):
                    mp.dps += extra2
                    res = _djacobi_theta3(z, q, nd)
                else:
                    mp.dps += 10
                    res = _djacobi_theta3a(z, q, nd)
            else:
                res = _djacobi_theta3(z, q, nd)
        elif n == 4:
            if abs(z.imag) != 0:
                if abs(z.imag) < cz * abs(log(q).real):
                    mp.dps += extra2
                    res = _djacobi_theta3(z, -q, nd)
                else:
                    mp.dps += 10
                    res = _djacobi_theta3a(z, -q, nd)
            else:
                res = _djacobi_theta3(z, -q, nd)
        else:
            raise ValueError
    finally:
        mp.prec = prec0
    return res