Skip to content
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

ENH: Add np.full to dispatchable functions #16138

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

MridulS
Copy link
Contributor

@MridulS MridulS commented Mar 1, 2024

Description

This almost fixes #10836, with NEP 35 it is possible to dispatch functions like np.full.

With this change the following code should work:

my_quantity = u.Quantity(1, u.m)
np.full(10, fill_value=my_quantity, like=my_quantity)

This does require passing in the like argument as numpy can't auto infer from fill_value, and I guess it gets close enough to the behavior requested in #10836 (comment)

  • By checking this box, the PR author has requested that maintainers do NOT use the "Squash and Merge" button. Maintainers should respect this when possible; however, the final decision is at the discretion of the maintainer that merges the PR.

@github-actions github-actions bot added the units label Mar 1, 2024
Copy link

github-actions bot commented Mar 1, 2024

Thank you for your contribution to Astropy! 🌌 This checklist is meant to remind the package maintainers who will review this pull request of some common things to look for.

  • Do the proposed changes actually accomplish desired goals?
  • Do the proposed changes follow the Astropy coding guidelines?
  • Are tests added/updated as required? If so, do they follow the Astropy testing guidelines?
  • Are docs added/updated as required? If so, do they follow the Astropy documentation guidelines?
  • Is rebase and/or squash necessary? If so, please provide the author with appropriate instructions. Also see instructions for rebase and squash.
  • Did the CI pass? If no, are the failures related? If you need to run daily and weekly cron jobs as part of the PR, please apply the "Extra CI" label. Codestyle issues can be fixed by the bot.
  • Is a change log needed? If yes, did the change log check pass? If no, add the "no-changelog-entry-needed" label. If this is a manual backport, use the "skip-changelog-checks" label unless special changelog handling is necessary.
  • Is this a big PR that makes a "What's new?" entry worthwhile and if so, is (1) a "what's new" entry included in this PR and (2) the "whatsnew-needed" label applied?
  • At the time of adding the milestone, if the milestone set requires a backport to release branch(es), apply the appropriate "backport-X.Y.x" label(s) before merge.

@@ -241,6 +241,10 @@ def like_helper(a, *args, **kwargs):
unit = a.unit if subok else None
return (a.view(np.ndarray),) + args, kwargs, unit, None

@dispatched_function
def full(shape, fill_value, dtype=None, order='C'):
return np.full(shape, fill_value, dtype, order), fill_value.unit, None
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This could also be something like:

Suggested change
return np.full(shape, fill_value, dtype, order), fill_value.unit, None
return np.full(shape, fill_value.view(np.ndarray), dtype, order), fill_value.unit, None

Copy link
Contributor

@mhvk mhvk Mar 1, 2024

Choose a reason for hiding this comment

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

You do not know here that fill_value is a Quantity - because you will have gotten here by the like argument. So, you'd need to pass it through _as_quantity(). I also think this works fine as a function_helper, i.e.,

@function_helper
def full(shape, fill_value, dtype=None, order='C'):
    fill_value = _as_quantity(fill_value)
    return (shape, fill_value.value, dtype, order), {}, fill_value.unit, None

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hmm, so something like np.full(10, fill_value=1, like=my_quantity) is expected behavior too?

Copy link
Contributor

Choose a reason for hiding this comment

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

I think the idea is you pass in a class, as in like=Quantity, and for Quantity, fill_value does not have to have units. But you make a good point that for Angle there should be units, so my solution is not correct for that. Unfortunately, in this piece of code we do not actually know what (sub)class of Quantity we're in! We only know inside __array_function__... I think this will need some looking into how it actually is called...

Copy link
Contributor

@mhvk mhvk left a comment

Choose a reason for hiding this comment

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

Thanks, that has long been on my wish list! Note that I mention other creation routines, but I'm frankly not sure any of them is very useful - np.full is really the one exception. So, I'm fine with just doing this one and hard-coding it in the list of functions we test and support.

p.s. Of course, if you really want to go for this, one can also do the same for Masked...

@@ -241,6 +241,10 @@ def like_helper(a, *args, **kwargs):
unit = a.unit if subok else None
return (a.view(np.ndarray),) + args, kwargs, unit, None

@dispatched_function
def full(shape, fill_value, dtype=None, order='C'):
return np.full(shape, fill_value, dtype, order), fill_value.unit, None
Copy link
Contributor

@mhvk mhvk Mar 1, 2024

Choose a reason for hiding this comment

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

You do not know here that fill_value is a Quantity - because you will have gotten here by the like argument. So, you'd need to pass it through _as_quantity(). I also think this works fine as a function_helper, i.e.,

@function_helper
def full(shape, fill_value, dtype=None, order='C'):
    fill_value = _as_quantity(fill_value)
    return (shape, fill_value.value, dtype, order), {}, fill_value.unit, None

a = u.Quantity(1, u.m)
b = np.full(3, a, like=a)
assert b.unit == a.unit
assert len(b) == 3
Copy link
Contributor

Choose a reason for hiding this comment

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

This file is really for is_close, etc., i.e., helper routines to deal with quantities rather than helper routines to deal with numpy functions (naming is hard!!). It should go in test_quantity_non_ufuncs.py (did I mention naming is hard?).

Note that that file attempts to keep track of whether our coverage is complete, and the set that it things needs to be covered is calculated in get_wrapped_functions - you'd need to extend that to include the generation-like routines like np.full (also np.zeros, np.arange, etc.). It would make sense to create a new TestCreatingFunctionsUsingLike or so.

@pllim pllim added this to the v6.1.0 milestone Mar 4, 2024
@astrofrog astrofrog modified the milestones: v6.1.0, v7.0.0 Apr 4, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

np.full for Quantities
4 participants