Videre
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,542 @@
|
||||
"""
|
||||
Implement the cmath module functions.
|
||||
"""
|
||||
|
||||
|
||||
import cmath
|
||||
import math
|
||||
|
||||
from numba.core.imputils import impl_ret_untracked
|
||||
from numba.core import types
|
||||
from numba.core.typing import signature
|
||||
from numba.cpython import mathimpl
|
||||
from numba.core.extending import overload
|
||||
|
||||
# registry = Registry('cmathimpl')
|
||||
# lower = registry.lower
|
||||
|
||||
|
||||
def is_nan(builder, z):
|
||||
return builder.fcmp_unordered('uno', z.real, z.imag)
|
||||
|
||||
def is_inf(builder, z):
|
||||
return builder.or_(mathimpl.is_inf(builder, z.real),
|
||||
mathimpl.is_inf(builder, z.imag))
|
||||
|
||||
def is_finite(builder, z):
|
||||
return builder.and_(mathimpl.is_finite(builder, z.real),
|
||||
mathimpl.is_finite(builder, z.imag))
|
||||
|
||||
|
||||
# @lower(cmath.isnan, types.Complex)
|
||||
def isnan_float_impl(context, builder, sig, args):
|
||||
[typ] = sig.args
|
||||
[value] = args
|
||||
z = context.make_complex(builder, typ, value=value)
|
||||
res = is_nan(builder, z)
|
||||
return impl_ret_untracked(context, builder, sig.return_type, res)
|
||||
|
||||
# @lower(cmath.isinf, types.Complex)
|
||||
def isinf_float_impl(context, builder, sig, args):
|
||||
[typ] = sig.args
|
||||
[value] = args
|
||||
z = context.make_complex(builder, typ, value=value)
|
||||
res = is_inf(builder, z)
|
||||
return impl_ret_untracked(context, builder, sig.return_type, res)
|
||||
|
||||
|
||||
# @lower(cmath.isfinite, types.Complex)
|
||||
def isfinite_float_impl(context, builder, sig, args):
|
||||
[typ] = sig.args
|
||||
[value] = args
|
||||
z = context.make_complex(builder, typ, value=value)
|
||||
res = is_finite(builder, z)
|
||||
return impl_ret_untracked(context, builder, sig.return_type, res)
|
||||
|
||||
|
||||
# @overload(cmath.rect)
|
||||
def impl_cmath_rect(r, phi):
|
||||
if all([isinstance(typ, types.Float) for typ in [r, phi]]):
|
||||
def impl(r, phi):
|
||||
if not math.isfinite(phi):
|
||||
if not r:
|
||||
# cmath.rect(0, phi={inf, nan}) = 0
|
||||
return abs(r)
|
||||
if math.isinf(r):
|
||||
# cmath.rect(inf, phi={inf, nan}) = inf + j phi
|
||||
return complex(r, phi)
|
||||
real = math.cos(phi)
|
||||
imag = math.sin(phi)
|
||||
if real == 0. and math.isinf(r):
|
||||
# 0 * inf would return NaN, we want to keep 0 but xor the sign
|
||||
real /= r
|
||||
else:
|
||||
real *= r
|
||||
if imag == 0. and math.isinf(r):
|
||||
# ditto
|
||||
imag /= r
|
||||
else:
|
||||
imag *= r
|
||||
return complex(real, imag)
|
||||
return impl
|
||||
|
||||
|
||||
def intrinsic_complex_unary(inner_func):
|
||||
def wrapper(context, builder, sig, args):
|
||||
[typ] = sig.args
|
||||
[value] = args
|
||||
z = context.make_complex(builder, typ, value=value)
|
||||
x = z.real
|
||||
y = z.imag
|
||||
# Same as above: math.isfinite() is unavailable on 2.x so we precompute
|
||||
# its value and pass it to the pure Python implementation.
|
||||
x_is_finite = mathimpl.is_finite(builder, x)
|
||||
y_is_finite = mathimpl.is_finite(builder, y)
|
||||
inner_sig = signature(sig.return_type,
|
||||
*(typ.underlying_float,) * 2 + (types.boolean,) * 2)
|
||||
res = context.compile_internal(builder, inner_func, inner_sig,
|
||||
(x, y, x_is_finite, y_is_finite))
|
||||
return impl_ret_untracked(context, builder, sig, res)
|
||||
return wrapper
|
||||
|
||||
|
||||
NAN = float('nan')
|
||||
INF = float('inf')
|
||||
|
||||
# @lower(cmath.exp, types.Complex)
|
||||
@intrinsic_complex_unary
|
||||
def exp_impl(x, y, x_is_finite, y_is_finite):
|
||||
"""cmath.exp(x + y j)"""
|
||||
if x_is_finite:
|
||||
if y_is_finite:
|
||||
c = math.cos(y)
|
||||
s = math.sin(y)
|
||||
r = math.exp(x)
|
||||
return complex(r * c, r * s)
|
||||
else:
|
||||
return complex(NAN, NAN)
|
||||
elif math.isnan(x):
|
||||
if y:
|
||||
return complex(x, x) # nan + j nan
|
||||
else:
|
||||
return complex(x, y) # nan + 0j
|
||||
elif x > 0.0:
|
||||
# x == +inf
|
||||
if y_is_finite:
|
||||
real = math.cos(y)
|
||||
imag = math.sin(y)
|
||||
# Avoid NaNs if math.cos(y) or math.sin(y) == 0
|
||||
# (e.g. cmath.exp(inf + 0j) == inf + 0j)
|
||||
if real != 0:
|
||||
real *= x
|
||||
if imag != 0:
|
||||
imag *= x
|
||||
return complex(real, imag)
|
||||
else:
|
||||
return complex(x, NAN)
|
||||
else:
|
||||
# x == -inf
|
||||
if y_is_finite:
|
||||
r = math.exp(x)
|
||||
c = math.cos(y)
|
||||
s = math.sin(y)
|
||||
return complex(r * c, r * s)
|
||||
else:
|
||||
r = 0
|
||||
return complex(r, r)
|
||||
|
||||
# @lower(cmath.log, types.Complex)
|
||||
@intrinsic_complex_unary
|
||||
def log_impl(x, y, x_is_finite, y_is_finite):
|
||||
"""cmath.log(x + y j)"""
|
||||
a = math.log(math.hypot(x, y))
|
||||
b = math.atan2(y, x)
|
||||
return complex(a, b)
|
||||
|
||||
|
||||
# @lower(cmath.log, types.Complex, types.Complex)
|
||||
def log_base_impl(context, builder, sig, args):
|
||||
"""cmath.log(z, base)"""
|
||||
[z, base] = args
|
||||
|
||||
def log_base(z, base):
|
||||
return cmath.log(z) / cmath.log(base)
|
||||
|
||||
res = context.compile_internal(builder, log_base, sig, args)
|
||||
return impl_ret_untracked(context, builder, sig, res)
|
||||
|
||||
|
||||
# @overload(cmath.log10)
|
||||
def impl_cmath_log10(z):
|
||||
if not isinstance(z, types.Complex):
|
||||
return
|
||||
|
||||
LN_10 = 2.302585092994045684
|
||||
|
||||
def log10_impl(z):
|
||||
"""cmath.log10(z)"""
|
||||
z = cmath.log(z)
|
||||
# This formula gives better results on +/-inf than cmath.log(z, 10)
|
||||
# See http://bugs.python.org/issue22544
|
||||
return complex(z.real / LN_10, z.imag / LN_10)
|
||||
|
||||
return log10_impl
|
||||
|
||||
|
||||
# @overload(cmath.phase)
|
||||
def phase_impl(x):
|
||||
"""cmath.phase(x + y j)"""
|
||||
|
||||
if not isinstance(x, types.Complex):
|
||||
return
|
||||
|
||||
def impl(x):
|
||||
return math.atan2(x.imag, x.real)
|
||||
return impl
|
||||
|
||||
|
||||
# @overload(cmath.polar)
|
||||
def polar_impl(x):
|
||||
if not isinstance(x, types.Complex):
|
||||
return
|
||||
|
||||
def impl(x):
|
||||
r, i = x.real, x.imag
|
||||
return math.hypot(r, i), math.atan2(i, r)
|
||||
return impl
|
||||
|
||||
|
||||
# @lower(cmath.sqrt, types.Complex)
|
||||
def sqrt_impl(context, builder, sig, args):
|
||||
# We risk spurious overflow for components >= FLT_MAX / (1 + sqrt(2)).
|
||||
|
||||
SQRT2 = 1.414213562373095048801688724209698079E0
|
||||
ONE_PLUS_SQRT2 = (1. + SQRT2)
|
||||
theargflt = sig.args[0].underlying_float
|
||||
# Get a type specific maximum value so scaling for overflow is based on that
|
||||
MAX = mathimpl.DBL_MAX if theargflt.bitwidth == 64 else mathimpl.FLT_MAX
|
||||
# THRES will be double precision, should not impact typing as it's just
|
||||
# used for comparison, there *may* be a few values near THRES which
|
||||
# deviate from e.g. NumPy due to rounding that occurs in the computation
|
||||
# of this value in the case of a 32bit argument.
|
||||
THRES = MAX / ONE_PLUS_SQRT2
|
||||
|
||||
def sqrt_impl(z):
|
||||
"""cmath.sqrt(z)"""
|
||||
# This is NumPy's algorithm, see npy_csqrt() in npy_math_complex.c.src
|
||||
a = z.real
|
||||
b = z.imag
|
||||
if a == 0.0 and b == 0.0:
|
||||
return complex(abs(b), b)
|
||||
if math.isinf(b):
|
||||
return complex(abs(b), b)
|
||||
if math.isnan(a):
|
||||
return complex(a, a)
|
||||
if math.isinf(a):
|
||||
if a < 0.0:
|
||||
return complex(abs(b - b), math.copysign(a, b))
|
||||
else:
|
||||
return complex(a, math.copysign(b - b, b))
|
||||
|
||||
# The remaining special case (b is NaN) is handled just fine by
|
||||
# the normal code path below.
|
||||
|
||||
# Scale to avoid overflow
|
||||
if abs(a) >= THRES or abs(b) >= THRES:
|
||||
a *= 0.25
|
||||
b *= 0.25
|
||||
scale = True
|
||||
else:
|
||||
scale = False
|
||||
# Algorithm 312, CACM vol 10, Oct 1967
|
||||
if a >= 0:
|
||||
t = math.sqrt((a + math.hypot(a, b)) * 0.5)
|
||||
real = t
|
||||
imag = b / (2 * t)
|
||||
else:
|
||||
t = math.sqrt((-a + math.hypot(a, b)) * 0.5)
|
||||
real = abs(b) / (2 * t)
|
||||
imag = math.copysign(t, b)
|
||||
# Rescale
|
||||
if scale:
|
||||
return complex(real * 2, imag)
|
||||
else:
|
||||
return complex(real, imag)
|
||||
|
||||
res = context.compile_internal(builder, sqrt_impl, sig, args)
|
||||
return impl_ret_untracked(context, builder, sig, res)
|
||||
|
||||
|
||||
# @lower(cmath.cos, types.Complex)
|
||||
def cos_impl(context, builder, sig, args):
|
||||
def cos_impl(z):
|
||||
"""cmath.cos(z) = cmath.cosh(z j)"""
|
||||
return cmath.cosh(complex(-z.imag, z.real))
|
||||
|
||||
res = context.compile_internal(builder, cos_impl, sig, args)
|
||||
return impl_ret_untracked(context, builder, sig, res)
|
||||
|
||||
# @overload(cmath.cosh)
|
||||
def impl_cmath_cosh(z):
|
||||
if not isinstance(z, types.Complex):
|
||||
return
|
||||
|
||||
def cosh_impl(z):
|
||||
"""cmath.cosh(z)"""
|
||||
x = z.real
|
||||
y = z.imag
|
||||
if math.isinf(x):
|
||||
if math.isnan(y):
|
||||
# x = +inf, y = NaN => cmath.cosh(x + y j) = inf + Nan * j
|
||||
real = abs(x)
|
||||
imag = y
|
||||
elif y == 0.0:
|
||||
# x = +inf, y = 0 => cmath.cosh(x + y j) = inf + 0j
|
||||
real = abs(x)
|
||||
imag = y
|
||||
else:
|
||||
real = math.copysign(x, math.cos(y))
|
||||
imag = math.copysign(x, math.sin(y))
|
||||
if x < 0.0:
|
||||
# x = -inf => negate imaginary part of result
|
||||
imag = -imag
|
||||
return complex(real, imag)
|
||||
return complex(math.cos(y) * math.cosh(x),
|
||||
math.sin(y) * math.sinh(x))
|
||||
return cosh_impl
|
||||
|
||||
|
||||
# @lower(cmath.sin, types.Complex)
|
||||
def sin_impl(context, builder, sig, args):
|
||||
def sin_impl(z):
|
||||
"""cmath.sin(z) = -j * cmath.sinh(z j)"""
|
||||
r = cmath.sinh(complex(-z.imag, z.real))
|
||||
return complex(r.imag, -r.real)
|
||||
|
||||
res = context.compile_internal(builder, sin_impl, sig, args)
|
||||
return impl_ret_untracked(context, builder, sig, res)
|
||||
|
||||
# @overload(cmath.sinh)
|
||||
def impl_cmath_sinh(z):
|
||||
if not isinstance(z, types.Complex):
|
||||
return
|
||||
|
||||
def sinh_impl(z):
|
||||
"""cmath.sinh(z)"""
|
||||
x = z.real
|
||||
y = z.imag
|
||||
if math.isinf(x):
|
||||
if math.isnan(y):
|
||||
# x = +/-inf, y = NaN => cmath.sinh(x + y j) = x + NaN * j
|
||||
real = x
|
||||
imag = y
|
||||
else:
|
||||
real = math.cos(y)
|
||||
imag = math.sin(y)
|
||||
if real != 0.:
|
||||
real *= x
|
||||
if imag != 0.:
|
||||
imag *= abs(x)
|
||||
return complex(real, imag)
|
||||
return complex(math.cos(y) * math.sinh(x),
|
||||
math.sin(y) * math.cosh(x))
|
||||
return sinh_impl
|
||||
|
||||
|
||||
# @lower(cmath.tan, types.Complex)
|
||||
def tan_impl(context, builder, sig, args):
|
||||
def tan_impl(z):
|
||||
"""cmath.tan(z) = -j * cmath.tanh(z j)"""
|
||||
r = cmath.tanh(complex(-z.imag, z.real))
|
||||
return complex(r.imag, -r.real)
|
||||
|
||||
res = context.compile_internal(builder, tan_impl, sig, args)
|
||||
return impl_ret_untracked(context, builder, sig, res)
|
||||
|
||||
|
||||
# @overload(cmath.tanh)
|
||||
def impl_cmath_tanh(z):
|
||||
if not isinstance(z, types.Complex):
|
||||
return
|
||||
|
||||
def tanh_impl(z):
|
||||
"""cmath.tanh(z)"""
|
||||
x = z.real
|
||||
y = z.imag
|
||||
if math.isinf(x):
|
||||
real = math.copysign(1., x)
|
||||
if math.isinf(y):
|
||||
imag = 0.
|
||||
else:
|
||||
imag = math.copysign(0., math.sin(2. * y))
|
||||
return complex(real, imag)
|
||||
# This is CPython's algorithm (see c_tanh() in cmathmodule.c).
|
||||
# XXX how to force float constants into single precision?
|
||||
tx = math.tanh(x)
|
||||
ty = math.tan(y)
|
||||
cx = 1. / math.cosh(x)
|
||||
txty = tx * ty
|
||||
denom = 1. + txty * txty
|
||||
return complex(
|
||||
tx * (1. + ty * ty) / denom,
|
||||
((ty / denom) * cx) * cx)
|
||||
|
||||
return tanh_impl
|
||||
|
||||
|
||||
# @lower(cmath.acos, types.Complex)
|
||||
def acos_impl(context, builder, sig, args):
|
||||
LN_4 = math.log(4)
|
||||
THRES = mathimpl.FLT_MAX / 4
|
||||
|
||||
def acos_impl(z):
|
||||
"""cmath.acos(z)"""
|
||||
# CPython's algorithm (see c_acos() in cmathmodule.c)
|
||||
if abs(z.real) > THRES or abs(z.imag) > THRES:
|
||||
# Avoid unnecessary overflow for large arguments
|
||||
# (also handles infinities gracefully)
|
||||
real = math.atan2(abs(z.imag), z.real)
|
||||
imag = math.copysign(
|
||||
math.log(math.hypot(z.real * 0.5, z.imag * 0.5)) + LN_4,
|
||||
-z.imag)
|
||||
return complex(real, imag)
|
||||
else:
|
||||
s1 = cmath.sqrt(complex(1. - z.real, -z.imag))
|
||||
s2 = cmath.sqrt(complex(1. + z.real, z.imag))
|
||||
real = 2. * math.atan2(s1.real, s2.real)
|
||||
imag = math.asinh(s2.real * s1.imag - s2.imag * s1.real)
|
||||
return complex(real, imag)
|
||||
|
||||
res = context.compile_internal(builder, acos_impl, sig, args)
|
||||
return impl_ret_untracked(context, builder, sig, res)
|
||||
|
||||
# @overload(cmath.acosh)
|
||||
def impl_cmath_acosh(z):
|
||||
if not isinstance(z, types.Complex):
|
||||
return
|
||||
|
||||
LN_4 = math.log(4)
|
||||
THRES = mathimpl.FLT_MAX / 4
|
||||
|
||||
def acosh_impl(z):
|
||||
"""cmath.acosh(z)"""
|
||||
# CPython's algorithm (see c_acosh() in cmathmodule.c)
|
||||
if abs(z.real) > THRES or abs(z.imag) > THRES:
|
||||
# Avoid unnecessary overflow for large arguments
|
||||
# (also handles infinities gracefully)
|
||||
real = math.log(math.hypot(z.real * 0.5, z.imag * 0.5)) + LN_4
|
||||
imag = math.atan2(z.imag, z.real)
|
||||
return complex(real, imag)
|
||||
else:
|
||||
s1 = cmath.sqrt(complex(z.real - 1., z.imag))
|
||||
s2 = cmath.sqrt(complex(z.real + 1., z.imag))
|
||||
real = math.asinh(s1.real * s2.real + s1.imag * s2.imag)
|
||||
imag = 2. * math.atan2(s1.imag, s2.real)
|
||||
return complex(real, imag)
|
||||
# Condensed formula (NumPy)
|
||||
#return cmath.log(z + cmath.sqrt(z + 1.) * cmath.sqrt(z - 1.))
|
||||
|
||||
return acosh_impl
|
||||
|
||||
|
||||
# @lower(cmath.asinh, types.Complex)
|
||||
def asinh_impl(context, builder, sig, args):
|
||||
LN_4 = math.log(4)
|
||||
THRES = mathimpl.FLT_MAX / 4
|
||||
|
||||
def asinh_impl(z):
|
||||
"""cmath.asinh(z)"""
|
||||
# CPython's algorithm (see c_asinh() in cmathmodule.c)
|
||||
if abs(z.real) > THRES or abs(z.imag) > THRES:
|
||||
real = math.copysign(
|
||||
math.log(math.hypot(z.real * 0.5, z.imag * 0.5)) + LN_4,
|
||||
z.real)
|
||||
imag = math.atan2(z.imag, abs(z.real))
|
||||
return complex(real, imag)
|
||||
else:
|
||||
s1 = cmath.sqrt(complex(1. + z.imag, -z.real))
|
||||
s2 = cmath.sqrt(complex(1. - z.imag, z.real))
|
||||
real = math.asinh(s1.real * s2.imag - s2.real * s1.imag)
|
||||
imag = math.atan2(z.imag, s1.real * s2.real - s1.imag * s2.imag)
|
||||
return complex(real, imag)
|
||||
|
||||
res = context.compile_internal(builder, asinh_impl, sig, args)
|
||||
return impl_ret_untracked(context, builder, sig, res)
|
||||
|
||||
# @lower(cmath.asin, types.Complex)
|
||||
def asin_impl(context, builder, sig, args):
|
||||
def asin_impl(z):
|
||||
"""cmath.asin(z) = -j * cmath.asinh(z j)"""
|
||||
r = cmath.asinh(complex(-z.imag, z.real))
|
||||
return complex(r.imag, -r.real)
|
||||
|
||||
res = context.compile_internal(builder, asin_impl, sig, args)
|
||||
return impl_ret_untracked(context, builder, sig, res)
|
||||
|
||||
# @lower(cmath.atan, types.Complex)
|
||||
def atan_impl(context, builder, sig, args):
|
||||
def atan_impl(z):
|
||||
"""cmath.atan(z) = -j * cmath.atanh(z j)"""
|
||||
r = cmath.atanh(complex(-z.imag, z.real))
|
||||
if math.isinf(z.real) and math.isnan(z.imag):
|
||||
# XXX this is odd but necessary
|
||||
return complex(r.imag, r.real)
|
||||
else:
|
||||
return complex(r.imag, -r.real)
|
||||
|
||||
res = context.compile_internal(builder, atan_impl, sig, args)
|
||||
return impl_ret_untracked(context, builder, sig, res)
|
||||
|
||||
# @lower(cmath.atanh, types.Complex)
|
||||
def atanh_impl(context, builder, sig, args):
|
||||
LN_4 = math.log(4)
|
||||
THRES_LARGE = math.sqrt(mathimpl.FLT_MAX / 4)
|
||||
THRES_SMALL = math.sqrt(mathimpl.FLT_MIN)
|
||||
PI_12 = math.pi / 2
|
||||
|
||||
def atanh_impl(z):
|
||||
"""cmath.atanh(z)"""
|
||||
# CPython's algorithm (see c_atanh() in cmathmodule.c)
|
||||
if z.real < 0.:
|
||||
# Reduce to case where z.real >= 0., using atanh(z) = -atanh(-z).
|
||||
negate = True
|
||||
z = -z
|
||||
else:
|
||||
negate = False
|
||||
|
||||
ay = abs(z.imag)
|
||||
if math.isnan(z.real) or z.real > THRES_LARGE or ay > THRES_LARGE:
|
||||
if math.isinf(z.imag):
|
||||
real = math.copysign(0., z.real)
|
||||
elif math.isinf(z.real):
|
||||
real = 0.
|
||||
else:
|
||||
# may be safe from overflow, depending on hypot's implementation...
|
||||
h = math.hypot(z.real * 0.5, z.imag * 0.5)
|
||||
real = z.real/4./h/h
|
||||
imag = -math.copysign(PI_12, -z.imag)
|
||||
elif z.real == 1. and ay < THRES_SMALL:
|
||||
# C99 standard says: atanh(1+/-0.) should be inf +/- 0j
|
||||
if ay == 0.:
|
||||
real = INF
|
||||
imag = z.imag
|
||||
else:
|
||||
real = -math.log(math.sqrt(ay) /
|
||||
math.sqrt(math.hypot(ay, 2.)))
|
||||
imag = math.copysign(math.atan2(2., -ay) / 2, z.imag)
|
||||
else:
|
||||
sqay = ay * ay
|
||||
zr1 = 1 - z.real
|
||||
real = math.log1p(4. * z.real / (zr1 * zr1 + sqay)) * 0.25
|
||||
imag = -math.atan2(-2. * z.imag,
|
||||
zr1 * (1 + z.real) - sqay) * 0.5
|
||||
|
||||
if math.isnan(z.imag):
|
||||
imag = NAN
|
||||
if negate:
|
||||
return complex(-real, -imag)
|
||||
else:
|
||||
return complex(real, imag)
|
||||
|
||||
res = context.compile_internal(builder, atanh_impl, sig, args)
|
||||
return impl_ret_untracked(context, builder, sig, res)
|
||||
@@ -0,0 +1,454 @@
|
||||
"""
|
||||
Provide math calls that uses intrinsics or libc math functions.
|
||||
"""
|
||||
|
||||
import math
|
||||
import operator
|
||||
import sys
|
||||
import numpy as np
|
||||
|
||||
import llvmlite.ir
|
||||
from llvmlite.ir import Constant
|
||||
|
||||
from numba.core.imputils import impl_ret_untracked
|
||||
from numba.core import types, config, cgutils
|
||||
from numba.core.extending import overload
|
||||
from numba.core.typing import signature
|
||||
from numba.cpython.unsafe.numbers import trailing_zeros
|
||||
|
||||
|
||||
# registry = Registry('mathimpl')
|
||||
# lower = registry.lower
|
||||
|
||||
|
||||
# Helpers, shared with cmathimpl.
|
||||
_NP_FLT_FINFO = np.finfo(np.dtype('float32'))
|
||||
FLT_MAX = _NP_FLT_FINFO.max
|
||||
FLT_MIN = _NP_FLT_FINFO.tiny
|
||||
|
||||
_NP_DBL_FINFO = np.finfo(np.dtype('float64'))
|
||||
DBL_MAX = _NP_DBL_FINFO.max
|
||||
DBL_MIN = _NP_DBL_FINFO.tiny
|
||||
|
||||
FLOAT_ABS_MASK = 0x7fffffff
|
||||
FLOAT_SIGN_MASK = 0x80000000
|
||||
DOUBLE_ABS_MASK = 0x7fffffffffffffff
|
||||
DOUBLE_SIGN_MASK = 0x8000000000000000
|
||||
|
||||
|
||||
def is_nan(builder, val):
|
||||
"""
|
||||
Return a condition testing whether *val* is a NaN.
|
||||
"""
|
||||
return builder.fcmp_unordered('uno', val, val)
|
||||
|
||||
def is_inf(builder, val):
|
||||
"""
|
||||
Return a condition testing whether *val* is an infinite.
|
||||
"""
|
||||
pos_inf = Constant(val.type, float("+inf"))
|
||||
neg_inf = Constant(val.type, float("-inf"))
|
||||
isposinf = builder.fcmp_ordered('==', val, pos_inf)
|
||||
isneginf = builder.fcmp_ordered('==', val, neg_inf)
|
||||
return builder.or_(isposinf, isneginf)
|
||||
|
||||
def is_finite(builder, val):
|
||||
"""
|
||||
Return a condition testing whether *val* is a finite.
|
||||
"""
|
||||
# is_finite(x) <=> x - x != NaN
|
||||
val_minus_val = builder.fsub(val, val)
|
||||
return builder.fcmp_ordered('ord', val_minus_val, val_minus_val)
|
||||
|
||||
def f64_as_int64(builder, val):
|
||||
"""
|
||||
Bitcast a double into a 64-bit integer.
|
||||
"""
|
||||
assert val.type == llvmlite.ir.DoubleType()
|
||||
return builder.bitcast(val, llvmlite.ir.IntType(64))
|
||||
|
||||
def int64_as_f64(builder, val):
|
||||
"""
|
||||
Bitcast a 64-bit integer into a double.
|
||||
"""
|
||||
assert val.type == llvmlite.ir.IntType(64)
|
||||
return builder.bitcast(val, llvmlite.ir.DoubleType())
|
||||
|
||||
def f32_as_int32(builder, val):
|
||||
"""
|
||||
Bitcast a float into a 32-bit integer.
|
||||
"""
|
||||
assert val.type == llvmlite.ir.FloatType()
|
||||
return builder.bitcast(val, llvmlite.ir.IntType(32))
|
||||
|
||||
def int32_as_f32(builder, val):
|
||||
"""
|
||||
Bitcast a 32-bit integer into a float.
|
||||
"""
|
||||
assert val.type == llvmlite.ir.IntType(32)
|
||||
return builder.bitcast(val, llvmlite.ir.FloatType())
|
||||
|
||||
def negate_real(builder, val):
|
||||
"""
|
||||
Negate real number *val*, with proper handling of zeros.
|
||||
"""
|
||||
# The negative zero forces LLVM to handle signed zeros properly.
|
||||
return builder.fsub(Constant(val.type, -0.0), val)
|
||||
|
||||
def call_fp_intrinsic(builder, name, args):
|
||||
"""
|
||||
Call a LLVM intrinsic floating-point operation.
|
||||
"""
|
||||
mod = builder.module
|
||||
intr = mod.declare_intrinsic(name, [a.type for a in args])
|
||||
return builder.call(intr, args)
|
||||
|
||||
|
||||
def _unary_int_input_wrapper_impl(wrapped_impl):
|
||||
"""
|
||||
Return an implementation factory to convert the single integral input
|
||||
argument to a float64, then defer to the *wrapped_impl*.
|
||||
"""
|
||||
def implementer(context, builder, sig, args):
|
||||
val, = args
|
||||
input_type = sig.args[0]
|
||||
fpval = context.cast(builder, val, input_type, types.float64)
|
||||
inner_sig = signature(types.float64, types.float64)
|
||||
res = wrapped_impl(context, builder, inner_sig, (fpval,))
|
||||
return context.cast(builder, res, types.float64, sig.return_type)
|
||||
|
||||
return implementer
|
||||
|
||||
def unary_math_int_impl(fn, float_impl):
|
||||
impl = _unary_int_input_wrapper_impl(float_impl)
|
||||
# lower(fn, types.Integer)(impl)
|
||||
|
||||
def unary_math_intr(fn, intrcode):
|
||||
"""
|
||||
Implement the math function *fn* using the LLVM intrinsic *intrcode*.
|
||||
"""
|
||||
# @lower(fn, types.Float)
|
||||
def float_impl(context, builder, sig, args):
|
||||
res = call_fp_intrinsic(builder, intrcode, args)
|
||||
return impl_ret_untracked(context, builder, sig.return_type, res)
|
||||
|
||||
unary_math_int_impl(fn, float_impl)
|
||||
return float_impl
|
||||
|
||||
def unary_math_extern(fn, f32extern, f64extern, int_restype=False):
|
||||
"""
|
||||
Register implementations of Python function *fn* using the
|
||||
external function named *f32extern* and *f64extern* (for float32
|
||||
and float64 inputs, respectively).
|
||||
If *int_restype* is true, then the function's return value should be
|
||||
integral, otherwise floating-point.
|
||||
"""
|
||||
f_restype = types.int64 if int_restype else None
|
||||
|
||||
def float_impl(context, builder, sig, args):
|
||||
"""
|
||||
Implement *fn* for a types.Float input.
|
||||
"""
|
||||
[val] = args
|
||||
mod = builder.module
|
||||
input_type = sig.args[0]
|
||||
lty = context.get_value_type(input_type)
|
||||
func_name = {
|
||||
types.float32: f32extern,
|
||||
types.float64: f64extern,
|
||||
}[input_type]
|
||||
fnty = llvmlite.ir.FunctionType(lty, [lty])
|
||||
fn = cgutils.insert_pure_function(builder.module, fnty, name=func_name)
|
||||
res = builder.call(fn, (val,))
|
||||
res = context.cast(builder, res, input_type, sig.return_type)
|
||||
return impl_ret_untracked(context, builder, sig.return_type, res)
|
||||
|
||||
# lower(fn, types.Float)(float_impl)
|
||||
|
||||
# Implement wrapper for integer inputs
|
||||
unary_math_int_impl(fn, float_impl)
|
||||
|
||||
return float_impl
|
||||
|
||||
|
||||
unary_math_intr(math.fabs, 'llvm.fabs')
|
||||
exp_impl = unary_math_intr(math.exp, 'llvm.exp')
|
||||
if sys.version_info >= (3, 11):
|
||||
exp2_impl = unary_math_intr(math.exp2, 'llvm.exp2')
|
||||
log_impl = unary_math_intr(math.log, 'llvm.log')
|
||||
log10_impl = unary_math_intr(math.log10, 'llvm.log10')
|
||||
sin_impl = unary_math_intr(math.sin, 'llvm.sin')
|
||||
cos_impl = unary_math_intr(math.cos, 'llvm.cos')
|
||||
|
||||
log1p_impl = unary_math_extern(math.log1p, "log1pf", "log1p")
|
||||
expm1_impl = unary_math_extern(math.expm1, "expm1f", "expm1")
|
||||
erf_impl = unary_math_extern(math.erf, "erff", "erf")
|
||||
erfc_impl = unary_math_extern(math.erfc, "erfcf", "erfc")
|
||||
|
||||
tan_impl = unary_math_extern(math.tan, "tanf", "tan")
|
||||
asin_impl = unary_math_extern(math.asin, "asinf", "asin")
|
||||
acos_impl = unary_math_extern(math.acos, "acosf", "acos")
|
||||
atan_impl = unary_math_extern(math.atan, "atanf", "atan")
|
||||
|
||||
asinh_impl = unary_math_extern(math.asinh, "asinhf", "asinh")
|
||||
acosh_impl = unary_math_extern(math.acosh, "acoshf", "acosh")
|
||||
atanh_impl = unary_math_extern(math.atanh, "atanhf", "atanh")
|
||||
sinh_impl = unary_math_extern(math.sinh, "sinhf", "sinh")
|
||||
cosh_impl = unary_math_extern(math.cosh, "coshf", "cosh")
|
||||
tanh_impl = unary_math_extern(math.tanh, "tanhf", "tanh")
|
||||
|
||||
log2_impl = unary_math_extern(math.log2, "log2f", "log2")
|
||||
ceil_impl = unary_math_extern(math.ceil, "ceilf", "ceil", True)
|
||||
floor_impl = unary_math_extern(math.floor, "floorf", "floor", True)
|
||||
|
||||
gamma_impl = unary_math_extern(math.gamma, "numba_gammaf", "numba_gamma") # work-around
|
||||
sqrt_impl = unary_math_extern(math.sqrt, "sqrtf", "sqrt")
|
||||
trunc_impl = unary_math_extern(math.trunc, "truncf", "trunc", True)
|
||||
lgamma_impl = unary_math_extern(math.lgamma, "lgammaf", "lgamma")
|
||||
|
||||
|
||||
# @lower(math.isnan, types.Float)
|
||||
def isnan_float_impl(context, builder, sig, args):
|
||||
[val] = args
|
||||
res = is_nan(builder, val)
|
||||
return impl_ret_untracked(context, builder, sig.return_type, res)
|
||||
|
||||
# @lower(math.isnan, types.Integer)
|
||||
def isnan_int_impl(context, builder, sig, args):
|
||||
res = cgutils.false_bit
|
||||
return impl_ret_untracked(context, builder, sig.return_type, res)
|
||||
|
||||
|
||||
# @lower(math.isinf, types.Float)
|
||||
def isinf_float_impl(context, builder, sig, args):
|
||||
[val] = args
|
||||
res = is_inf(builder, val)
|
||||
return impl_ret_untracked(context, builder, sig.return_type, res)
|
||||
|
||||
# @lower(math.isinf, types.Integer)
|
||||
def isinf_int_impl(context, builder, sig, args):
|
||||
res = cgutils.false_bit
|
||||
return impl_ret_untracked(context, builder, sig.return_type, res)
|
||||
|
||||
|
||||
# @lower(math.isfinite, types.Float)
|
||||
def isfinite_float_impl(context, builder, sig, args):
|
||||
[val] = args
|
||||
res = is_finite(builder, val)
|
||||
return impl_ret_untracked(context, builder, sig.return_type, res)
|
||||
|
||||
|
||||
# @lower(math.isfinite, types.Integer)
|
||||
def isfinite_int_impl(context, builder, sig, args):
|
||||
res = cgutils.true_bit
|
||||
return impl_ret_untracked(context, builder, sig.return_type, res)
|
||||
|
||||
|
||||
# @lower(math.copysign, types.Float, types.Float)
|
||||
def copysign_float_impl(context, builder, sig, args):
|
||||
lty = args[0].type
|
||||
mod = builder.module
|
||||
fn = cgutils.get_or_insert_function(mod, llvmlite.ir.FunctionType(lty, (lty, lty)),
|
||||
'llvm.copysign.%s' % lty.intrinsic_name)
|
||||
res = builder.call(fn, args)
|
||||
return impl_ret_untracked(context, builder, sig.return_type, res)
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
|
||||
# @lower(math.frexp, types.Float)
|
||||
def frexp_impl(context, builder, sig, args):
|
||||
val, = args
|
||||
fltty = context.get_data_type(sig.args[0])
|
||||
intty = context.get_data_type(sig.return_type[1])
|
||||
expptr = cgutils.alloca_once(builder, intty, name='exp')
|
||||
fnty = llvmlite.ir.FunctionType(fltty, (fltty, llvmlite.ir.PointerType(intty)))
|
||||
fname = {
|
||||
"float": "numba_frexpf",
|
||||
"double": "numba_frexp",
|
||||
}[str(fltty)]
|
||||
fn = cgutils.get_or_insert_function(builder.module, fnty, fname)
|
||||
res = builder.call(fn, (val, expptr))
|
||||
res = cgutils.make_anonymous_struct(builder, (res, builder.load(expptr)))
|
||||
return impl_ret_untracked(context, builder, sig.return_type, res)
|
||||
|
||||
|
||||
# @lower(math.ldexp, types.Float, types.intc)
|
||||
def ldexp_impl(context, builder, sig, args):
|
||||
val, exp = args
|
||||
fltty, intty = map(context.get_data_type, sig.args)
|
||||
fnty = llvmlite.ir.FunctionType(fltty, (fltty, intty))
|
||||
fname = {
|
||||
"float": "numba_ldexpf",
|
||||
"double": "numba_ldexp",
|
||||
}[str(fltty)]
|
||||
fn = cgutils.insert_pure_function(builder.module, fnty, name=fname)
|
||||
res = builder.call(fn, (val, exp))
|
||||
return impl_ret_untracked(context, builder, sig.return_type, res)
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
|
||||
# @lower(math.atan2, types.int64, types.int64)
|
||||
def atan2_s64_impl(context, builder, sig, args):
|
||||
[y, x] = args
|
||||
y = builder.sitofp(y, llvmlite.ir.DoubleType())
|
||||
x = builder.sitofp(x, llvmlite.ir.DoubleType())
|
||||
fsig = signature(types.float64, types.float64, types.float64)
|
||||
return atan2_float_impl(context, builder, fsig, (y, x))
|
||||
|
||||
# @lower(math.atan2, types.uint64, types.uint64)
|
||||
def atan2_u64_impl(context, builder, sig, args):
|
||||
[y, x] = args
|
||||
y = builder.uitofp(y, llvmlite.ir.DoubleType())
|
||||
x = builder.uitofp(x, llvmlite.ir.DoubleType())
|
||||
fsig = signature(types.float64, types.float64, types.float64)
|
||||
return atan2_float_impl(context, builder, fsig, (y, x))
|
||||
|
||||
# @lower(math.atan2, types.Float, types.Float)
|
||||
def atan2_float_impl(context, builder, sig, args):
|
||||
assert len(args) == 2
|
||||
mod = builder.module
|
||||
ty = sig.args[0]
|
||||
lty = context.get_value_type(ty)
|
||||
func_name = {
|
||||
types.float32: "atan2f",
|
||||
types.float64: "atan2"
|
||||
}[ty]
|
||||
fnty = llvmlite.ir.FunctionType(lty, (lty, lty))
|
||||
fn = cgutils.insert_pure_function(builder.module, fnty, name=func_name)
|
||||
res = builder.call(fn, args)
|
||||
return impl_ret_untracked(context, builder, sig.return_type, res)
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
|
||||
# @lower(math.hypot, types.int64, types.int64)
|
||||
def hypot_s64_impl(context, builder, sig, args):
|
||||
[x, y] = args
|
||||
y = builder.sitofp(y, llvmlite.ir.DoubleType())
|
||||
x = builder.sitofp(x, llvmlite.ir.DoubleType())
|
||||
fsig = signature(types.float64, types.float64, types.float64)
|
||||
res = hypot_float_impl(context, builder, fsig, (x, y))
|
||||
return impl_ret_untracked(context, builder, sig.return_type, res)
|
||||
|
||||
|
||||
# @lower(math.hypot, types.uint64, types.uint64)
|
||||
def hypot_u64_impl(context, builder, sig, args):
|
||||
[x, y] = args
|
||||
y = builder.sitofp(y, llvmlite.ir.DoubleType())
|
||||
x = builder.sitofp(x, llvmlite.ir.DoubleType())
|
||||
fsig = signature(types.float64, types.float64, types.float64)
|
||||
res = hypot_float_impl(context, builder, fsig, (x, y))
|
||||
return impl_ret_untracked(context, builder, sig.return_type, res)
|
||||
|
||||
|
||||
# @lower(math.hypot, types.Float, types.Float)
|
||||
def hypot_float_impl(context, builder, sig, args):
|
||||
xty, yty = sig.args
|
||||
assert xty == yty == sig.return_type
|
||||
x, y = args
|
||||
|
||||
# Windows has alternate names for hypot/hypotf, see
|
||||
# https://msdn.microsoft.com/fr-fr/library/a9yb3dbt%28v=vs.80%29.aspx
|
||||
fname = {
|
||||
types.float32: "_hypotf" if sys.platform == 'win32' else "hypotf",
|
||||
types.float64: "_hypot" if sys.platform == 'win32' else "hypot",
|
||||
}[xty]
|
||||
plat_hypot = types.ExternalFunction(fname, sig)
|
||||
|
||||
if sys.platform == 'win32' and config.MACHINE_BITS == 32:
|
||||
inf = xty(float('inf'))
|
||||
|
||||
def hypot_impl(x, y):
|
||||
if math.isinf(x) or math.isinf(y):
|
||||
return inf
|
||||
return plat_hypot(x, y)
|
||||
else:
|
||||
def hypot_impl(x, y):
|
||||
return plat_hypot(x, y)
|
||||
|
||||
res = context.compile_internal(builder, hypot_impl, sig, args)
|
||||
return impl_ret_untracked(context, builder, sig.return_type, res)
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
# @lower(math.radians, types.Float)
|
||||
def radians_float_impl(context, builder, sig, args):
|
||||
[x] = args
|
||||
coef = context.get_constant(sig.return_type, math.pi / 180)
|
||||
res = builder.fmul(x, coef)
|
||||
return impl_ret_untracked(context, builder, sig.return_type, res)
|
||||
|
||||
unary_math_int_impl(math.radians, radians_float_impl)
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
# @lower(math.degrees, types.Float)
|
||||
def degrees_float_impl(context, builder, sig, args):
|
||||
[x] = args
|
||||
coef = context.get_constant(sig.return_type, 180 / math.pi)
|
||||
res = builder.fmul(x, coef)
|
||||
return impl_ret_untracked(context, builder, sig.return_type, res)
|
||||
|
||||
unary_math_int_impl(math.degrees, degrees_float_impl)
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
# @lower(math.pow, types.Float, types.Float)
|
||||
# @lower(math.pow, types.Float, types.Integer)
|
||||
def pow_impl(context, builder, sig, args):
|
||||
impl = context.get_function(operator.pow, sig)
|
||||
return impl(builder, args)
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
|
||||
def _unsigned(T):
|
||||
"""Convert integer to unsigned integer of equivalent width."""
|
||||
pass
|
||||
|
||||
@overload(_unsigned)
|
||||
def _unsigned_impl(T):
|
||||
if T in types.unsigned_domain:
|
||||
return lambda T: T
|
||||
elif T in types.signed_domain:
|
||||
newT = getattr(types, 'uint{}'.format(T.bitwidth))
|
||||
return lambda T: newT(T)
|
||||
|
||||
|
||||
def gcd_impl(context, builder, sig, args):
|
||||
xty, yty = sig.args
|
||||
assert xty == yty == sig.return_type
|
||||
x, y = args
|
||||
|
||||
def gcd(a, b):
|
||||
"""
|
||||
Stein's algorithm, heavily cribbed from Julia implementation.
|
||||
"""
|
||||
T = type(a)
|
||||
if a == 0: return abs(b)
|
||||
if b == 0: return abs(a)
|
||||
za = trailing_zeros(a)
|
||||
zb = trailing_zeros(b)
|
||||
k = min(za, zb)
|
||||
# Uses np.*_shift instead of operators due to return types
|
||||
u = _unsigned(abs(np.right_shift(a, za)))
|
||||
v = _unsigned(abs(np.right_shift(b, zb)))
|
||||
while u != v:
|
||||
if u > v:
|
||||
u, v = v, u
|
||||
v -= u
|
||||
v = np.right_shift(v, trailing_zeros(v))
|
||||
r = np.left_shift(T(u), k)
|
||||
return r
|
||||
|
||||
res = context.compile_internal(builder, gcd, sig, args)
|
||||
return impl_ret_untracked(context, builder, sig.return_type, res)
|
||||
|
||||
|
||||
# lower(math.gcd, types.Integer, types.Integer)(gcd_impl)
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user