Skip to content

API: Return scalars for scalar inputs to np.real/imag - #8388

Merged
seberg merged 1 commit into
numpy:masterfrom
gfyoung:real-imag-scalar
Mar 26, 2017
Merged

seberg merged 1 commit into
numpy:masterfrom
gfyoung:real-imag-scalar

Conversation

@gfyoung

@gfyoung gfyoung commented Dec 17, 2016

Copy link
Copy Markdown
Contributor

Returns scalars for scalar inputs for the functions np.real and np.imag, as consistent with functions like np.angle and np.conj.

Closes #8386.

@seberg

seberg commented Dec 18, 2016

Copy link
Copy Markdown
Member

Its not obvious that this is correct. doing this implies that putting in a 0-D array will not give you a view like the attribute does.

@gfyoung

gfyoung commented Dec 18, 2016

Copy link
Copy Markdown
Contributor Author

@seberg : True, but it is consistent with what these other numpy functions do. It would be preferable to choose one (return view) or the other (return scalar).

@seberg

seberg commented Dec 18, 2016

Copy link
Copy Markdown
Member

The other functions all return copies though.

@gfyoung

gfyoung commented Dec 18, 2016

Copy link
Copy Markdown
Contributor Author

Then in that case we should return scalars across the board, unless there is an advantageous reason why we should return a view for the 0-D array case that would override consistency.

@seberg

seberg commented Dec 18, 2016

Copy link
Copy Markdown
Member

It is a view for all other dimensions, I am just saying that it is an API change, and that I think the comparison to angle or conj is only half valid.

@gfyoung

gfyoung commented Dec 18, 2016 •

Copy link
Copy Markdown
Contributor Author

Fair enough, though I did acknowledge that this is an API change if you read the title of this PR.

Also, correct me if I'm wrong, but how many functions in numpy return array when a scalar is passed in? I think the majority behavior is to return a scalar.

@charris

charris commented Dec 29, 2016 •

Copy link
Copy Markdown
Member

Having a view can be useful as the view can be used as the value of an out parameter.

EDIT: Not clear to me that this should treat 0-D arrays as scalars.

@gfyoung

gfyoung commented Dec 29, 2016 •

Copy link
Copy Markdown
Contributor Author

@charris: I still return 0-D arrays when one is passed in, but when a scalar is passed in, I return scalar. That seems consistent with what we do with other functions.

@seberg

seberg commented Dec 29, 2016

Copy link
Copy Markdown
Member

@gfyoung you do? In that case, can you make it clear with a test pointing that out? 0-D arrays are rare, but still....

@gfyoung

gfyoung commented Dec 29, 2016

Copy link
Copy Markdown
Contributor Author

@seberg , @charris : Added test cases to confirm that 0-D array input gets 0-D array output. Ready to merge if there are no other concerns.

@seberg

seberg commented Dec 29, 2016 via email

Copy link
Copy Markdown
Member

@gfyoung

gfyoung commented Dec 29, 2016 •

Copy link
Copy Markdown
Contributor Author

Nope, sorry, but me asking for a test was a trick question really.

What type of comment is this? Not really sure why you're trying to be "clever".

Also, you should look at the PR again before making comments like this. I wrote the test case out, and the behavior does return 0-D array upon 0-D input as requested.

@seberg

seberg commented Dec 29, 2016

Copy link
Copy Markdown
Member

Was a bit evil, but anyway... You did not test for array return, because scalars are equal to arrays in those tests.

@gfyoung

gfyoung commented Dec 29, 2016 •

Copy link
Copy Markdown
Contributor Author

You did not test for array return, because scalars are equal to arrays in those tests.

Now that's a more useful comment: straightforward and to the point. I've updated the tests to check.

Please refrain from your "trickery" in subsequent comments. Thanks!

@gfyoung

gfyoung commented Dec 29, 2016

Copy link
Copy Markdown
Contributor Author

@charris , @seberg : Updated test to confirm that 0-D array is returned when we pass in 0-D array, and everything is still passing.

Comment thread numpy/lib/type_check.py Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Now it never returns scalars.

@gfyoung gfyoung Dec 30, 2016 •

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.

I STRONGLY beg to differ on this point. I just confirmed it myself. I would encourage you to check it out yourself to confirm.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

oops, misread.

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.

Ah, okay. No worries. 😄

@gfyoung

gfyoung commented Jan 11, 2017

Copy link
Copy Markdown
Contributor Author

@charris , @seberg : Any updates on this?

Comment thread numpy/lib/type_check.py Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Given that we imported _nx on the line above, and much of this file uses it already, is there any real point importing isscalar here?

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.

Fair enough. Done.

Comment thread numpy/lib/tests/test_type_check.py Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This doesn't actually check that the scalar is returned - all of these tests would have passed before this patch, I think.

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.

Ah, that is true. Done.

@gfyoung

gfyoung commented Feb 20, 2017

Copy link
Copy Markdown
Contributor Author

@eric-wieser : Made the requested changes. Please have a look.

@eric-wieser eric-wieser left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Generally looks good, just some minor nitpicks on doc comments - not sure if this needs a manual release note

Comment thread numpy/lib/type_check.py Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nit-pick: "component of the ..."

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.

Done.

Comment thread numpy/lib/type_check.py Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

(same here)

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.

Done.

Comment thread numpy/lib/type_check.py Outdated

@eric-wieser eric-wieser Feb 20, 2017 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Not really float: complex128 -> float64, complex32 -> float32. But that was here before, so out of scope for this PR, I think

@gfyoung gfyoung Feb 20, 2017 •

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.

Actually, the exact float type seems platform-dependent. On my machine, it actually returns float128 when I pass in complex128.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

That sounds like a bug if its true?

@gfyoung gfyoung Feb 20, 2017 •

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.

Can't tell at this point, though @eric-wieser I am a little surprised that complex128 would return float64 if the architecture is capable of supporting complex128.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I am a little surprised that complex128 would return float64

Why? 64 bits of real + 64 bits of imaginary = 128 bits of complex

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.

Okay...but then why would I get float128 returned on my machine then?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Since the result is a view (unless it happens to be scalar), it frankly seems pretty improbable to me at least for the array case. Can you double check? And if, what system exactly?

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.

Ah, okay. I should have clarified. For the scalar case, I get float128, but for the array case, I do indeed get float64.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

By "the scalar case", you presumably mean complex not np.complex128

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.

@eric-wieser : That is correct.

@gfyoung

gfyoung commented Feb 20, 2017 •

Copy link
Copy Markdown
Contributor Author

not sure if this needs a manual release note

IMO probably not This change is relatively minor compared to those that would find themselves in the release notes.

@gfyoung

gfyoung commented Feb 21, 2017

Copy link
Copy Markdown
Contributor Author

@eric-wieser @seberg : Changes made as requested, and all is green.

Comment thread numpy/lib/type_check.py Outdated

@eric-wieser eric-wieser Feb 21, 2017 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Just thinking about duck typing here... How about:

try:
    return val.real
except AttributeError:
    return asanyarray(val).real

Which has the nice bonus that builtin complex returns builtin float not np.float64

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.

Good point. Done.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Although now this is inconsistent with np.conj, which does not return a native complex...

What was the previous implementation?

@eric-wieser

eric-wieser commented Feb 21, 2017 •

Copy link
Copy Markdown
Member

Tests look good now, but I'm kinda tempted to duck-type this in the same way we duck-type np.min and np.ndim(...) in fromnumeric

@gfyoung

gfyoung commented Feb 21, 2017

Copy link
Copy Markdown
Contributor Author

@eric-wieser @seberg : Changes made as requested, and all is green.

@eric-wieser

Copy link
Copy Markdown
Member

Not green for me, but looks good. @seberg, do you agree with the duck-typing I suggested above that @gfyoung has now implemented?

@gfyoung

gfyoung commented Mar 8, 2017

Copy link
Copy Markdown
Contributor Author

@seberg : Any updates on this? I think @eric-wieser is waiting for your response before merging.

@gfyoung

gfyoung commented Mar 25, 2017 •

Copy link
Copy Markdown
Contributor Author

