-
-
Notifications
You must be signed in to change notification settings - Fork 10.8k
API,ENH: Change definition of complex sign #25441
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
03aeeb2
to
37ee450
Compare
This needs a release note. |
And maybe a note on the mailing list. |
37ee450
to
3ecb450
Compare
@charris - oops, had forgotten to include the changelog entry, now corrected ( |
|
||
log_start = _nx.log10(start) | ||
log_stop = _nx.log10(stop) | ||
result = logspace(log_start, log_stop, num=num, | ||
endpoint=endpoint, base=10.0, dtype=dtype) | ||
endpoint=endpoint, base=10.0, dtype=dt) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
note to reviewers: this was a small bug exposed by changing to an in-place multiplication with out_sign
below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As far as I'm able to review it, looks good to me! Could we have a small additional test_copysign_complex()
next to test_sign_complex()
?
I saw your email to the list and spent some time trying to find open source usages of It's not really something that this would break, but I did find that numba explicitly emulates numpy's behavior: https://github.com/numba/numba/blob/28e68e58c2268ac022d384b51417ab14d819814a/numba/np/npyfuncs.py#L579 So maybe someone should raise an issue with numba if this is merged? |
3ecb450
to
284817f
Compare
@mtsokol - thanks for the review. Asking for tests is always good - it smoked out some undesirable behaviour (e.g., real being inf while imag being nan). So, I now use the definition of |
284817f
to
7a1836b
Compare
Thanks @mhvk! This does look like an improvement indeed. I think it also caught a gap in the array API test suite, or this would have shown up as a failure (maybe you can check this @mtsokol?). I ran the SciPy test suite against this branch, and it showed a single error (I double checked, it's reproducible) in
There are a handful of It's hard to tell quickly what is going on there. It could be an unstable test - if the behavior of (just seeing the new commit, will retest EDIT: indeed, no difference) |
@ngoldbaum - thanks for checking! And a good point about numba; I raised an issue there: numba/numba#9376 About scipy, my latest change is not likely to resolve the issue since it only deals with infinities and NaN. I'll try to see if I can figure out what is going on as well. |
p.s. Second push was just a linter issue, so no need to test that again! (EDIT: and third push an uncaptured invalid value error on FreeBSD). |
7a1836b
to
9ddcb23
Compare
It is weird, the scipy function explicitly is for real values only according to its docstring. Maybe complex To be honest, I think it is basically luck that that test passed with the existing definition of |
See scipy/scipy#19739 for an issue about the failing complex interpolation (and how it can be made to fail also with the present definition of |
Just to duplicate what is said on the scipy issue: the scipy test is wrong and it's safe to ignore it for this PR. |
@rgommers It looks like Neither The array-api-tests CI stage doesn't fail here because One thing - I noticed that |
That'd be great, thanks!
That indeed seems like an obvious improvement. And it should be backwards compatible. It's very well possible that |
Following the API Array standard, the complex sign is now calculated as ``z / |z|`` (instead of the rather less logical case where the sign of the real part was taken, unless the real part was zero, in which case the sign of the imaginary part was returned). Like for real numbers, zero is returned if ``z==0``.
9ddcb23
to
0255397
Compare
OK, following discussion on the mailing list, it seemed there was fair consensus on changing the definition of complex sign, but not on using it in Anyway, I think this is ready for final review... |
FYI data-apis/array-api-tests#227 got merged today. |
@mtsokol approved. Does anyone else want to have a look? Or can this redefinition of |
out = np.zeros(a.shape, a.dtype) | ||
tgt = np.array([ | ||
1., -1., 1j, -1j, | ||
] + [complex(np.nan, np.nan)] * 5 + [ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it perhaps confusing that this is the case:
In [1]: np.sign(np.nan)
Out[1]: np.float64(nan)
In [2]: np.sign(complex(np.nan))
Out[2]: np.complex128(nan+nanj)
Perhaps if the real or imaginary part is equal to zero, we should return 0 instead of NaN for the real or imaginary part of the result.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure, my sense would be that when either component is NaN, it is best to propagate that to both; it also keeps the result closest to the definition of z/|z|
(for |z}->0,±inf, one can argue about meaningful limits, but less so for NaN).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, on second thought I agree that having only a single kind of nan
result here probably makes sense. I hope I never have a technical problem where complex NaN is something I need to worry about...
This got overwhelming support on the mailing list, let's pull this in. Thank you @mhvk! |
This may be controversial, but the old definition of sign for complex numbers is really useless, and if we don't change it in 2.0, when will we? Note how the code in
geomspace
is substantially simplified by using the new definition!From the release note included:
Following the API Array standard, the complex sign is now calculated as
z / |z|
(instead of the rather less logical case where the sign of the real part was taken, unless the real part was zero, in which case the sign of the imaginary part was returned). Like for real numbers, zero is returned ifz==0
.EDIT: after discussion on the list, the consensus seemed to be that redefining
sign
was a good idea, but that the case forcopysign
was not strong. So, that is now removed from this PR.With this, it has become possible to extendnp.copysign(x1, x2)
to complex numbers, since it can now generally return|x1| * sign(x2)
with the sign as defined above (with no special treatment for zero).