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
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
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
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