
GITHUB . COM {
}
Detected CMS Systems:
- Wordpress (2 occurrences)
Title:
Decide on what the resolution rules for __op__/__rop__/__numpy_ufunc__ actually are Β· Issue #5844 Β· numpy/numpy
Description:
There is a complex set of questions around how to handle method resolution in the presence of __numpy_ufunc__. Currently in master is an extremely complicated set of rules that isn't documented...
Website Age:
17 years and 8 months (reg. 2007-10-09).
Matching Content Categories {π}
- Telecommunications
- Dating & Relationships
- Family & Parenting
Content Management System {π}
What CMS is github.com built with?
Github.com is built with WORDPRESS.
Traffic Estimate {π}
What is the average monthly size of github.com audience?
ππ Tremendous Traffic: 10M - 20M visitors per month
Based on our best estimate, this website will receive around 10,000,019 visitors per month in the current month.
However, some sources were not loaded, we suggest to reload the page to get complete results.
check SE Ranking
check Ahrefs
check Similarweb
check Ubersuggest
check Semrush
How Does Github.com Make Money? {πΈ}
Subscription Packages {π³}
We've located a dedicated page on github.com that might include details about subscription plans or recurring payments. We identified it based on the word pricing in one of its internal links. Below, you'll find additional estimates for its monthly recurring revenues.How Much Does Github.com Make? {π°}
Subscription Packages {π³}
Prices on github.com are in US Dollars ($).
They range from $4.00/month to $21.00/month.
We estimate that the site has approximately 4,989,889 paying customers.
The estimated monthly recurring revenue (MRR) is $20,957,532.
The estimated annual recurring revenues (ARR) are $251,490,385.
Wordpress Themes and Plugins {π¨}
What WordPress theme does this site use?
It is strange but we were not able to detect any theme on the page.
What WordPress plugins does this website use?
It is strange but we were not able to detect any plugins on the page.
Keywords {π}
numpyufunc, dispatch, binop, ndarray, ufunc, object, return, arraypriority, logic, dont, rules, class, mechanism, mhvk, numpy, python, array, problem, commented, layer, work, present, doesnt, arrays, pythons, subclass, notimplemented, make, method, call, behavior, subclasses, run, cases, scipysparse, machinery, stuff, system, fallback, check, things, problems, classes, member, ufuncs, operation, binops, arraylike, write, def,
Topics {βοΈ}
/numpy/numpy/blob/master/numpy/core/src/multiarray/number potentially triggering array_wrap place binop invoked multiple container elements personal information decide suitably written numpy incoherent nonsense afaict additional ordering mechanism actual ufunc result classes __numpy ufunc__ byzantine __array_priority__ route essentially identical system complicated dispatch systems provided inplace ops projects milestone actual dispatch rules receiving fallback handling ndarray default binop binop dispatch system wiki page comment metadata assignees standard mixin class handle method resolution enables overriding np standard ndarray methods normal python mechanism native binop mechanism implement binary operations ndarray dispatches immediately logically independent purpose binop dispatch mechanism binop/numpy_ufunc mechanisms standard ufunc stuff main alternative proposal ufunc-returns-notimplemented overridden __op__ methods extremely complicated set __array_priority__ deprecation warning including __numpy_ufunc__ resolution simpler dispatch logic type projects continue supporting classes ensure ndarray ufuncs ufunc stuff immediately current dispatch machinery solutions proposed cases binop dispatch __numpy_ufunc__ returns notimplemented numpy ufuncs distributed array class
Payment Methods {π}
- Braintree
Questions {β}
- Already have an account?
- But is it too simple?
- But what exactly makes the above prioritisation necessary?
- Could it be simpler than it is now?
- How should the dispatch logic work so that things make sense?
- Or am I missing something?
- Or am I missing something?
- The second criticism is this: what is the point in replacing Python's binop dispatch mechanism by a second layer of dispatch, which works in the same way?
- That we ran in the first layer?
- The same way?
- Things make sense?
Schema {πΊοΈ}
DiscussionForumPosting:
context:https://schema.org
headline:Decide on what the resolution rules for __op__/__rop__/__numpy_ufunc__ actually are
articleBody:There is a complex set of questions around how to handle method resolution in the presence of `__numpy_ufunc__`. Currently in master is an extremely complicated set of rules that isn't documented and that I don't actually understand (see #5748 for the latest set of changes to this), so it's kinda hard to know whether they are correct, but I suspect not. And this is a blocker for 1.10, b/c whatever we release in 1.10 will be set in stone forever.
I strongly feel that we cannot include `__numpy_ufunc__` in a release without at least having a document somewhere describing what the actual dispatch rules are. I hope that doesn't mean we have to defer `__numpy_ufunc__` for another release, but if it does then it does.
AFAICT this is how `a op b` dispatch works for ndarrays, BEFORE `__numpy_ufunc__` (i.e., this is how 1.9 works):
- First Python uses the subclass rule to decide whether to invoke `a.__op__(b)` or `b.__rop__(a)`. So in the case where one of these objects is a proper subclass of the other, that object always gets to do absolutely anything, so that's fine. The interesting cases are the ones where neither is a proper subclass of the other (either because it's like, `matrix + masked array`, or because it's like `ndarray + scipy.sparse`). So without loss of generality, let's focus on the case where Python calls `a.__op__(b)`, and `a` is either an instance of `ndarray` or else an instance of a subclass of `ndarray` which has not overridden `__op__`, i.e. we're getting `ndarray.__op__(a, b)`.
- `ndarray.__op__` has the following logic (see `PyArray_GenericBinaryFunction` in `number.c`):
- If `b` is not an `ndarray` at all (even a subclass), and `b` has a higher `__array_priority__` than `a`, then we return `NotImplemented` and let control pass to `b.__rop__(a)`.
- Otherwise, we call `np.op(a, b)` and let the ufunc machinery take over.
- `np.op(a, b)` does the following (see `PyUFunc_GenericFunction`, `PyUFunc_GeneralizedFunction`, in `ufunc_object.c`, and also `ufunc_generic_call` which converts `-2` return values from the previous into `NotImplemented` so you have to audit their whole call stack):
- If `b` is not an `ndarray`, and calling `np.array(b)` returns an object array (presumably because coercion failed... though I guess this could also be hit if `b.__array__()` return an object array or something), AND `b` has a higher `__array_priority__` than `a`, and `b` has an `__rop__` method, then return `NotImplemented`.
- If any of our arrays contain structured dtypes or strings, and there are no special struct ufunc loops registered, but not if any of our arrays contain objects, then return `NotImplemented`. (This is buried in `get_ufunc_arguments`, search for `return -2`.)
- Otherwise we return the actual ufunc result.
Now, my suggestion is that the way we would EVENTUALLY like this to look is:
- First, Python uses the subclass rule to decide whether to invoke `a.__op__(b)` or `b.__rop__(a)`. As above, let's assume that it invokes `ndarray.__op__(a, b)`.
- `ndarray.__op__(a, b)` calls `np.op(a, b)` (which in turn invokes all the standard ufunc stuff, including `__numpy_ufunc__` resolution).
- There is no step 3.
I submit that it is obvious that IF we can make this work, then it is obviously the ideal outcome, because it is the simplest possible solution. But is it too simple? To determine this we have to answer two questions: (1) Will it adequately address all the relevant use cases? (2) Can we get there from here?
So let's compare the current rules to my dream rules.
First, we observe that everything that currently happens inside the ufunc machinery looks like it's totally wrong. The first check can only be triggered if `b` is a non-`ndarray` that has a higher `__array_priority__` (among other things), but if we look above, we see that those conditions are sufficient to trigger the check in `ndarray.__op__`, so checking again at the ufunc level is redundant at best. And the second check is just incoherent nonsense AFAICT. The only reason to return `NotImplemented` is b/c you want to pass control to another `__(r)op__` method, and there's no reason arrays containing structured dtypes in particular should somehow magically have different `__(r)op__` methods available than other arrays. So we can just get rid of all the ufunc stuff immediately, great.
That leaves the `__array_priority__` stuff. We have two problems here: we can't just drop this immediately b/c of backcompat issues, and we need to have some way to continue to support all the use cases that this currently supports. The first problem is just a matter of having a deprecation period. For the second, observe that a class which defines a `__numpy_ufunc__` method gets complete control over what any ufunc call does, so it has _almost_ as much power as a class that currently sets `__array_priority__`. The only additional power that `__array_priority__` currently gives you is that it lets you distinguish between e.g. a call to `ndarray.__add(a, b)` versus a call to `np.add(a, b)`. So the only code that really loses out from my proposed change is code which wants `a + b` and `add(a, b)` to do different things.
AFAIK in the entire history of numpy there is only one situation where this power has been used on purpose: the definition of matrix classes where `a * b` is matmul, but `np.multiply(a, b)` is elmul. And we've all agreed that such classes should be deprecated and eventually phased out (cite: PEP 465).
So, I conclude that EVENTUALLY my dream rules should work great. The only problem is that we need some temporary compromises to get us from here to there. Therefore, I propose we use the following dispatch rules in numpy 1.10, with the goal of moving to my "dream rules" in some future version:
- First, Python uses the subclass rule to decide whether to invoke `a.__op__(b)` or `b.__rop__(a)`. As above, let's assume that it invokes `ndarray.__op__(a, b)`.
- `ndarray.__op__(a, b)` does the following:
- If `b` **does not define `__numpy_ufunc__` and** is not an `ndarray` at all (even a subclass), and `b` has a higher `__array_priority__` than `a`, then we **issue a deprecation warning and** return `NotImplemented` and let control pass to `b.__rop__(a)`. (bolded parts are changes compared to the current behaviour)
- If `__op__` is `__mul__` and `b->tp_class->tp_name.startswith("scipy.sparse.")`, then return `NotImplemented`. (This rule is necessary in addition to the above, because `scipy.sparse` has already made a release containing `__numpy_ufunc__` methods, so the exception above doesn't apply.)
- Otherwise, we call `np.op(a, b)` and let the ufunc machinery take over.
I believe that this is adequate to covers all practical use cases for the current dispatch machinery, and gives us a clean path to better dispatch machinery in the future.
The main alternative proposal is Pauli's, which involves a very complicated check (I won't try to summarize here, see [this comment and following code](https://github.com/numpy/numpy/blob/eecb2e3c07f29c0ac991d364a846a2f8293a432a/numpy/core/src/multiarray/number.c#L103)). The goal of that approach is to continue supporting classes where `a + b` and `add(a, b)` do different things. I don't think that keeping substantial additional complexity around indefinitely is worth it in order to support functionality that no-one has ever found a use for except in one very specific case (overriding `__mul__`), and where we generally agree that that one specific case should be phased out as possible.
I would very much appreciate feedback from scipy.sparse and astropy in particular on whether the above covers all their concerns.
(Partial) History: #4815, #5748
CC: @pv, @cowlicks, @mhvk
author:
url:https://github.com/njsmith
type:Person
name:njsmith
datePublished:2015-05-06T08:09:28.000Z
interactionStatistic:
type:InteractionCounter
interactionType:https://schema.org/CommentAction
userInteractionCount:328
url:https://github.com/5844/numpy/issues/5844
context:https://schema.org
headline:Decide on what the resolution rules for __op__/__rop__/__numpy_ufunc__ actually are
articleBody:There is a complex set of questions around how to handle method resolution in the presence of `__numpy_ufunc__`. Currently in master is an extremely complicated set of rules that isn't documented and that I don't actually understand (see #5748 for the latest set of changes to this), so it's kinda hard to know whether they are correct, but I suspect not. And this is a blocker for 1.10, b/c whatever we release in 1.10 will be set in stone forever.
I strongly feel that we cannot include `__numpy_ufunc__` in a release without at least having a document somewhere describing what the actual dispatch rules are. I hope that doesn't mean we have to defer `__numpy_ufunc__` for another release, but if it does then it does.
AFAICT this is how `a op b` dispatch works for ndarrays, BEFORE `__numpy_ufunc__` (i.e., this is how 1.9 works):
- First Python uses the subclass rule to decide whether to invoke `a.__op__(b)` or `b.__rop__(a)`. So in the case where one of these objects is a proper subclass of the other, that object always gets to do absolutely anything, so that's fine. The interesting cases are the ones where neither is a proper subclass of the other (either because it's like, `matrix + masked array`, or because it's like `ndarray + scipy.sparse`). So without loss of generality, let's focus on the case where Python calls `a.__op__(b)`, and `a` is either an instance of `ndarray` or else an instance of a subclass of `ndarray` which has not overridden `__op__`, i.e. we're getting `ndarray.__op__(a, b)`.
- `ndarray.__op__` has the following logic (see `PyArray_GenericBinaryFunction` in `number.c`):
- If `b` is not an `ndarray` at all (even a subclass), and `b` has a higher `__array_priority__` than `a`, then we return `NotImplemented` and let control pass to `b.__rop__(a)`.
- Otherwise, we call `np.op(a, b)` and let the ufunc machinery take over.
- `np.op(a, b)` does the following (see `PyUFunc_GenericFunction`, `PyUFunc_GeneralizedFunction`, in `ufunc_object.c`, and also `ufunc_generic_call` which converts `-2` return values from the previous into `NotImplemented` so you have to audit their whole call stack):
- If `b` is not an `ndarray`, and calling `np.array(b)` returns an object array (presumably because coercion failed... though I guess this could also be hit if `b.__array__()` return an object array or something), AND `b` has a higher `__array_priority__` than `a`, and `b` has an `__rop__` method, then return `NotImplemented`.
- If any of our arrays contain structured dtypes or strings, and there are no special struct ufunc loops registered, but not if any of our arrays contain objects, then return `NotImplemented`. (This is buried in `get_ufunc_arguments`, search for `return -2`.)
- Otherwise we return the actual ufunc result.
Now, my suggestion is that the way we would EVENTUALLY like this to look is:
- First, Python uses the subclass rule to decide whether to invoke `a.__op__(b)` or `b.__rop__(a)`. As above, let's assume that it invokes `ndarray.__op__(a, b)`.
- `ndarray.__op__(a, b)` calls `np.op(a, b)` (which in turn invokes all the standard ufunc stuff, including `__numpy_ufunc__` resolution).
- There is no step 3.
I submit that it is obvious that IF we can make this work, then it is obviously the ideal outcome, because it is the simplest possible solution. But is it too simple? To determine this we have to answer two questions: (1) Will it adequately address all the relevant use cases? (2) Can we get there from here?
So let's compare the current rules to my dream rules.
First, we observe that everything that currently happens inside the ufunc machinery looks like it's totally wrong. The first check can only be triggered if `b` is a non-`ndarray` that has a higher `__array_priority__` (among other things), but if we look above, we see that those conditions are sufficient to trigger the check in `ndarray.__op__`, so checking again at the ufunc level is redundant at best. And the second check is just incoherent nonsense AFAICT. The only reason to return `NotImplemented` is b/c you want to pass control to another `__(r)op__` method, and there's no reason arrays containing structured dtypes in particular should somehow magically have different `__(r)op__` methods available than other arrays. So we can just get rid of all the ufunc stuff immediately, great.
That leaves the `__array_priority__` stuff. We have two problems here: we can't just drop this immediately b/c of backcompat issues, and we need to have some way to continue to support all the use cases that this currently supports. The first problem is just a matter of having a deprecation period. For the second, observe that a class which defines a `__numpy_ufunc__` method gets complete control over what any ufunc call does, so it has _almost_ as much power as a class that currently sets `__array_priority__`. The only additional power that `__array_priority__` currently gives you is that it lets you distinguish between e.g. a call to `ndarray.__add(a, b)` versus a call to `np.add(a, b)`. So the only code that really loses out from my proposed change is code which wants `a + b` and `add(a, b)` to do different things.
AFAIK in the entire history of numpy there is only one situation where this power has been used on purpose: the definition of matrix classes where `a * b` is matmul, but `np.multiply(a, b)` is elmul. And we've all agreed that such classes should be deprecated and eventually phased out (cite: PEP 465).
So, I conclude that EVENTUALLY my dream rules should work great. The only problem is that we need some temporary compromises to get us from here to there. Therefore, I propose we use the following dispatch rules in numpy 1.10, with the goal of moving to my "dream rules" in some future version:
- First, Python uses the subclass rule to decide whether to invoke `a.__op__(b)` or `b.__rop__(a)`. As above, let's assume that it invokes `ndarray.__op__(a, b)`.
- `ndarray.__op__(a, b)` does the following:
- If `b` **does not define `__numpy_ufunc__` and** is not an `ndarray` at all (even a subclass), and `b` has a higher `__array_priority__` than `a`, then we **issue a deprecation warning and** return `NotImplemented` and let control pass to `b.__rop__(a)`. (bolded parts are changes compared to the current behaviour)
- If `__op__` is `__mul__` and `b->tp_class->tp_name.startswith("scipy.sparse.")`, then return `NotImplemented`. (This rule is necessary in addition to the above, because `scipy.sparse` has already made a release containing `__numpy_ufunc__` methods, so the exception above doesn't apply.)
- Otherwise, we call `np.op(a, b)` and let the ufunc machinery take over.
I believe that this is adequate to covers all practical use cases for the current dispatch machinery, and gives us a clean path to better dispatch machinery in the future.
The main alternative proposal is Pauli's, which involves a very complicated check (I won't try to summarize here, see [this comment and following code](https://github.com/numpy/numpy/blob/eecb2e3c07f29c0ac991d364a846a2f8293a432a/numpy/core/src/multiarray/number.c#L103)). The goal of that approach is to continue supporting classes where `a + b` and `add(a, b)` do different things. I don't think that keeping substantial additional complexity around indefinitely is worth it in order to support functionality that no-one has ever found a use for except in one very specific case (overriding `__mul__`), and where we generally agree that that one specific case should be phased out as possible.
I would very much appreciate feedback from scipy.sparse and astropy in particular on whether the above covers all their concerns.
(Partial) History: #4815, #5748
CC: @pv, @cowlicks, @mhvk
author:
url:https://github.com/njsmith
type:Person
name:njsmith
datePublished:2015-05-06T08:09:28.000Z
interactionStatistic:
type:InteractionCounter
interactionType:https://schema.org/CommentAction
userInteractionCount:328
url:https://github.com/5844/numpy/issues/5844
Person:
url:https://github.com/njsmith
name:njsmith
url:https://github.com/njsmith
name:njsmith
InteractionCounter:
interactionType:https://schema.org/CommentAction
userInteractionCount:328
interactionType:https://schema.org/CommentAction
userInteractionCount:328
External Links {π}(2)
Analytics and Tracking {π}
- Site Verification - Google
Libraries {π}
- Clipboard.js
- D3.js
- Lodash
Emails and Hosting {βοΈ}
Mail Servers:
- aspmx.l.google.com
- alt1.aspmx.l.google.com
- alt2.aspmx.l.google.com
- alt3.aspmx.l.google.com
- alt4.aspmx.l.google.com
Name Servers:
- dns1.p08.nsone.net
- dns2.p08.nsone.net
- dns3.p08.nsone.net
- dns4.p08.nsone.net
- ns-1283.awsdns-32.org
- ns-1707.awsdns-21.co.uk
- ns-421.awsdns-52.com
- ns-520.awsdns-01.net