Skip to content

ENH: initial implementation of core __array_function__ machinery #12005

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 13 commits into from
Sep 24, 2018

Conversation

shoyer
Copy link
Member

@shoyer shoyer commented Sep 21, 2018

I'd like to implement NEP-18 in a multi-step process:

  1. (This PR) Pure Python implementation of __array_function__ machinery,
    based on the prototype implementation from NEP-18.
  2. Rewrite this machinery in C as needed to improve performance.
  3. Implement overrides on NumPy functions.

Steps 2 and 3 should be able to happen in parallel (and with other people
contributing!).

This PR still needs more tests, especially for ndarray.__array_function__

I'd like to implement NEP-18 in a multi-step process:
1. (This PR) Pure Python implementation of `__array_function__` machinery,
   based on the prototype implementation from NEP-18.
2. Rewrite this machinery in C as needed to improve performance.
3. Implement overrides on NumPy functions.

Steps 2 and 3 should be able to happen in parallel (and with other people
contributing!).

This PR still needs more tests, especially for `ndarray.__array_function__`
@shoyer
Copy link
Member Author

shoyer commented Sep 21, 2018

OK, I think this has decent test coverage now and is ready for review.

@shoyer
Copy link
Member Author

shoyer commented Sep 21, 2018

This test is failing on Python 2.7 with Shippable:

________________________ test_suppress_warnings_module _________________________
[gw1] linux2 -- Python 2.7.12 /root/venv/2.7/bin/python

    def test_suppress_warnings_module():
        # Initial state of module, no warnings
        my_mod = _get_fresh_mod()
        assert_equal(getattr(my_mod, '__warningregistry__', {}), {})
    
        def warn_other_module():
            # Apply along axis is implemented in python; stacklevel=2 means
            # we end up inside its module, not ours.
            def warn(arr):
                warnings.warn("Some warning 2", stacklevel=2)
                return arr
            np.apply_along_axis(warn, 0, [0])
    
        # Test module based warning suppression:
>       assert_warn_len_equal(my_mod, 0)

It looks unrelated.

@tylerjereddy
Copy link
Contributor

tylerjereddy commented Sep 21, 2018

It looks unrelated.

Correct, the warnings tests are not thread-safe & we run the tests with more than 1 core on shippable CI; I can't remember how much extra time it takes to run the suite on a single core, but that one test will fail stochastically so we should maybe roll back to 1 core, which is a bit of a sin since we get 96 cores on those ARM nodes.

@tylerjereddy
Copy link
Contributor

tylerjereddy commented Sep 21, 2018

Maybe we could xfail on some hook related to n_cores > 1 for pytest run for that particular test.

for detecting master vs. "child" process with a fixture: pytest-dev/pytest-xdist#47 (comment)

if result is not NotImplemented:
return True, result

raise TypeError('no implementation found for {} on types that implement '
Copy link
Member

Choose a reason for hiding this comment

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

Is it the case that at least one of the non-ndarray types with override must implement the function?

Copy link
Member Author

Choose a reason for hiding this comment

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

Not necessarily. For example, a project like scipy.sparse might define a starter __array_function__ that returns (almost) always returns NotImplemented, just as a way to guarantee that scipy matrices don't get inadvertently coerced into numpy arrays.

(But it occurs to me that we don't have a test for this yet, which I should fix!)

@charris
Copy link
Member

charris commented Sep 23, 2018

Could you add a release note with a short explanation and a link to the NEP.

@shoyer
Copy link
Member Author

shoyer commented Sep 23, 2018 via email

@charris
Copy link
Member

charris commented Sep 23, 2018

but I think it's a little early for that.

I believe there is already some experimental support out there. Was it @mhvk? Might get together and pick a function.

Is it intended that ndarray methods can be overridden?

@shoyer
Copy link
Member Author

shoyer commented Sep 23, 2018

Eventually I'd like the release note to read "Every core NumPy function now supports __array_function__." But there clearly not true now :).

@shoyer
Copy link
Member Author

shoyer commented Sep 23, 2018

Is it intended that ndarray methods can be overridden?

No, this is only for functions.

@shoyer
Copy link
Member Author

shoyer commented Sep 24, 2018

The "subclasses before superclasses" rule coupled with the "special handling for ndarray" rule makes the logic for picking types to call pretty tricky.

In particular, in which order should we try overrides for arguments with types [ndarray, duck_array, ndarray_subclass]? Per the text in the NEP, I suspect we should call ndarray_subclass.__array_function__ before duck_array.__array_function__ because we say we mention "subclasses before superclasses" before "special handling for ndarray", but honestly it's a little ambiguous.

@mhvk
Copy link
Contributor

mhvk commented Sep 24, 2018

@shoyer - One way of thinking about it is that ndarray actually does not get special treatment - the only reason we skip it in this case is because we already know it will return NotImplemented. I.e., our logic is actually quite simple, but we allow shortcuts.

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.

Looks very nice! Some small comments only.

@shoyer
Copy link
Member Author

shoyer commented Sep 24, 2018

One way of thinking about it is that ndarray actually does not get special treatment - the only reason we skip it in this case is because we already know it will return NotImplemented. I.e., our logic is actually quite simple, but we allow shortcuts.

Yes, this helps.

@mhvk
Copy link
Contributor

mhvk commented Sep 24, 2018

@shoyer - most of the diff now seems to be against benchmarks - surely not the intent!

@shoyer
Copy link
Member Author

shoyer commented Sep 24, 2018

I'll split removing the Benchmark baseclass into another PR.

@shoyer
Copy link
Member Author

shoyer commented Sep 24, 2018

I removed the unrelated changes to the benchmark suite.

@mhvk
Copy link
Contributor

mhvk commented Sep 24, 2018

This update has made the code quite a bit clearer; the different split between the actual override call is particularly helpful.
Is it time to start trying this out, e.g., on concatenate?

@mattip
Copy link
Member

mattip commented Sep 24, 2018

+1 to merge this as-is as step 1, and to add a tracking issue to follow up with the other two goals in the first comment

@shoyer
Copy link
Member Author

shoyer commented Sep 24, 2018

see #12028 for a tracking issue

@charris
Copy link
Member

charris commented Sep 24, 2018

Let's get this started. @shoyer Might want to break the tracking issue down into smaller bits. It would also be good to have some idea of how to gather feedback, and maybe some small demo project (masked arrays, TNG?) or existing project as a test platform. Also might consider long term possibility of JIT for python code, that is, how much does a C implementation actually buy us.

@charris
Copy link
Member

charris commented Sep 24, 2018

Thanks Stephan.

@shoyer shoyer deleted the nep-18-initial branch September 24, 2018 23:19
@charris charris changed the title ENH: initial implementation of core __array_function__ machinery ENH: initial implementation of core __array_function__ machinery Nov 10, 2018
@rgommers rgommers added this to the 1.17.0 release milestone Jun 27, 2019
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.

8 participants