Thanks to visit codestin.com
Credit goes to github.com

Skip to content

float(np.complex128) silently drops imaginary part #13007

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

Open
goerz opened this issue Feb 21, 2019 · 8 comments
Open

float(np.complex128) silently drops imaginary part #13007

goerz opened this issue Feb 21, 2019 · 8 comments

Comments

@goerz
Copy link
Contributor

goerz commented Feb 21, 2019

>>> v = 2j
>>> float(v)
*** TypeError: can't convert complex to float

>>> v = np.complex128(2j)
>>> float(v)
0.0

Silently dropping imaginary parts is a recipe for disaster (bugs) in numerical code!

@seberg
Copy link
Member

seberg commented Feb 21, 2019

Out of curiosity which numpy/python versions are you using? This should at least give you:

__main__:1: ComplexWarning: Casting complex values to real discards the imaginary part

(or at least it does for me on 1.16.1 and python 3.7). It could be good to think about moving forward towards an error in any case.

@goerz
Copy link
Contributor Author

goerz commented Feb 21, 2019

Huh. Actually, I also get that warning when I do it in IPython, but not inside a debugger. I'm on '1.15.4' and Python 3.6 and 3.7. In any case, the ComplexWarning does get triggered, even though pdb wasn't showing it to me, and it took me a while to figure out how to actually catch that warning (https://stackoverflow.com/a/54814548/152544)

I don't know. Personally, I'm rather allergic to type-casting errors (I've seen them produce subtle, hard to detect wrong results in simulations too many times), so I would much prefer ComplexWarning to be an exception by default. I suppose the reason it's not is efficiency?

Feel free to close if you don't find this issue actionable any further.

@seberg
Copy link
Member

seberg commented Feb 21, 2019

I think warnings should be less efficient. Note you could set them to "error" for yourself, unless that breaks some dependency that happens to be not-clean. The reason is probably historic, going to warning is a simple step, going to error might break a lot of code that implicitly assumes it works and requires to first give DeprecationWarnings as well.

Would have to check if there is more at work here or not. I guess we could close it, although I would agree with trying to move to an error eventually.

@namurphy
Copy link

namurphy commented Mar 5, 2020

I would support moving towards an error since there is inconsistent behavior with float and numpy.float as applied to complex numbers from Python versus complex numbers from NumPy.

>>> import numpy as np
>>> print(np.__version__)
1.18.1

>>> standard_complex_number = 5 + 6j
>>> numpy_complex_number = np.complex128(5 + 6j)

>>> float(standard_complex_number)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can't convert complex to float

>>> np.float(standard_complex_number)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can't convert complex to float

>>> float(numpy_complex_number)  # happens loudly the first time
<stdin>:1: ComplexWarning: Casting complex values to real discards the imaginary part
5.0

>>> np.float(numpy_complex_number)  # happens silently the second time
5.0

I missed the ComplexWarning at first since I was working on a unit test, so it took me a while to figure out where the inconsistent behavior was happening. A deprecation warning could suggest using numpy.real instead, for which this behavior is explicit and intentional.

@bsipocz
Copy link
Member

bsipocz commented Mar 5, 2020

xrefing to the astropy issue (astropy/astropy#10010), so we more likely notice when this will be closed.

@seberg
Copy link
Member

seberg commented Mar 5, 2020

There may be two distinct cases here:

In [2]: float(np.complex_(1j))                                                                                          
/home/sebastian/.local/bin/ipython3:1: ComplexWarning: Casting complex values to real discards the imaginary part
  #!/usr/bin/python3
Out[2]: 0.0

In [3]: np.complex_(1j).astype(np.float64)                                                                              
/home/sebastian/.local/bin/ipython3:1: ComplexWarning: Casting complex values to real discards the imaginary part
  #!/usr/bin/python3
Out[3]: 0.0

I think the first one is more obviously incorrect. I also agree that we should probably be doing less unsafe casting in the second case as well, but currently we tend to do unsafe casting everywhere in these cases. I.e. np.complex_(1j).astype(np.float64, casting="same_kind") fails.

@rgommers
Copy link
Member

+1 for deprecating and then turning all of the ComplexWarning cases into exceptions.

Cc @leofang, who brought this up at data-apis/array-api#102 (comment). CuPy follows NumPy's current behaviour, but erroring out would be nicer.

@seberg
Copy link
Member

seberg commented Jan 3, 2021

I am very open to the first case. Does anyone have a clue about whether the second case is actually used in the wild and might require work-arounds? Disabling astype() seems tricky to me, unless you would add a new casting level like arr.astype(float64, casting="very-unsafe"), since otherwise it requires to change the code pattern to using arr.real.astype() and I am not immediately certain that doesn't break other patterns.

I am happy to be convinced that a cast from complex to float can only reasonably occur in a situation dealing explicitly with complex numbers where using arr.real explicitly works. Otherwise, it might be better to rethink the casting="unsafe" default rather than the complex cast explicitly. (i.e. why should string_arr.astype("float") work if complex to float doesn't?)

EDIT: To be clear, I can't think of any place where you would want to force cast everything (including complex) to float using astype, but where using arr.real may be weird. So I will not need much convincing. The main thing is that disabling the unsafe cast entirely seems like a big step.

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

No branches or pull requests

5 participants