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
Investigate CuPy after NEP-18 support #4462
Comments
This should work after my PR cupy/cupy#2026 I know the example given in the description is just a reference, but since some corrections are necessary, below is a fully functional sample (after the PR mentioned above is merged): import cupy
x = cupy.random.random((5000, 1000))
import dask.array as da
d = da.from_array(x, chunks=(1000, 1000), asarray=False)
x.sum()
x.mean()
(x[:1000] + x[:1000].T).sum(axis=1)
u, s, v = da.linalg.svd(d)
s.compute() |
That's pretty exciting! I'll add a couple comments to the CuPy PR. Some follow ups:
|
A, whoops. @pentschev mentioned in side-channels that I probably meant |
Currently, Traceback (most recent call last):
File "dask_ex.py", line 9, in <module>
d.mean().compute()
File "/home/nfs/pentschev/.local/lib/python3.5/site-packages/dask/base.py", line 156, in compute
(result,) = compute(self, traverse=False, **kwargs)
File "/home/nfs/pentschev/.local/lib/python3.5/site-packages/dask/base.py", line 398, in compute
results = schedule(dsk, keys, **kwargs)
File "/home/nfs/pentschev/.local/lib/python3.5/site-packages/dask/threaded.py", line 76, in get
pack_exception=pack_exception, **kwargs)
File "/home/nfs/pentschev/.local/lib/python3.5/site-packages/dask/local.py", line 459, in get_async
raise_exception(exc, tb)
File "/home/nfs/pentschev/.local/lib/python3.5/site-packages/dask/compatibility.py", line 112, in reraise
raise exc
File "/home/nfs/pentschev/.local/lib/python3.5/site-packages/dask/local.py", line 230, in execute_task
result = _execute_task(task, data)
File "/home/nfs/pentschev/.local/lib/python3.5/site-packages/dask/core.py", line 119, in _execute_task
return func(*args2)
File "/home/nfs/pentschev/.local/lib/python3.5/site-packages/dask/optimization.py", line 942, in __call__
dict(zip(self.inkeys, args)))
File "/home/nfs/pentschev/.local/lib/python3.5/site-packages/dask/core.py", line 149, in get
result = _execute_task(task, cache)
File "/home/nfs/pentschev/.local/lib/python3.5/site-packages/dask/core.py", line 119, in _execute_task
return func(*args2)
File "/home/nfs/pentschev/.local/lib/python3.5/site-packages/dask/compatibility.py", line 93, in apply
return func(*args, **kwargs)
File "/home/nfs/pentschev/.local/lib/python3.5/site-packages/dask/array/reductions.py", line 336, in mean_chunk
result['n'] = n
ValueError: object __array__ method not producing an array |
So the relevant function is here: def mean_chunk(x, sum=chunk.sum, numel=numel, dtype='f8', **kwargs):
n = numel(x, dtype=dtype, **kwargs)
total = sum(x, dtype=dtype, **kwargs)
empty = empty_lookup.dispatch(type(n))
result = empty(n.shape, dtype=[('total', total.dtype), ('n', n.dtype)])
result['n'] = n
result['total'] = total
return result It looks like we have our own custom In principle, we probably want to use However this requires some finesse because we still need to support non-NEP-18 enabled installs (which are currently the majority). I'm not yet sure what the right approach is here yet. |
@mrocklin thanks for the detailed description. I will try to dive a bit into the code and see if I can suggest a fix that covers both cases. Is it safe to assume that existing tests cover the pre-NEP-18 case? |
You should make sure that you also have the
|
I also wouldn't dive too deeply into the |
Here is a minimal example with dask-glm, which I think serves as a good general application here. import numpy as np
import dask.array as da
X = np.random.random((1000, 10)) # ideally we swap this out with cupy
dX = da.from_array(X, chunks=(100, 10))
y = np.ones(1000) # ideally we swap this out with cupy
dy = da.from_array(y, chunks=(100,))
import dask_glm.algorithms
dask_glm.algorithms.admm(dX, dy, max_iter=5)
dask_glm.algorithms.proximal_grad(dX, dy, max_iter=5) |
After NEP-18, Dask will be capable of working on arrays from NumPy-like libraries, e.g., CuPy. For such unimplemented methods, such as TypeError: no implementation found for 'numpy.take_along_axis' on types that implement __array_function__: [<class 'cupy.core.core.ndarray'>] I think a discussion on handling this case is important for the near future. |
I see two general approaches to situations like these:
|
Please, don't get too attached to the example I presented, it was randomly chosen. Implementing all missing functions is probably out of question, there's hundreds of them: https://docs-cupy.chainer.org/en/stable/reference/comparison.html |
Understood. The two approaches above are probably the same two approaches for other cases as well. If it's easy to add to cupy then lets do it. Otherwise lets see if we can easily avoid the operation in Dask Array. Either approach is probably slightly useful outside of this endeavor as well. |
From my latest bug hunt I've found some new issues, starting with those directly related to lack of fully NumPy-compliant implementations in CuPy:
For the two issues mentioned above, the options I see for now are:
Please note that I'm mentioning CuPy only here because that's what I've been working on mostly, but all these issues will eventually happen when we want to work with other NumPy-like libraries. Also, all issues mentioned above are not an extensive list, just the ones I've found so far, they will most likely happen for other functions too. @shoyer @jakirkham this may interest you. |
On a more general note, for me personally I think it may be helpful to set milestones for Dask's NEP-18 support. At first, it seemed that there wouldn't be so many issues, but unfortunately we have many due to lack of features that Dask implements and CuPy does not, as well as various bugs in both libraries. My suggestion is that we come up with subsets of functions (probably ordered by importance) and tackle them by that order, to prevent us from working only on least important issues and forgetting in the short-term about the important ones. Since I'm relatively new to Dask, I don't think I'm qualified to come up with such a list, so any input here is very welcome. |
In a side-channel conversation about trying to get dask-glm working, @pentschev brought up cases like the following, where we create an array of zeros of a type similar to an input array, but with a new shape (n, p) = X.shape
...
z = np.zeros(p) In this particular case in dask-glm it probably doesn't matter, because In general though, how do we handle this? One approach would be to get a numpy-like module out of an array module = get_module(input_array)
x = module.zeros(shape=(...)) In this way we still watch the dispatchability of the |
What about adding a |
I would much rather get started on a "back-end" NEP, similar to @mrocklin's suggestion. Dispatching creation functions and other API calls that do not take in an array are a recurring pattern... I'd prefer a NEP to playing whack-a-mole with issues that pop up with them. |
When looking at the dask-glm solvers in particular, I suspect that admm, bfgs, and proximal-grad are likely the most performant. |
After reading through PR ( #4543 ), was thinking about whether we might be able to get a rough Tried to come up with something on the plane and made this. import numpy
def resize(arr, shape):
if type(arr) is numpy.ma.masked_array:
# NumPy Masked Arrays error resize method
out = numpy.ma.resize(arr, shape)
else:
out = arr.copy()
out.resize(shape)
return out
def zeros_like(prototype, shape=None, dtype=None):
# Standardize input arguments
dtype = (dtype or prototype.dtype)
shape = (shape or prototype.shape)
# Create an empty array
empty = resize(prototype, prototype.ndim * (0,))
# Generate a new array from the empty array
arr = empty.astype(dtype)
arr = resize(arr, shape)
return arr This works on NumPy arrays, NumPy masked arrays, and SciPy sparse arrays. Did not have Sparse or CuPy at the time. So hadn't tested those. Now that I'm looking at them, they both lack Maybe we could add |
@jakirkham supposing there was a If doing this is acceptable until we get |
That's a fair complaint. Here's why the
If we didn't care about 1, we could replace this with |
@jakirkham sorry, maybe I wasn't clear with what I meant. I do understand why |
In other words: I agree what you suggested would be a nice workaround if we could avoid this, potentially huge, overhead of data duplication. |
No worries. Just wanted to make sure we were on the same page. If we go the It's worth thinking about. Let me know if you have any ideas. ;) |
As to the specializations, I think we didn't want to go down that road for a permanent solution. Maybe we could reconsider for a temporary one? |
I was attempting to avoid this even as a temporary one, but if that's acceptable, we could go down that road. There's no need to make it a permanent solution anyway, I believe that once we can get |
The following example is failing for me with CuPy 6.1.0 and Master branch of Dask: import cupy
x = cupy.random.random((5000, 1000))
import dask.array as da
d = da.from_array(x, chunks=(1000, 1000), asarray=False)
u, s, v = da.linalg.svd(d)
s.compute() The error I am getting is: |
You need NumPy 1.16.x and environment variable |
@system123 does setting the environment variable |
Just got around to testing it and it is working after setting the env variable. |
Great, glad to hear it. Thanks for the fix @pentschev |
I think this is largely supported now and we can close. If there are edge cases we should open new issues and resolve |
Building off of #3007 it would be good to further investigate CuPy, especially after they added support for NEP-18 in cupy/cupy#1650 .
To investigate this further, I recommend the following steps
Install cupy with that change installed (depending on when the last release was, this might only be in development versions)
install dask
Allocate a cupy array of random data
Split that array into a dask array
Start trying out Numpy syntax, and see what breaks
Probably after this we will learn about many flaws either in Dask Array, CuPy, or both.
cc @zronaghi @pentschev
The text was updated successfully, but these errors were encountered: