You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Thanks for leading the development of sparse.
I'm looking forward to see it in xarray:)
Currently, our logic to support dask.array and numpy.ndarray is hard-coded everywhere.
For example, we have many computation paths for nansum, dask.Array, np.ndarray with bottleneck, bare np.ndarray and we use our in-house implementation for object-type arrays.
The easiest way to support sparse might be to add a specific path for sparse by hard-coding again,
but it is less flexible.
Do we need to be capable of supporting other objects for future extension?
If so, we may need to start from (heavy) refactoring.
@shoyer,
Could you give any suggestion?
I am personally interested in helping this, but I may need to decide the direction first.
Do we need to be capable of supporting other objects for future extension?
If so, we may need to start from (heavy) refactoring.
For two array backends, it didn't make sense to write an abstraction layer for this, in part because it wasn't clear what we needed. But for three examples, it probably does -- that's the point where shared use cases become clear. Undoubtedly, there will be other cases in the future where users will want to extend xarray to handle new array types (arrays with units come to mind).
For implementing these overloads/functions, there are various possible solutions. Our current ad-hoc system is similar to what @hameerabbasi suggests -- we check the type of the first argument and use that to dispatch to an appropriate function. This has the advantage of being easy to implement for a known set of types, but a single dispatch order is not very extensible -- it's impossible to anticipate every third-party class. Recently, NumPy has moved away from this (e.g., with __array_ufunc__).
One appealing option is to make use of @mrocklin's multipledispatch library, which was originally developed for Blaze and is still in active use. Possible concerns:
Performance. Import times need to be fast, and I know this is something that multipledispatch can sometimes struggle with. My guess is that this wouldn't be a problem for us, since we can rely on other dispatch mechanisms most operations (including __array_ufunc__ and Python's builtin arithmetic overrides).
Dispatch for stack/concatenate: How do we handle dispatching for functions that take a list of arrays? e.g., if a list of arrays has contains any dask arrays, we need to use dask. Ideally, we would resolve the type of an object like [np.array(...), np.array(...), ..., da.Array(...)] to a mixed type like List[Union[np.ndarray, da.Array]], for which an override could be implemented.
Dispatch for the first argument(s) only: This is a minor point, but some functions don't need to be dispatched on all of their arguments, e.g., sum() only really needs to dispatch on the array types but can pass other arguments like axis directly on. I suppose could simply annotate extra position arguments with object, but this will get annoying for multiple optional arguments which would all need separate implementations (if I understand multipledispatch correctly).
Import times on multipledispatch have improved thanks to work by @llllllllll . They could probably be further improved if people wanted to invest modest intellectual effort here. Costs scale with the number of type signatures on each operation. In blaze this was very high, well into the hundreds, in our case it would be, I think, more modest around 2-10. (also, historical note, multipledispatch predates my involvement in Blaze).
When possible it would be useful to upstream these concerns to NumPy, even if we have to move faster than NumPy is able to support.
Dispatch for stack/concatenate is definitely on the radar for NumPy development, but I don't know when it's actually going to happen. The likely interface is something like __array_ufunc__: a special method like __array_concatenate__ is called on each element in the list, until one does not return NotImplemented. This is a different style of overloads than multipledispatch, one that is slightly simpler to implement but possibly slower and with fewer guarantees of correctness.
We only need this for a couple of operations, so in any case we can probably implement our own ad-hoc dispatch system for np.stack and np.concatenate, either along the of multipledispatch or NumPy/__array_ufunc__.
On further contemplation, overloading based on union types with a system like multipledispatch does seem tricky. It's not clear to me that there's even a well defined type for inputs to concatenate that should be dispatched to dask vs. numpy, for example. We want to let that dask handle any cases where at least one input is a dask array, but a type like List[Union[np.ndarray, da.Array]] actually matches a list of all numpy arrays, too -- unless we require an exact match for the type.
In blaze we have variadic sequences for multiple dispatch, and the List[Union] case is something we have run into. We have a type called VarArgs which takes a variadic sequence of type-arguments and represents a sequence of a unions over the arguments, for example: VarArgs[pd.Series, pd.DataFrame] is a sequence of unknown length which is known to hold either series or dataframes. With some mild metaprogramming we made it so that VarArs[pd.Series] is a subclass of VarArgs[pd.Series, pd.DataFrame], or in general, more specific sequences are subclasses of more general sequences. This means that you can solve the ambiguity by registering a dispatch for VarArgs[np.ndarray]andVarArgs[np.ndarray, da.Array] and you know that the second function can only be called if the sequence holds at least one dask array.
You could hide this behind a top-level function that wraps the input for the user, or register a dispatch for list which boxes and recurses into itself.
We could make a particular list an instance of a particular TypedVarArgs; however, multiple dispatch uses the type() of arguments as well as issubclass to do dispatching. Multiple dispatch depends on being able to partially order types to make dispatching more efficient. The constructor of VarArgs scans for the types of the elements and constructs an instance of a new (but memoized) subclass of VarArgs which encodes the element types so that issubclass works as expected. The problem is that type([1.0, 'foo']) returns just list which erases all information about the elements.
As for my last concern, "Dispatch for the first argument(s) only" it looks like the simple answer is that multipledispatch already only dispatches based on positional arguments. So as long as we're strict about using keyword arguments for extra parameters like axis (which is good style anyways), we only need a single overload per array type for single dispatch functions like sum().
It looks like this resolves almost all of my concerns about using multiple dispatch.
One thing that would be nice is it VarArgs is actually distributed as part of multipledispatch rather than needing to be copied separately into xarray. That would make it easier for third parties to extend our operations, by simply importing VarArgs from multipledispatch rather than importing it from somewhere internal in xarray.
How about something like checking inside a list if something is top priority, then call a, if second priority, call b, etc.
Usually, this is not a good idea. The problem is that it's impossible to know a global priority order across unrelated packages. It's usually better to declare valid type matches explicitly.
NumPy tried this with __array_priority__, but in practice these priority numbers are basically meaningless for all comparisons other than comparisons to the priority of NumPy arrays.
On Thu, Apr 19, 2018 at 4:23 PM, Stephan Hoyer ***@***.***> wrote:
This library would have hard dependencies only on numpy and
multipledispatch, and would expose a multipledispatch namespace so
extending it doesn't have to happen in the library itself.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#1938 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AASszI-z2bvzo597NWGzF0E9J486VBbHks5tqPJNgaJpZM4SQsHy>
.
On Fri, Apr 20, 2018 at 9:59 AM, Matthew Rocklin ***@***.***> wrote:
What name should we go with? I have a slight preference for duckarray
over arrayish but happy with whatever the group decides.
On Fri, Apr 20, 2018 at 1:51 AM, Hameer Abbasi ***@***.***>
wrote:
> I've created one, as per your e-mail: https://github.com/hameerabbas
> i/arrayish
>
> —
> You are receiving this because you were mentioned.
> Reply to this email directly, view it on GitHub
> <#1938 (comment)>,
> or mute the thread
> <https://github.com/notifications/unsubscribe-auth/AASszJ0A1I96lO8uHy4rO2Oj_35znavlks5tqXdJgaJpZM4SQsHy>
> .
>
I've written it up and already released version 0.0.1 on PyPI, except concatenate and stack (which need TypedSequence). I can still change the name, but I'd rather not.
Also, import duckarray as da conflicts with import dask.array as da.
Let's move this discussion over to hameerabbasi/arrayish#1. But, in summary, I got the impression that the community in general is unhappy with the name "duck arrays".
I am sitting in the SciPy talk about CuPy. Would be great if someone could give us an update on how this issue stands before tomorrow's xarray sprint.
Someone my want to try plugging CuPy arrays into xarray. But this issue doesn't really resolve the best way to do that. As far as I can tell @hameerabbasi's "arrayish" project was deprecated in favor of uarray / unumpy.
What is the best path forward as of today, July 12, 2019?
Internally, we need to use NumPy functions directly (if __array_function__ is enabled) instead of our current Dask/NumPy versions. Fortunately, pretty much all this logic lives in one place, in xarray.core.duck_array_ops.
We'll need to think a little bit about indexing in particular. Right now we have special indexing wrappers for NumPy arrays and Dask arrays; we would need to decide how to handle arbitrary array objects (probably by indexing them like NumPy arrays?). Basic indexing should work either way, but indexing with arrays can be a little tricky since few duck-array types support NumPy's full semantics (which are pretty complex).
Activity
fujiisoup commentedon Feb 23, 2018
Thanks for leading the development of sparse.
I'm looking forward to see it in xarray:)
Currently, our logic to support
dask.arrayandnumpy.ndarrayis hard-coded everywhere.For example, we have many computation paths for
nansum,dask.Array,np.ndarraywithbottleneck, barenp.ndarrayand we use our in-house implementation for object-type arrays.The easiest way to support
sparsemight be to add a specific path forsparseby hard-coding again,but it is less flexible.
Do we need to be capable of supporting other objects for future extension?
If so, we may need to start from (heavy) refactoring.
@shoyer,
Could you give any suggestion?
I am personally interested in helping this, but I may need to decide the direction first.
hameerabbasi commentedon Feb 23, 2018
Then I would suggest something like the following for hooks (omitting imports):
And then, in code, call the appropriate
nansuminstead ofnp.nansum:If you need help, I'd be willing to give it. :-) But I'm not a user of XArray, so I don't really understand the use-cases or codebase.
shoyer commentedon Feb 23, 2018
For two array backends, it didn't make sense to write an abstraction layer for this, in part because it wasn't clear what we needed. But for three examples, it probably does -- that's the point where shared use cases become clear. Undoubtedly, there will be other cases in the future where users will want to extend xarray to handle new array types (arrays with units come to mind).
For implementing these overloads/functions, there are various possible solutions. Our current ad-hoc system is similar to what @hameerabbasi suggests -- we check the type of the first argument and use that to dispatch to an appropriate function. This has the advantage of being easy to implement for a known set of types, but a single dispatch order is not very extensible -- it's impossible to anticipate every third-party class. Recently, NumPy has moved away from this (e.g., with
__array_ufunc__).One appealing option is to make use of @mrocklin's multipledispatch library, which was originally developed for Blaze and is still in active use. Possible concerns:
multipledispatchcan sometimes struggle with. My guess is that this wouldn't be a problem for us, since we can rely on other dispatch mechanisms most operations (including__array_ufunc__and Python's builtin arithmetic overrides).stack/concatenate: How do we handle dispatching for functions that take a list of arrays? e.g., if a list of arrays has contains any dask arrays, we need to use dask. Ideally, we would resolve the type of an object like[np.array(...), np.array(...), ..., da.Array(...)]to a mixed type likeList[Union[np.ndarray, da.Array]], for which an override could be implemented.sum()only really needs to dispatch on the array types but can pass other arguments likeaxisdirectly on. I suppose could simply annotate extra position arguments withobject, but this will get annoying for multiple optional arguments which would all need separate implementations (if I understand multipledispatch correctly).mrocklin commentedon Feb 23, 2018
Import times on multipledispatch have improved thanks to work by @llllllllll . They could probably be further improved if people wanted to invest modest intellectual effort here. Costs scale with the number of type signatures on each operation. In blaze this was very high, well into the hundreds, in our case it would be, I think, more modest around 2-10. (also, historical note, multipledispatch predates my involvement in Blaze).
When possible it would be useful to upstream these concerns to NumPy, even if we have to move faster than NumPy is able to support.
shoyer commentedon Feb 23, 2018
Dispatch for stack/concatenate is definitely on the radar for NumPy development, but I don't know when it's actually going to happen. The likely interface is something like
__array_ufunc__: a special method like__array_concatenate__is called on each element in the list, until one does not return NotImplemented. This is a different style of overloads than multipledispatch, one that is slightly simpler to implement but possibly slower and with fewer guarantees of correctness.We only need this for a couple of operations, so in any case we can probably implement our own ad-hoc dispatch system for
np.stackandnp.concatenate, either along the of multipledispatch or NumPy/__array_ufunc__.On further contemplation, overloading based on union types with a system like multipledispatch does seem tricky. It's not clear to me that there's even a well defined type for inputs to concatenate that should be dispatched to dask vs. numpy, for example. We want to let that dask handle any cases where at least one input is a dask array, but a type like
List[Union[np.ndarray, da.Array]]actually matches a list of all numpy arrays, too -- unless we require an exact match for the type.llllllllll commentedon Feb 23, 2018
In blaze we have variadic sequences for multiple dispatch, and the
List[Union]case is something we have run into. We have a type calledVarArgswhich takes a variadic sequence of type-arguments and represents a sequence of a unions over the arguments, for example:VarArgs[pd.Series, pd.DataFrame]is a sequence of unknown length which is known to hold either series or dataframes. With some mild metaprogramming we made it so thatVarArs[pd.Series]is a subclass ofVarArgs[pd.Series, pd.DataFrame], or in general, more specific sequences are subclasses of more general sequences. This means that you can solve the ambiguity by registering a dispatch forVarArgs[np.ndarray]andVarArgs[np.ndarray, da.Array]and you know that the second function can only be called if the sequence holds at least one dask array.Here is an example of what that looks like for
merge, which is concat(axis=1): https://github.com/blaze/blaze/blob/master/blaze/compute/pandas.py#L691This is the definition of
VarArgs: https://github.com/blaze/blaze/blob/master/blaze/compute/varargs.pyshoyer commentedon Feb 23, 2018
@llllllllll very cool! Is there a special trick I need to use this? I tried:
This gives me an error when I try to use it:
llllllllll commentedon Feb 23, 2018
VarArgsitself is actually a type, so you need to create instances which wrap the list argument, for example:You could hide this behind a top-level function that wraps the input for the user, or register a dispatch for list which boxes and recurses into itself.
hameerabbasi commentedon Feb 23, 2018
Can't some wild metaprogramming make it so that
[1.0, 'foo']itself is an instance ofVarArgs[float, str](or be converted?)llllllllll commentedon Feb 23, 2018
We could make a particular list an instance of a particular
TypedVarArgs; however, multiple dispatch uses thetype()of arguments as well asissubclassto do dispatching. Multiple dispatch depends on being able to partially order types to make dispatching more efficient. The constructor ofVarArgsscans for the types of the elements and constructs an instance of a new (but memoized) subclass ofVarArgswhich encodes the element types so thatissubclassworks as expected. The problem is thattype([1.0, 'foo'])returns justlistwhich erases all information about the elements.llllllllll commentedon Feb 23, 2018
The wrapping dispatch would just look like:
hameerabbasi commentedon Feb 23, 2018
How about something like checking inside a list if something is top priority, then call
a, if second priority, callb, etc.shoyer commentedon Feb 23, 2018
Yes, I just tested out the wrapping dispatch. It works and is quite clean.
shoyer commentedon Feb 23, 2018
As for my last concern, "Dispatch for the first argument(s) only" it looks like the simple answer is that multipledispatch already only dispatches based on positional arguments. So as long as we're strict about using keyword arguments for extra parameters like
axis(which is good style anyways), we only need a single overload per array type for single dispatch functions likesum().It looks like this resolves almost all of my concerns about using multiple dispatch.
One thing that would be nice is it
VarArgsis actually distributed as part of multipledispatch rather than needing to be copied separately into xarray. That would make it easier for third parties to extend our operations, by simply importingVarArgsfrom multipledispatch rather than importing it from somewhere internal in xarray.shoyer commentedon Feb 23, 2018
Usually, this is not a good idea. The problem is that it's impossible to know a global priority order across unrelated packages. It's usually better to declare valid type matches explicitly.
NumPy tried this with
__array_priority__, but in practice these priority numbers are basically meaningless for all comparisons other than comparisons to the priority of NumPy arrays.26 remaining items
mrocklin commentedon Apr 19, 2018
shoyer commentedon Apr 20, 2018
I like
duckarraya little better without the underscore.Should we go ahead and start
pydata/duckarray? Or is it better to incubate in somebody's personal repo?hameerabbasi commentedon Apr 20, 2018
I've created one, as per your e-mail: https://github.com/hameerabbasi/arrayish
The name is inspired from a recent discussion about this on the Numpy mailing list.
mrocklin commentedon Apr 20, 2018
mrocklin commentedon Apr 20, 2018
hameerabbasi commentedon Apr 20, 2018
I've written it up and already released version 0.0.1 on PyPI, except
concatenateandstack(which needTypedSequence). I can still change the name, but I'd rather not.Also,
import duckarray as daconflicts withimport dask.array as da.mrocklin commentedon Apr 20, 2018
Thanks for taking the initiative here @hameerabbasi ! It's good to see something up already.
Here is a link to the discussion that I think @hameerabbasi is referring to: http://numpy-discussion.10968.n7.nabble.com/new-NEP-np-AbstractArray-and-np-asabstractarray-tt45282.html#none
I haven't read through that entirely yet, was arrayish decided on by the community or was the term still up for discussion?
hameerabbasi commentedon Apr 20, 2018
Let's move this discussion over to hameerabbasi/arrayish#1. But, in summary, I got the impression that the community in general is unhappy with the name "duck arrays".
rabernat commentedon Jul 12, 2019
I am sitting in the SciPy talk about CuPy. Would be great if someone could give us an update on how this issue stands before tomorrow's xarray sprint.
Someone my want to try plugging CuPy arrays into xarray. But this issue doesn't really resolve the best way to do that. As far as I can tell @hameerabbasi's "arrayish" project was deprecated in favor of uarray / unumpy.
What is the best path forward as of today, July 12, 2019?
hameerabbasi commentedon Jul 12, 2019
uarray/unumpyis shaping up nicely. 😄rabernat commentedon Jul 12, 2019
@hameerabbasi - are you at SciPy by any chance?
mrocklin commentedon Jul 12, 2019
@jacobtomlinson got things sorta-working with NEP-18 and CuPy in an afternoon in Iris (with a strong emphasis on "kinda").
On the CuPy side you're fine. If you're on NumPy 1.16 you'll need to enable the
__array_function__interface with the following environment variable:If you're using Numpy 1.17 then this is on by default.
I think that most of the work here is on the Xarray side. We'll need to remove things like explicit type checks.
hameerabbasi commentedon Jul 12, 2019
@rabernat I can attend remotely.
shoyer commentedon Jul 12, 2019
We're at the point where this could be hacked together pretty quickly:
ndarrayunless required #2956). Checking for an__array_function__attribute is probably a good heuristic for duck arrays (it's what dask is using).__array_function__is enabled) instead of our current Dask/NumPy versions. Fortunately, pretty much all this logic lives in one place, inxarray.core.duck_array_ops.