Skip to content

MAINT: Fix computation of numpy.array_api.linalg.vector_norm #21084

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

Merged
merged 4 commits into from
Jun 16, 2022

Conversation

asmeurer
Copy link
Member

There were several issues due to the fact that it was mostly untested in the array API test suite.

This also fixes #21083. I'm unclear exactly how I should add a test for this.

For the array API vector_norm, tests are being added to the array API test suite.

Various pieces were incorrect due to a lack of complete coverage of this
function in the array API test suite.
Previously it would always give float64 because an internal calculation
involved a NumPy scalar and a Python float. The fix is to use a 0-D array
instead of a NumPy scalar so that it type promotes with the float correctly.

Fixes numpy#21083

I don't have a test for this yet because I'm unclear how exactly to test it.
Copy link
Member

@seberg seberg left a comment

Choose a reason for hiding this comment

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

A few small comments/suggestions but looks fine. Maybe @honno can sign this off?

if a.ndim == 0:
a = a[None]
else:
a = a.flatten()
Copy link
Member

@seberg seberg Feb 18, 2022

Choose a reason for hiding this comment

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

I don't understand, flatten should always return a 1-D array?

EDIT: Actually, why not .ravel()? flatten forces a copy and that is really not necessary (ravel copies more often than you would expect too, but at least not always.)

Copy link
Member Author

Choose a reason for hiding this comment

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

Oh I never realized that about flatten. We have to make it a 1-D array to force it to do a vector norm (this weird shape-based behavior is why we split norm into vector_norm and matrix_norm in the array API).

Copy link
Member

Choose a reason for hiding this comment

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

Yes, but np.array(0).ravel() (or flatten) also is 1D, this is not <= 1 but == 1, so ravel() covers the a[None] path just as well.

(np.prod([a.shape[i] for i in axis], dtype=int), *[a.shape[i] for i in rest]))
_axis = 0
else:
_axis = axis
Copy link
Member

Choose a reason for hiding this comment

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

We do have a normalize_axis_tuple helper in numpy/core/numeric.py, maybe worthwhile?

Copy link
Member Author

Choose a reason for hiding this comment

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

Ah, I should have guessed there are probably already helpers in NumPy to do some of these things. I'll see if I can find anything that makes this simpler.

elif isinstance(axis, int):
_axis = (axis,)
else:
_axis = axis
Copy link
Member

Choose a reason for hiding this comment

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

Might put this together with the setup (i.e. figuring out the result shape there? But looks fine.

@asmeurer
Copy link
Member Author

@seberg any thoughts on how I can add a test for #21083?

@seberg
Copy link
Member

seberg commented Feb 18, 2022

Frankly, no, this is almost certainly ancient code from the early days of NumPy (or possibly older!), I would just git grep for norm tests and add one next to it where it seems reasonable.

@asmeurer
Copy link
Member Author

Yeah, I figured out where the norm tests are. I just am really unclear how exactly they work. Just reading them it looks to me like this ought to already be tested (specifically here).

Actually, looking closer now, I think I see the problem, which is that the test here doesn't actually test the output dtype, just that it is a floating dtype.

@seberg
Copy link
Member

seberg commented Feb 18, 2022

On first glance, I think the tests are just buggy. _TestNormGeneral doesn't seem to actually use self.dt in the relevent tests? And even then, I am not sure its checks would be sufficient (but didn't dig in).

It would be nice to just replace this stuff with pytest.parametrize, that would be much clearer to read than this inheritance dance.

@asmeurer
Copy link
Member Author

I agree. I don't know if this sort of thing is standard in the NumPy test suite or if it's just a weird leftover thing from ages ago that hasn't been modernized.

@seberg
Copy link
Member

seberg commented Feb 18, 2022

Just a weird leftover, probably it was a good and typical pattern at some point. I would not mind if it was just parametrized even if that is a biggish diff, on first sight, it looks straight forward enough.

@asmeurer
Copy link
Member Author

I also didn't realize those test are also testing integer inputs to norm() (which aren't even in scope in the array API). So testing the correct dtype is a little more complicated. Is there a helper function to get the correct promoted type?

Also, I hope this isn't a problem, but I'm probably not going to refactor those tests to use parameterization. It would make a good first issue for someone to do that, but I really just want to add a test for my fix if it's not too complicated.

@seberg
Copy link
Member

seberg commented Feb 19, 2022

Is there a helper function to get the correct promoted type?

I dunno np.result_type(dtype1, dtyp2, 0.)? I am not sure what the current logic ends up doing exactly, though. I mean, promotion does depend on the operation in principle, i.e. the maximum norm should preserve dtype in principle, but other norms should promote to floating.

Also, I hope this isn't a problem, but I'm probably not going to refactor those tests to use parameterization. It would make a good first issue for someone to do that, but I really just want to add a test for my fix if it's not too complicated.

nah sure, but doesn't hurt to hope ;).

@charris charris changed the title Fix computation of numpy.array_api.linalg.vector_norm MAINT: Fix computation of numpy.array_api.linalg.vector_norm Feb 20, 2022
@honno
Copy link
Contributor

honno commented Feb 21, 2022

@seberg's comments on using ravel() and the existing axes utils (if possible) make sense, otherwise LGTM. Personally I prefer figuring out result shapes after the fact, as you can follow up the reshape straight after to reduce cognitive load.

@asmeurer
Copy link
Member Author

I think fiddling with the existing linalg tests is going to be a deeper rabbit hole than I really want to get down for this PR. If you want I can add a simple, separate regression test for the norm() fix. Otherwise, I would suggest opening a "good first contribution" issue for refactoring those tests to use pytest parameterization, and also to more explicitly test the result type in each case.

I have cleaned up the implementation of numpy.array_api.vector_norm a little bit as per the review comments.

@charris charris closed this Apr 6, 2022
@charris charris reopened this Apr 6, 2022
@seberg
Copy link
Member

seberg commented Jun 14, 2022

Unfortunately there is now a (probably very small) merge conflict. @honno otherwise you were happy with this and we should just merge it?

@honno
Copy link
Contributor

honno commented Jun 15, 2022

Unfortunately there is now a (probably very small) merge conflict. @honno otherwise you were happy with this and we should just merge it?

Yep LGTM other then merge conflicts. Again @seberg's previous suggestions on ravel() and exploring existing axis are worth exploring, but it'd be nice to just get this in.

However let's just see if @asmeurer is okay with this PR still, as there have been more questions and clarifications on the linalg extension in the spec since this PR was opened.

@asmeurer
Copy link
Member Author

Looks like #21083 was fixed by #17709 so my fix is no longer needed. No opinion on which of the fixes is better. I guess neither would be needed once type promotion is cleaned up. At least that PR was (apparently) able to add tests.

@asmeurer
Copy link
Member Author

The actual fix in the array_api is still relevant. I have fixed the conflicts.

@seberg
Copy link
Member

seberg commented Jun 16, 2022

Lets put it in then. Thanks!

@seberg seberg merged commit 70026c4 into numpy:main Jun 16, 2022
leofang pushed a commit to leofang/cupy that referenced this pull request Jul 29, 2022
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.

BUG: "Non-standard" vector-norms of 1-D arrays in norm() always return float64 dtype
4 participants