-
-
Notifications
You must be signed in to change notification settings - Fork 10.1k
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
BUG: np.resize
negative shape and subclasses edge case fixes
#15037
Conversation
I think you should be raising an error when the size is negative, not just forcing it to be positive |
@eric-wieser |
numpy/core/tests/test_numeric.py
Outdated
@@ -48,6 +48,14 @@ def test_reshape_from_zero(self): | |||
assert_array_equal(Ar, np.zeros((2, 1), Ar.dtype)) | |||
assert_equal(A.dtype, Ar.dtype) | |||
|
|||
def test_negativeresize(self): | |||
A = np.arange(0, 10, dtype=np.float32) | |||
new_shape = (10,-1) |
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.
Can you add a test for (-10, -1)
too, which should also fail?
(hint: It won't fail, you need to adjust your condition above)
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.
I think this should be resolved now
numpy/core/tests/test_numeric.py
Outdated
new_shape = (10,-1) | ||
err_msg = (r"new_shape can't have negative values." | ||
r" found: new_shape=(10,-1)") | ||
with pytest.raises(ValueError, match=err_msg): |
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.
match="negative"
would probably be enough here - matching the full message makes the test overly brittle to wording changes.
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.
I dont know why the tests are failing with the current fix! Can you please guide me through it? Thanks for your replies!
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.
You can see the failing test result here:
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.
There's a bug in the error handling, opened gh-15039
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.
Ohk! Thanks!
Close / reopen to rerun tests now that #15039 is in |
Still failing, but with a better traceback. Something very weird is going on here: |
The reason is probably that EDIT: Oh, |
I pushed a commit trying that (some hopefully slight rewording/style fixes). |
I am so sorry to add some bad commit from my work on another issue! |
Sorry, I had lost track of this. Forced push the old version here. But I think we may try one from the discussion I had with Eric. Do you want to have a look, otherwise please ping me so we can maybe just fix this up. |
@seberg want to finish this up? Perhaps the implementation should become: ret = np.zeros_like(a, shape=new_shape, order="C")
ret.flat[:] = a # if a.size == 0 this is a no-op, hence the `zeros` above
return ret |
So... turned out the The There is an additional bug fix, since it should be |
numpy/core/fromnumeric.py
Outdated
extra = new_size % a.size | ||
repeats = (new_size - extra) // a.size |
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.
Shorter and perhaps faster as
repeats, extra = divmod(new_size, a.size)
edit: actually, I think what you have here is wrong - shouldn't repeats round up, not down? That can be spelt as:
repeats, extra = divmod(newsize,-a.size)
repeats = -repeats
or perhaps more clearly
repeats = -(-newsize // a.size) # ceil division
a = concatenate((a,) * repeats)
a = a[:newsize]
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.
There is no down rounding here, since the result is always an exact integer (the //
only makes sure it remains an int)? But yes, divmod is better, somehow forgot it is a python function/operator...
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.
There is rounding down, you did it via the subtraction.
As I read it, this will make np.arange(5).resize(7)
give an array of the wrong size - your division will give 1
, and then extra will be 2
so you'll return an array of length 3
.
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.
Sorry, yeah, I guess I would have thought such a big error gets found by the tests :/
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.
Jeesh... emberassing. I just opted for duplicating the concatenate
line to keep it stupid, open for other things, but...
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 there a test for np.arange(5).resize(7)
?
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.
There is now...
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.
where?
np.resize
negative shape and subclasses edge case fixes
|
||
def test_subclass(self): | ||
class MyArray(np.ndarray): | ||
__array_priority__ = 1. |
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.
What happens without this member?
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.
concatenate seems to lose the subclass info. That could actually be a bug, since all inputs are MyArray
.
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.
Doesn't concatenate
always lose subclass info?
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.
Hmm, no, the test would fail if it did :).
When a negative entry is passed into `np.resize` shape, an error will now be raised reliably. Previously the `-1` semantics of reshape allowed edge cases to pass and return incorrect results. In the corner case of an empty result shape or empty input shape, the result type will now be preserved.
I think this was ready, I am a bit hesitant to merge though, since I was the one who pushed the last changed. |
extra = total_size % Na | ||
new_size = 1 | ||
for dim_length in new_shape: | ||
new_size *= dim_length |
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.
Could put this below the error check for a marginal speed boost in the error case, but doesn't really matter.
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.
Doubt its nicer to read, and I refuse to optimize error-cases without good reason :). Thanks for another pass Eric lets put it in.
Fixes: #15030