Skip to content

NEP35 implementation tracker #17124

Open
Open

Description

@neutrinoceros

This was initially discussed in #17001. This is the larger scope issue.

In short, NEP 35 can be seen as an extension of NEP 18 and provides a different yet similar dispatching mechanism for some array constructor functions in NumPy, which can (and should) be implemented in Quantity and Masked.
Because NumPy itself doesn't explicitly list them1, our completion tests do not report them as missing implementations, and the list needs to be somehow retrieved manually. I'll write it down here as two TODO lists (one for each subpackage) and link relevant PRs

Quantity

Bonus round

Masked

  • arange
  • empty
  • ones
  • zeros
  • full
  • array
  • asarray
  • asanyarray
  • ascontiguousarray
  • asfortranarray
  • require
  • fromfunction
  • fromstring
  • fromiter
  • fromfile
  • frombuffer
  • identity
  • loadtxt
  • genfromtxt
  • eye
  • tri

Bonus round

  • Once the list is completed, a What's New should be added to our next release notes

FWIW, I'm actively working towards completing the implementation for Quantity in time for astropy 7.0's feature freeze, and would like to tackle Masked too if time allows.

Footnotes

  1. yet ↩

Activity

  1. pllim commented on Oct 4, 2024

    @pllim
    Member

    Do we need a Wow Ambitious label? 😆

  2. neutrinoceros commented on Oct 9, 2024

    @neutrinoceros
    ContributorAuthor

    Now that the units part is done, we can focus the discussion on utils.masked.

    Reading through NEP 35, I think it would make sense that np.ones((16, 16), like=Masked(Quantity([], u.cm)) would work, but there's an additional layer of indirection that I'm not sure how to overcome. Namely, MaskedQuantity gets its __array_function__ implementation from Quantity, not Masked. I think this warrants adding tests to check that the behavior of NEP 35 functions is not completely bananas for astropy 7.0.0, but maybe not rush the implementation itself in time for the feature freeze (after all, I only proposed this out of love for consistency, but no one actually requested it).
    So, let me start the conversation with: do we want to support this ?

  3. neutrinoceros commented on Oct 10, 2024

    @neutrinoceros
    ContributorAuthor

    ping @mhvk just in case

  4. mhvk commented on Oct 10, 2024

    @mhvk
    Contributor

    Ah, mixed classes are an interesting bit... For the normal case, inside Quantity.__array_function__, we do .value for every quantity, and this value will be MaskedNDArray if we're dealing with a MaskedQuantity. This then gets passed on with a super() call to MaskedNDArray.__array_function__.

    I think it should work similarly for the NEP 35 functions, at least if they are covered with function_helper. If they are dispatched, I think one would have to pass on a like=self.value, which would be trickier. So, it may be that my suggestion to use function_helper ended up being quite lucky...

  5. neutrinoceros commented on Oct 10, 2024

    @neutrinoceros
    ContributorAuthor

    Awesome. Other crucial questions I want to ask upfront:
    Does it always make sense to enforce the same rules we defined for units propagation with masks ?
    1) I'm guessing falling back to the mask from the like argument always makes sense (but I'm happy to be shown wrong)
    2) for functions where other arguments may plausibly be masked arrays too, use their masks for the output instead ?
    3) what happens if we get np.asarray(np.ma.maskedarray([1], [True], like=Masked(np.array([], False)) ? (I'm asking because I know that numpy.ma masks behave very differently than ours) I'd expect this to still pick the mask from the input np.ma array, but convert it to Masked(np.ndarray), but I haven't thought deeply about it. Maybe we should not support such ill defined cases at all (at least for the time being ?) ?

  6. mhvk commented on Oct 10, 2024

    @mhvk
    Contributor

    My sense would be that it would be a bit different from Quantity, in that the mask of the like argument would never be involved, just like is currently the case for zeros_like, ones_like, etc. This also because like's shape would likely not have anything to do with the actual function. But a mask for the fill_value in full(..., like=m) would of course be used. So, in your example for asarray, I think one should get Masked([1], [True]).

  7. neutrinoceros commented on Oct 17, 2024

    @neutrinoceros
    ContributorAuthor

    Alright, we have ~8 days before the feature freeze and I have not started implementing the Masked counterpart. It may be still be doable on my end but I can't speak for my reviewer(s). So, before I dive in: @mhvk, assuming I can manage to do all 21 functions by Monday, do you think you'll be able to review by Friday ? If not, that's perfectly okay to me, there are plenty other things to be done, I'm just trying to optimize the time we have left.

  8. mhvk commented on Oct 17, 2024

    @mhvk
    Contributor

    Yes, I think I can review that. But I don't think this is particularly urgent and it is not an API change, so only if there aren't more important API affecting changes to do.

  9. neutrinoceros commented on Oct 18, 2024

    @neutrinoceros
    ContributorAuthor

    Upon reflection I think my time will be better spent trying to help pushing existing PRs accross the finish line. As you said, this is not urgent !

  10. neutrinoceros commented on Nov 12, 2024

    @neutrinoceros
    ContributorAuthor

    (hopefully) quick question for @mhvk : why is there currently no @function_helper decorator in utils.masked ?

  11. mhvk commented on Nov 12, 2024

    @mhvk
    Contributor

    (hopefully) quick question for @mhvk : why is there currently no @function_helper decorator in utils.masked ?

    Unlike Quantity, there weren't really many cases where one can just drop to ndarray, run the function, and stick a mask back on -- from a quick look, there were only a few, like iscomplex and unwrap, and it looks like I just decided to dispatch them instead. I guess the closest equivalent is @apply_to_both.

  12. neutrinoceros commented on Nov 13, 2024

    @neutrinoceros
    ContributorAuthor

    It looks to me that, if only for the sake of making comparison with the work we did on Quantity easier, it would be a good starting point to add such a similar decorator to masked first. Any objection ?

  13. mhvk commented on Nov 13, 2024

    @mhvk
    Contributor

    It looks to me that, if only for the sake of making comparison with the work we did on Quantity easier, it would be a good starting point to add such a similar decorator to masked first. Any objection ?

    No, no objection, though I'm still not quite sure @apply_to_both wouldn't be more appropriate for zeros/ones/empty/full_like (for arange, almost certainly dispatching is best).

  14. neutrinoceros commented on Nov 21, 2024

    @neutrinoceros
    ContributorAuthor

    @mhvk I see that numpy/numpy#27504 was just merged. Should we expect a subtle break in devdeps, or will this allow us to simplify what we did in units later on ?

  15. mhvk commented on Nov 21, 2024

    @mhvk
    Contributor

    @mhvk I see that numpy/numpy#27504 was just merged. Should we expect a subtle break in devdeps, or will this allow us to simplify what we did in units later on ?

    It just means that we can do super().__array_function__(...) without fear of breaking. Specifically, it will no longer be necessary to do:

    try:
    result = super().__array_function__(function, types, args, kwargs)
    except AttributeError as e:
    if "_implementation" not in str(e):
    raise
    # numpy bug, see https://github.com/numpy/numpy/issues/27500
    result = function(*args, **kwargs)

    I guess we can put a if NUMPY_LT_2_2 around the try/except, and in the else clause just call super() unconditionally (or add a comment in the try/except - these days, the construct has no cost if no exception is raised).

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions