-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Blockwise array creation redux #7417
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
Changes from all commits
1bc7556
2f1cafd
4732153
9c11f5a
ea1ff6e
9cffa5e
ef89354
3685f9a
b7ad844
5cee0ec
b733296
e991b0f
83a2744
cd17f94
707d41e
1e4a232
716097d
0cf7881
b555e2e
5c4a1bd
1e9b49a
10fa918
3d4f291
80f9d04
7003df9
96fe5c6
f45ff24
e92f014
4887543
7ac4ff6
bd5ac4f
9b324f8
25f5108
b09ac1e
5e933cc
fc530eb
ef42b08
5ed22e0
fe4449c
04405e2
69002e0
c6243e7
cdb2536
8899811
a3a5476
ea1e777
44b9ac9
cc7c2ce
f6bf935
28d5d4b
947aa5c
1330bcc
474eff4
91de1d6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -40,12 +40,13 @@ | |
| persist, | ||
| tokenize, | ||
| ) | ||
| from ..blockwise import blockwise as core_blockwise | ||
| from ..blockwise import broadcast_dimensions | ||
| from ..context import globalmethod | ||
| from ..core import quote | ||
| from ..delayed import Delayed, delayed | ||
| from ..highlevelgraph import HighLevelGraph | ||
| from ..layers import reshapelist | ||
| from ..highlevelgraph import HighLevelGraph, MaterializedLayer | ||
| from ..layers import ArraySliceDep, reshapelist | ||
| from ..sizeof import sizeof | ||
| from ..utils import ( | ||
| IndexCallable, | ||
|
|
@@ -232,46 +233,88 @@ def slices_from_chunks(chunks): | |
| return list(product(*slices)) | ||
|
|
||
|
|
||
| def getem( | ||
| arr, | ||
| def graph_from_arraylike( | ||
| arr, # Any array-like which supports slicing | ||
|
ian-r-rose marked this conversation as resolved.
|
||
| chunks, | ||
| shape, | ||
| name, | ||
| getitem=getter, | ||
| shape=None, | ||
| out_name=None, | ||
| lock=False, | ||
| asarray=True, | ||
| dtype=None, | ||
| ): | ||
| """Dask getting various chunks from an array-like | ||
|
|
||
| >>> getem('X', chunks=(2, 3), shape=(4, 6)) # doctest: +SKIP | ||
| {('X', 0, 0): (getter, 'X', (slice(0, 2), slice(0, 3))), | ||
| ('X', 1, 0): (getter, 'X', (slice(2, 4), slice(0, 3))), | ||
| ('X', 1, 1): (getter, 'X', (slice(2, 4), slice(3, 6))), | ||
| ('X', 0, 1): (getter, 'X', (slice(0, 2), slice(3, 6)))} | ||
|
|
||
| >>> getem('X', chunks=((2, 2), (3, 3))) # doctest: +SKIP | ||
| {('X', 0, 0): (getter, 'X', (slice(0, 2), slice(0, 3))), | ||
| ('X', 1, 0): (getter, 'X', (slice(2, 4), slice(0, 3))), | ||
| ('X', 1, 1): (getter, 'X', (slice(2, 4), slice(3, 6))), | ||
| ('X', 0, 1): (getter, 'X', (slice(0, 2), slice(3, 6)))} | ||
| inline_array=False, | ||
| ) -> HighLevelGraph: | ||
| """ | ||
| HighLevelGraph for slicing chunks from an array-like according to a chunk pattern. | ||
|
|
||
| If ``inline_array`` is True, this make a Blockwise layer of slicing tasks where the | ||
| array-like is embedded into every task., | ||
|
|
||
| If ``inline_array`` is False, this inserts the array-like as a standalone value in | ||
| a MaterializedLayer, then generates a Blockwise layer of slicing tasks that refer | ||
| to it. | ||
|
|
||
| >>> dict(graph_from_arraylike(arr, chunks=(2, 3), shape=(4, 6), name="X", inline_array=True)) # doctest: +SKIP | ||
| {(arr, 0, 0): (getter, arr, (slice(0, 2), slice(0, 3))), | ||
| (arr, 1, 0): (getter, arr, (slice(2, 4), slice(0, 3))), | ||
| (arr, 1, 1): (getter, arr, (slice(2, 4), slice(3, 6))), | ||
| (arr, 0, 1): (getter, arr, (slice(0, 2), slice(3, 6)))} | ||
|
|
||
| >>> dict( # doctest: +SKIP | ||
| graph_from_arraylike(arr, chunks=((2, 2), (3, 3)), shape=(4,6), name="X", inline_array=False) | ||
| ) | ||
| {"original-X": arr, | ||
| ('X', 0, 0): (getter, 'original-X', (slice(0, 2), slice(0, 3))), | ||
| ('X', 1, 0): (getter, 'original-X', (slice(2, 4), slice(0, 3))), | ||
| ('X', 1, 1): (getter, 'original-X', (slice(2, 4), slice(3, 6))), | ||
| ('X', 0, 1): (getter, 'original-X', (slice(0, 2), slice(3, 6)))} | ||
| """ | ||
| out_name = out_name or arr | ||
| chunks = normalize_chunks(chunks, shape, dtype=dtype) | ||
| keys = product([out_name], *(range(len(bds)) for bds in chunks)) | ||
| slices = slices_from_chunks(chunks) | ||
| out_ind = tuple(range(len(shape))) | ||
|
|
||
| if ( | ||
| has_keyword(getitem, "asarray") | ||
| and has_keyword(getitem, "lock") | ||
| and (not asarray or lock) | ||
| ): | ||
| values = [(getitem, arr, x, asarray, lock) for x in slices] | ||
| getter = partial(getitem, asarray=asarray, lock=lock) | ||
| else: | ||
| # Common case, drop extra parameters | ||
| values = [(getitem, arr, x) for x in slices] | ||
| getter = getitem | ||
|
|
||
| if inline_array: | ||
| layer = core_blockwise( | ||
| getter, | ||
| name, | ||
| out_ind, | ||
| arr, | ||
| None, | ||
| ArraySliceDep(chunks), | ||
| out_ind, | ||
| numblocks={}, | ||
| ) | ||
| return HighLevelGraph.from_collections(name, layer) | ||
| else: | ||
|
ian-r-rose marked this conversation as resolved.
|
||
| original_name = "original-" + name | ||
|
|
||
| return dict(zip(keys, values)) | ||
| layers = {} | ||
| layers[original_name] = MaterializedLayer({original_name: arr}) | ||
| layers[name] = core_blockwise( | ||
| getter, | ||
| name, | ||
| out_ind, | ||
| original_name, | ||
| None, | ||
| ArraySliceDep(chunks), | ||
| out_ind, | ||
| numblocks={}, | ||
| ) | ||
|
ian-r-rose marked this conversation as resolved.
|
||
|
|
||
| deps = { | ||
| original_name: set(), | ||
| name: {original_name}, | ||
| } | ||
| return HighLevelGraph(layers, deps) | ||
|
|
||
|
|
||
| def dotmany(A, B, leftfunc=None, rightfunc=None, **kwargs): | ||
|
|
@@ -3233,13 +3276,10 @@ def from_array( | |
| ) | ||
|
|
||
| if name in (None, True): | ||
| token = tokenize(x, chunks) | ||
| original_name = "array-original-" + token | ||
| token = tokenize(x, chunks, lock, asarray, fancy, getitem, inline_array) | ||
| name = name or "array-" + token | ||
| elif name is False: | ||
| original_name = name = "array-" + str(uuid.uuid1()) | ||
| else: | ||
| original_name = name | ||
| name = "array-" + str(uuid.uuid1()) | ||
|
|
||
| if lock is True: | ||
| lock = SerializableLock() | ||
|
|
@@ -3266,23 +3306,17 @@ def from_array( | |
| else: | ||
|
ian-r-rose marked this conversation as resolved.
|
||
| getitem = getter_nofancy | ||
|
|
||
| if inline_array: | ||
| get_from = x | ||
| else: | ||
| get_from = original_name | ||
|
|
||
| dsk = getem( | ||
| get_from, | ||
| dsk = graph_from_arraylike( | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @rjzamora wanted to flag this as where we'll have to think through some of the array inlining logic.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Got it. So, the original code allows you to include the array in the graph once (with a dedicated key), or to inline it in every task. So far, this PR is effectively inlining the array in every task by including it in the IO function (in It seems like you could support
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yes, you have this exactly right -- I'm planning to re-introduce non-inlining, but haven't tackled it yet. For the case of lazy arrays like Zarr, or small ones, this should already do what we want. |
||
| x, | ||
| chunks, | ||
| x.shape, | ||
| name, | ||
| getitem=getitem, | ||
| shape=x.shape, | ||
| out_name=name, | ||
| lock=lock, | ||
| asarray=asarray, | ||
| dtype=x.dtype, | ||
| inline_array=inline_array, | ||
| ) | ||
| if not inline_array: | ||
| dsk[original_name] = x | ||
|
|
||
| # Workaround for TileDB, its indexing is 1-based, | ||
| # and doesn't seems to support 0-length slicing | ||
|
|
@@ -4278,7 +4312,7 @@ def asarray( | |
| return from_array(a, getitem=getter_inline, **kwargs) | ||
|
|
||
|
|
||
| def asanyarray(a, dtype=None, order=None, *, like=None): | ||
| def asanyarray(a, dtype=None, order=None, *, like=None, inline_array=False): | ||
| """Convert the input to a dask array. | ||
|
|
||
| Subclasses of ``np.ndarray`` will be passed through as chunks unchanged. | ||
|
|
@@ -4305,6 +4339,9 @@ def asanyarray(a, dtype=None, order=None, *, like=None): | |
| argument. If ``like`` is a Dask array, the chunk type of the | ||
| resulting array will be defined by the chunk type of ``like``. | ||
| Requires NumPy 1.20.0 or higher. | ||
| inline_array: | ||
| Whether to inline the array in the resulting dask graph. For more information, | ||
| see the documentation for ``dask.array.from_array()``. | ||
|
|
||
| Returns | ||
| ------- | ||
|
|
@@ -4343,7 +4380,13 @@ def asanyarray(a, dtype=None, order=None, *, like=None): | |
| return a.map_blocks(np.asanyarray, like=like_meta, dtype=dtype, order=order) | ||
| else: | ||
| a = np.asanyarray(a, like=like_meta, dtype=dtype, order=order) | ||
| return from_array(a, chunks=a.shape, getitem=getter_inline, asarray=False) | ||
| return from_array( | ||
| a, | ||
| chunks=a.shape, | ||
| getitem=getter_inline, | ||
| asarray=False, | ||
| inline_array=inline_array, | ||
| ) | ||
|
|
||
|
|
||
| def is_scalar_for_elemwise(arg): | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function certainly seems to be private/internal, but perhaps we should get confirmation that it isn't being used directly by down-stream libraries (like xarray) before removing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A quick search seems to indicate it is unused in xarray. Are there other projects we should check with as well?
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the Dask org there are a couple old blogposts that reference this
getemfunction. Not seeing any other references in the org. Idk if people are still looking at blogposts that far back or using them anywherehttps://blog.dask.org/2014/12/27/Towards-OOC
https://blog.dask.org/2014/12/30/Towards-OOC-Frontend
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't remove it, but renamed it
graph_from_arraylike, as something a bit more descriptive. I'd be happy to roll that back in the interests of backwards compatibility (thought the signature of the function will have also changed to be HLG-ified, so the backwards compatibility ship may have sailed)Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's pretty straightforward to add a note to old blogposts telling people things have changed in more recent versions of Dask. We could do that if you want.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi, this broke Vaex, in case anyone reads this, this was my compatibility fix: