Skip to content

Fix dpnp.tensor.acosh handling of complex(+-0, NaN)#2914

Open
vlad-perevezentsev wants to merge 6 commits into
masterfrom
fix_acosh_nan
Open

Fix dpnp.tensor.acosh handling of complex(+-0, NaN)#2914
vlad-perevezentsev wants to merge 6 commits into
masterfrom
fix_acosh_nan

Conversation

@vlad-perevezentsev
Copy link
Copy Markdown
Contributor

@vlad-perevezentsev vlad-perevezentsev commented May 13, 2026

This PR proposes to fix issue #2880 where dpnp.tensor.acosh(complex(0, NaN)) returned NaN + NaN j instead of NaN ± π/2 j as required by the Array API specification

The issue was caused by an incorrect early-return branch in acosh.hpp:

if (std::isnan(ry)) {
    return resT{ry, ry};
}

This special case bypassed the general logic which already produces the correct result.

The fix removes this unnecessary branch making the behavior consistent with both the Array API specification and NumPy

In [1]: import dpnp.tensor as dpt

In [2]: import numpy as np

In [3]: a = dpt.asarray([complex(+0, np.nan)])

In [4]: dpt.acosh(a)
Out[4]: usm_ndarray([nan+1.57079633j])

In [5]: a = np.asarray([complex(+0, np.nan)])

In [6]: np.acosh(a)
Out[6]: array([nan+1.57079633j])

Also adds a new test_acosh_zero_nan test to cover this case

  • Have you provided a meaningful PR description?
  • Have you added a test, reproducer or referred to an issue with a reproducer?
  • Have you tested your changes locally for CPU and GPU devices?
  • Have you made sure that new changes do not introduce compiler warnings?
  • Have you checked performance impact of proposed changes?
  • Have you added documentation for your changes, if necessary?
  • Have you added your changes to the changelog?

@vlad-perevezentsev vlad-perevezentsev self-assigned this May 13, 2026
@vlad-perevezentsev vlad-perevezentsev linked an issue May 13, 2026 that may be closed by this pull request
@github-actions
Copy link
Copy Markdown
Contributor

View rendered docs @ https://intelpython.github.io/dpnp/pull/2914/index.html

@coveralls
Copy link
Copy Markdown
Collaborator

coveralls commented May 13, 2026

Coverage Status

coverage: 78.441% (-0.003%) from 78.444% — fix_acosh_nan into master

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 13, 2026

Array API standard conformance tests for dpnp=0.21.0dev0=py313h509198e_23 ran successfully.
Passed: 1357
Failed: 3
Skipped: 16

@antonwolfy antonwolfy added this to the 0.21.0 release milestone May 13, 2026
Comment thread dpnp/tests/tensor/elementwise/test_hyperbolic.py
Comment thread dpnp/tests/tensor/elementwise/test_hyperbolic.py Outdated
Comment thread dpnp/tests/tensor/elementwise/test_hyperbolic.py Outdated
Comment thread dpnp/tests/tensor/elementwise/test_hyperbolic.py Outdated

Y_dpt = dpt.asnumpy(dpt.acosh(yf))

for i in range(len(x)):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we need to place a comment that NumPy returns wrong result: NaN + NaN j
and probably to add a link to the issue in NumPy repo

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

conda-forge numpy gives the nan, nan result, while wheels give the 0, nan result, because of something to do with numpy dispatching

numpy/numpy#29167 (comment)

probably, it would be best to just not use numpy for testing these kinds of edge case results

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I have noticed that too
It is better not to compare it to NumPy at all
Thank you!

Copy link
Copy Markdown
Contributor

@antonwolfy antonwolfy May 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But both cases seem invalid, we expect NaN ± π/2 j based on the spec. Should we report then?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But both cases seem invalid, we expect NaN ± π/2 j based on the spec. Should we report then?

PyPI isn't incorrect

In [1]: import numpy as np

In [2]: vals = [complex(np.nan, -np.inf), complex(-np.inf, np.nan), complex(0, np.nan)]

In [3]: x = np.array(vals)

In [4]: np.arcsinh(x[0]), np.arcsin(x[1]), np.arccosh(x[2])
Out[4]:
(np.complex128(inf+nanj),
 np.complex128(nan+infj),
 np.complex128(nan+1.5707963267948966j))

the last case is NaN + pi/2, it's only conda-forge which is incorrect.

I don't think we shouldn't file, just noting that, they are transiently aware of it and it's platform-dependent for NumPy failure

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, I thought the same faulty result for PyPI.

Comment thread dpnp/tests/tensor/elementwise/test_hyperbolic.py Outdated
Comment on lines +220 to +222
pi_half = np.full(len(x), np.pi / 2, dtype=xf.real.dtype)
assert np.all(np.isnan(Y_dpt.real))
assert_allclose(abs(Y_dpt.imag), pi_half, atol=1e-6)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess it might be simplified:

Suggested change
pi_half = np.full(len(x), np.pi / 2, dtype=xf.real.dtype)
assert np.all(np.isnan(Y_dpt.real))
assert_allclose(abs(Y_dpt.imag), pi_half, atol=1e-6)
assert np.isnan(Y_dpt.real).all()
assert_allclose(np.abs(Y_dpt.imag), np.pi / 2, atol=1e-6)

Copy link
Copy Markdown
Contributor

@antonwolfy antonwolfy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only minor nit in the tests. The remaining is absolutely LGTM!
Thank you @vlad-perevezentsev

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

acosh returns wrong result for 0+nanj

4 participants