@eric-wieser : @seberg seems to not be responding (either because the message wasn't seen or this is tacit approval). Ideas of how to move forward? This change seems relatively trivial.

@seberg

seberg commented Mar 26, 2017

Copy link
Copy Markdown
Member

It looks good to me now, sorry if I am slow but don't have much brain cycles for numpy right now, and frankly the first implementations looked too tricky to me to just approve off. Even here I would not mind mentioning it in the release notes, but probably it is not necessary.

@eric-wieser

eric-wieser commented Mar 26, 2017 •

Copy link
Copy Markdown
Member

Ugh, I've just realized that this fails to meet the goal of "Returns scalars for scalar inputs as consistent with functions like np.angle and np.conj".

type(np.angle(1+1j)) == np.float64, but type(np.real(1+1j)) == float

@seberg

seberg commented Mar 26, 2017

Copy link
Copy Markdown
Member

Well, this is subclassing hell.... np.angle I don't understand why that is a goal, there is no angle method after all. np.conjugate could indeed be set up to go via the method in principle, since it is pretty much a well defined method within python. This "function that is basically supposed to be an operator" thing is really annoying.

Overall, I think the approach of try/except is consistent with what we do on most methods at least, so I don't mind the change, and doubt right now it creates much trouble. @mhvk you are a subclass guy, maybe you can give your opinion/final decision on whether this is an improvement or not?

@eric-wieser

eric-wieser commented Mar 26, 2017 •

Copy link
Copy Markdown
Member

What we had in an earlier iteration actually fails in exactly the same way:

arr = asanyarray(val)
return arr.real.item() if isscalar(val) else arr.real
#               ^ item returns float, not float64

A possible fix would be to avoid .item():

arr = asanyarray(val)
return arr[()].real if isscalar(val) else arr.real

Or possibly

def asanyarray_or_scalar(val):
    arr = asanyarray(val)
    if isscalar(val):
        return arr[()]
    else:
        return arr

def real(val):
    return asanyarray_or_scalar(val).real

@seberg

seberg commented Mar 26, 2017

Copy link
Copy Markdown
Member

Not sure I like that, because it does not return views. Though, I admit, if it creates problems in code, it should at least not be silent problems, since the scalars are immutable.

@eric-wieser

Copy link
Copy Markdown
Member

@seberg: That comment got edited way too much, you should probably check it still says what you were replying to

@seberg

seberg commented Mar 26, 2017

Copy link
Copy Markdown
Member

Yeah, well... we just have to be careful we don't simply replace one problem with another.

@eric-wieser

Copy link
Copy Markdown
Member

I agree. I think the above code now does return a view, if passed a viewable object?

@mhvk

mhvk commented Mar 26, 2017

Copy link
Copy Markdown
Contributor

I had a look and I think this is the right implementation. I don't think it makes all that much sense to compare with conj and angle, as both actually do an operation, while real and imag just select a particular part of an operand. (Indeed, angle could use some work - why, o why, this need to asarray all input...).

So, I think this should just go in.

@seberg

seberg commented Mar 26, 2017

Copy link
Copy Markdown
Member

Isn't it a great convenience ;). Ok, then lets just put it in and hopefully we are not missing anything that might get broken here.

@seberg
seberg merged commit 82e0860 into numpy:master Mar 26, 2017
@charris

charris commented Mar 26, 2017

Copy link
Copy Markdown
Member

Needs a release note...

@gfyoung
gfyoung deleted the real-imag-scalar branch March 26, 2017 20:01
@gfyoung

gfyoung commented Mar 26, 2017

Copy link
Copy Markdown
Contributor Author

@seberg , @eric-wieser , @mhvk : Thanks for all of the input!

@charris : Acknowledged. Will put up PR as follow-up.

@WarrenWeckesser

WarrenWeckesser commented Mar 27, 2017 •

Copy link
Copy Markdown
Member

Take a look at scipy/scipy#7227

Note, in particular, the quirk of numpy.testing.assert_equal() that I commented on there (using numpy 1.12.1):

In [10]: from numpy.testing import assert_equal

In [11]: assert_equal(np.array(-0.0), 0)

In [12]: assert_equal(-0.0, 0)
---------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
<ipython-input-12-8315da2bce84> in <module>()
----> 1 assert_equal(-0.0, 0)

/Users/warren/miniconda3scipy/lib/python3.5/site-packages/numpy/testing/utils.py in assert_equal(actual, desired, err_msg, verbose)
    410         elif desired == 0 and actual == 0:
    411             if not signbit(desired) == signbit(actual):
--> 412                 raise AssertionError(msg)
    413     # If TypeError or ValueError raised while using isnan and co, just handle
    414     # as before

AssertionError: 
Items are not equal:
 ACTUAL: -0.0
 DESIRED: 0

I haven't tracked down the ultimate source of the failure in scipy, but I suspect a use of assert_equal that previously passed is now failing because something that was an array containing -0.0 is now the scalar -0.0.

@WarrenWeckesser

Copy link
Copy Markdown
Member

I've narrowed down the problem to the issue reported in #8848, so further discussion should be over there.

@gfyoung

gfyoung commented Mar 27, 2017

Copy link
Copy Markdown
Contributor Author

@WarrenWeckesser : Still, yikes...what you indicated above is a bug it seems.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants