pyodide / pyodide/pyodide

Incorrect results when using complex dtype in numpy

Open
#159 3 comments 0 reactions 0 assignees View on GitHub
bug numpy
Dominant language
Python
Stars
14.8k
Forks
1k
Avg merge
20h 29m
Merged PRs (30d)
19

Description

Some of the calculations that use complex numbers in numpy produce results that are incorrect, which makes the corresponding numpy tests fail.

```
_________________________ TestComplexFunctions.test_it _________________________

self =

def test_it(self):
for f in self.funcs:
if f is np.arccosh:
x = 1.5
else:
x = .5
fr = f(x)
fz = f(complex(x))
> assert_almost_equal(fz.real, fr, err_msg='real part %s' % f)
E AssertionError:
E Arrays are not almost equal to 7 decimals real part
E ACTUAL: -1.1102230246251565e-16
E DESIRED: 0.5235987755982989

lib/python3.7/site-packages/numpy/core/tests/test_umath.py:2424: AssertionError
____________________ TestComplexFunctions.test_branch_cuts _____________________

self =

def test_branch_cuts(self):
# check branch cuts and continuity on them
_check_branch_cut(np.log, -0.5, 1j, 1, -1, True)
_check_branch_cut(np.log2, -0.5, 1j, 1, -1, True)
_check_branch_cut(np.log10, -0.5, 1j, 1, -1, True)
_check_branch_cut(np.log1p, -1.5, 1j, 1, -1, True)
_check_branch_cut(np.sqrt, -0.5, 1j, 1, -1, True)

> _check_branch_cut(np.arcsin, [ -2, 2], [1j, 1j], 1, -1, True)

lib/python3.7/site-packages/numpy/core/tests/test_umath.py:2444:
f = , x0 = array([-2.+0.j, 2.+0.j])
dx = array([0.+1.j, 0.+1.j]), re_sign = 1, im_sign = -1, sig_zero_ok = True
dtype =

def _check_branch_cut(f, x0, dx, re_sign=1, im_sign=-1, sig_zero_ok=False,
dtype=complex):
"""
Check for a branch cut in a function.

Assert that `x0` lies on a branch cut of function `f` and `f` is
continuous from the direction `dx`.

Parameters
----------
f : func
Function to check
x0 : array-like
Point on branch cut
dx : array-like
Direction to check continuity in
re_sign, im_sign : {1, -1}
Change of sign of the real or imaginary part expected
sig_zero_ok : bool
Whether to check if the branch cut respects signed zero (if applicable)
dtype : dtype
Dtype to check (should be complex)

"""
x0 = np.atleast_1d(x0).astype(dtype)
dx = np.atleast_1d(dx).astype(dtype)
if np.dtype(dtype).char == 'F':
scale = np.finfo(dtype).eps * 1e2
atol = np.float32(1e-2)
else:
scale = np.finfo(dtype).eps * 1e3
atol = 1e-4

y0 = f(x0)
yp = f(x0 + dx*scale*np.absolute(x0)/np.absolute(dx))
ym = f(x0 - dx*scale*np.absolute(x0)/np.absolute(dx))

assert_(np.all(np.absolute(y0.real - yp.real) < atol), (y0, yp))
assert_(np.all(np.absolute(y0.imag - yp.imag) < atol), (y0, yp))
> assert_(np.all(np.absolute(y0.real - ym.real*re_sign) < atol), (y0, ym))
E AssertionError: (array([-1.3169579-1.57079633j, -1.3169579+1.57079633j]), array([1.3169579-1.57079633j, 1.3169579+1.57079633j]))

lib/python3.7/site-packages/numpy/core/tests/test_umath.py:2672: AssertionError
_______________ TestComplexFunctions.test_branch_cuts_complex64 ________________

self =

def test_branch_cuts_complex64(self):
# check branch cuts and continuity on them
_check_branch_cut(np.log, -0.5, 1j, 1, -1, True, np.complex64)
_check_branch_cut(np.log2, -0.5, 1j, 1, -1, True, np.complex64)
_check_branch_cut(np.log10, -0.5, 1j, 1, -1, True, np.complex64)
_check_branch_cut(np.log1p, -1.5, 1j, 1, -1, True, np.complex64)
_check_branch_cut(np.sqrt, -0.5, 1j, 1, -1, True, np.complex64)
> _check_branch_cut(np.arcsin, [ -2, 2], [1j, 1j], 1, -1, True, np.complex64)

lib/python3.7/site-packages/numpy/core/tests/test_umath.py:2469:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

f = , x0 = array([-2.+0.j, 2.+0.j], dtype=complex64)
dx = array([0.+1.j, 0.+1.j], dtype=complex64), re_sign = 1, im_sign = -1
sig_zero_ok = True, dtype =

def _check_branch_cut(f, x0, dx, re_sign=1, im_sign=-1, sig_zero_ok=False,
dtype=complex):
"""
Check for a branch cut in a function.

Assert that `x0` lies on a branch cut of function `f` and `f` is
continuous from the direction `dx`.

Parameters
----------
f : func
Function to check
x0 : array-like
Point on branch cut
dx : array-like
Direction to check continuity in
re_sign, im_sign : {1, -1}
Change of sign of the real or imaginary part expected
sig_zero_ok : bool
Whether to check if the branch cut respects signed zero (if applicable)
dtype : dtype
Dtype to check (should be complex)

"""
x0 = np.atleast_1d(x0).astype(dtype)
dx = np.atleast_1d(dx).astype(dtype)
dx = np.atleast_1d(dx).astype(dtype)

if np.dtype(dtype).char == 'F':
scale = np.finfo(dtype).eps * 1e2
atol = np.float32(1e-2)
else:
scale = np.finfo(dtype).eps * 1e3
atol = 1e-4

y0 = f(x0)
yp = f(x0 + dx*scale*np.absolute(x0)/np.absolute(dx))
ym = f(x0 - dx*scale*np.absolute(x0)/np.absolute(dx))

assert_(np.all(np.absolute(y0.real - yp.real) < atol), (y0, yp))
assert_(np.all(np.absolute(y0.imag - yp.imag) < atol), (y0, yp))
> assert_(np.all(np.absolute(y0.real - ym.real*re_sign) < atol), (y0, ym))
E AssertionError: (array([-1.3169578-1.5707964j, -1.3169578+1.5707964j], dtype=complex64), array([1.316958-1.5707825j, 1.316958+1.5707825j], dtype=comp
lex64))
___________________ TestComplexFunctions.test_against_cmath ____________________

self =

def test_against_cmath(self):
import cmath

points = [-1-1j, -1+1j, +1-1j, +1+1j]
name_map = {'arcsin': 'asin', 'arccos': 'acos', 'arctan': 'atan',
'arcsinh': 'asinh', 'arccosh': 'acosh', 'arctanh': 'atanh'}
atol = 4*np.finfo(complex).eps
for func in self.funcs:
fname = func.__name__.split('.')[-1]
cname = name_map.get(fname, fname)
try:
cfunc = getattr(cmath, cname)
except AttributeError:
continue
for p in points:
a = complex(func(np.complex_(p)))
b = cfunc(p)
> assert_(abs(a - b) < atol, "%s %s: %s; cmath: %s" % (fname, p, a, b))
E AssertionError: arcsin (-1-1j): (1.0612750619050357-0.6662394324925153j); cmath: (-0.6662394324925153-1.0612750619050357j)

lib/python3.7/site-packages/numpy/core/tests/test_umath.py:2503: AssertionError
_________________ TestComplexFunctions.test_loss_of_precision __________________

self =

def test_loss_of_precision(self):
for dtype in [np.complex64, np.complex_]:
> self.check_loss_of_precision(dtype)

lib/python3.7/site-packages/numpy/core/tests/test_umath.py:2592:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
lib/python3.7/site-packages/numpy/core/tests/test_umath.py:2549: in check_loss_of_precision
check(x_series, 2.1*eps)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

x = array([9.99999968e-21, 1.21736865e-20, 1.48198648e-20, 1.80412373e-20,
2.19628376e-20, 2.67368701e-20, 3.254862...55170e-04, 4.54267400e-04,
5.53010846e-04, 6.73218106e-04, 8.19554611e-04, 9.97700030e-04],
dtype=float32)
rtol = 2.5033950805664064e-07

def check(x, rtol):
x = x.astype(real_dtype)

z = x.astype(dtype)
d = np.absolute(np.arcsinh(x)/np.arcsinh(z).real - 1)
assert_(np.all(d < rtol), (np.argmax(d), x[np.argmax(d)], d.max(),
> 'arcsinh'))
E AssertionError: (0, 1e-20, inf, 'arcsinh')

lib/python3.7/site-packages/numpy/core/tests/test_umath.py:2520: AssertionError
```

A number of other tests that use complex dtypes pass however.

Full test logs can be found in https://github.com/iodide-project/pyodide/issues/69#issuecomment-420616635
Tested with Python 3.7 and numpy 1.15.1.

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.