dask-array 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (144) hide show
  1. dask_array/__init__.py +228 -0
  2. dask_array/_backends.py +76 -0
  3. dask_array/_backends_array.py +99 -0
  4. dask_array/_blockwise.py +1410 -0
  5. dask_array/_broadcast.py +272 -0
  6. dask_array/_chunk.py +445 -0
  7. dask_array/_chunk_types.py +54 -0
  8. dask_array/_collection.py +1644 -0
  9. dask_array/_concatenate.py +331 -0
  10. dask_array/_core_utils.py +1365 -0
  11. dask_array/_dispatch.py +141 -0
  12. dask_array/_einsum.py +277 -0
  13. dask_array/_expr.py +544 -0
  14. dask_array/_expr_flow.py +586 -0
  15. dask_array/_gufunc.py +805 -0
  16. dask_array/_histogram.py +617 -0
  17. dask_array/_map_blocks.py +652 -0
  18. dask_array/_new_collection.py +10 -0
  19. dask_array/_numpy_compat.py +135 -0
  20. dask_array/_overlap.py +1159 -0
  21. dask_array/_rechunk.py +1050 -0
  22. dask_array/_reshape.py +710 -0
  23. dask_array/_routines.py +102 -0
  24. dask_array/_shuffle.py +448 -0
  25. dask_array/_stack.py +264 -0
  26. dask_array/_svg.py +291 -0
  27. dask_array/_templates.py +29 -0
  28. dask_array/_test_utils.py +257 -0
  29. dask_array/_ufunc.py +385 -0
  30. dask_array/_utils.py +349 -0
  31. dask_array/_visualize.py +223 -0
  32. dask_array/_xarray.py +337 -0
  33. dask_array/core/__init__.py +34 -0
  34. dask_array/core/_blockwise_funcs.py +312 -0
  35. dask_array/core/_conversion.py +422 -0
  36. dask_array/core/_from_graph.py +97 -0
  37. dask_array/creation/__init__.py +71 -0
  38. dask_array/creation/_arange.py +121 -0
  39. dask_array/creation/_diag.py +116 -0
  40. dask_array/creation/_diagonal.py +241 -0
  41. dask_array/creation/_eye.py +103 -0
  42. dask_array/creation/_linspace.py +102 -0
  43. dask_array/creation/_mesh.py +134 -0
  44. dask_array/creation/_ones_zeros.py +454 -0
  45. dask_array/creation/_pad.py +270 -0
  46. dask_array/creation/_repeat.py +55 -0
  47. dask_array/creation/_tile.py +36 -0
  48. dask_array/creation/_tri.py +28 -0
  49. dask_array/creation/_utils.py +296 -0
  50. dask_array/fft.py +320 -0
  51. dask_array/io/__init__.py +39 -0
  52. dask_array/io/_base.py +10 -0
  53. dask_array/io/_from_array.py +257 -0
  54. dask_array/io/_from_delayed.py +95 -0
  55. dask_array/io/_from_graph.py +54 -0
  56. dask_array/io/_from_npy_stack.py +67 -0
  57. dask_array/io/_store.py +336 -0
  58. dask_array/io/_tiledb.py +159 -0
  59. dask_array/io/_to_npy_stack.py +65 -0
  60. dask_array/io/_zarr.py +449 -0
  61. dask_array/linalg/__init__.py +39 -0
  62. dask_array/linalg/_cholesky.py +234 -0
  63. dask_array/linalg/_lu.py +300 -0
  64. dask_array/linalg/_norm.py +94 -0
  65. dask_array/linalg/_qr.py +601 -0
  66. dask_array/linalg/_solve.py +349 -0
  67. dask_array/linalg/_svd.py +394 -0
  68. dask_array/linalg/_tensordot.py +334 -0
  69. dask_array/linalg/_utils.py +74 -0
  70. dask_array/manipulation/__init__.py +45 -0
  71. dask_array/manipulation/_expand.py +321 -0
  72. dask_array/manipulation/_flip.py +92 -0
  73. dask_array/manipulation/_roll.py +78 -0
  74. dask_array/manipulation/_transpose.py +309 -0
  75. dask_array/random/__init__.py +125 -0
  76. dask_array/random/_choice.py +181 -0
  77. dask_array/random/_expr.py +256 -0
  78. dask_array/random/_generator.py +441 -0
  79. dask_array/random/_random_state.py +259 -0
  80. dask_array/random/_utils.py +84 -0
  81. dask_array/reductions/__init__.py +84 -0
  82. dask_array/reductions/_arg_reduction.py +130 -0
  83. dask_array/reductions/_common.py +1082 -0
  84. dask_array/reductions/_cumulative.py +522 -0
  85. dask_array/reductions/_percentile.py +261 -0
  86. dask_array/reductions/_reduction.py +725 -0
  87. dask_array/reductions/_trace.py +56 -0
  88. dask_array/routines/__init__.py +133 -0
  89. dask_array/routines/_apply.py +84 -0
  90. dask_array/routines/_bincount.py +112 -0
  91. dask_array/routines/_broadcast.py +111 -0
  92. dask_array/routines/_coarsen.py +115 -0
  93. dask_array/routines/_diff.py +79 -0
  94. dask_array/routines/_gradient.py +158 -0
  95. dask_array/routines/_indexing.py +65 -0
  96. dask_array/routines/_insert_delete.py +132 -0
  97. dask_array/routines/_misc.py +122 -0
  98. dask_array/routines/_nonzero.py +72 -0
  99. dask_array/routines/_search.py +123 -0
  100. dask_array/routines/_select.py +113 -0
  101. dask_array/routines/_statistics.py +171 -0
  102. dask_array/routines/_topk.py +82 -0
  103. dask_array/routines/_triangular.py +74 -0
  104. dask_array/routines/_unique.py +232 -0
  105. dask_array/routines/_where.py +62 -0
  106. dask_array/slicing/__init__.py +67 -0
  107. dask_array/slicing/_basic.py +550 -0
  108. dask_array/slicing/_blocks.py +138 -0
  109. dask_array/slicing/_bool_index.py +145 -0
  110. dask_array/slicing/_setitem.py +329 -0
  111. dask_array/slicing/_squeeze.py +101 -0
  112. dask_array/slicing/_utils.py +1133 -0
  113. dask_array/slicing/_vindex.py +282 -0
  114. dask_array/stacking/__init__.py +15 -0
  115. dask_array/stacking/_block.py +83 -0
  116. dask_array/stacking/_simple.py +58 -0
  117. dask_array/templates/array.html.j2 +48 -0
  118. dask_array/tests/__init__.py +0 -0
  119. dask_array/tests/conftest.py +22 -0
  120. dask_array/tests/test_api.py +40 -0
  121. dask_array/tests/test_binary_op_chunks.py +107 -0
  122. dask_array/tests/test_coarse_slice_through_blockwise.py +362 -0
  123. dask_array/tests/test_collection.py +799 -0
  124. dask_array/tests/test_creation.py +1102 -0
  125. dask_array/tests/test_expr_flow.py +143 -0
  126. dask_array/tests/test_linalg.py +1130 -0
  127. dask_array/tests/test_map_blocks_multi_output.py +104 -0
  128. dask_array/tests/test_rechunk_pushdown.py +214 -0
  129. dask_array/tests/test_reductions.py +1091 -0
  130. dask_array/tests/test_routines.py +2853 -0
  131. dask_array/tests/test_shuffle_chunks.py +67 -0
  132. dask_array/tests/test_slice_pushdown.py +968 -0
  133. dask_array/tests/test_slice_through_blockwise.py +678 -0
  134. dask_array/tests/test_slice_through_overlap.py +366 -0
  135. dask_array/tests/test_slice_through_reshape.py +272 -0
  136. dask_array/tests/test_slicing.py +839 -0
  137. dask_array/tests/test_transpose_slice_pushdown.py +208 -0
  138. dask_array/tests/test_visualize.py +94 -0
  139. dask_array/tests/test_xarray.py +193 -0
  140. dask_array-0.1.0.dist-info/METADATA +48 -0
  141. dask_array-0.1.0.dist-info/RECORD +144 -0
  142. dask_array-0.1.0.dist-info/WHEEL +4 -0
  143. dask_array-0.1.0.dist-info/entry_points.txt +2 -0
  144. dask_array-0.1.0.dist-info/licenses/LICENSE +29 -0
@@ -0,0 +1,522 @@
1
+ from __future__ import annotations
2
+
3
+ import math
4
+ import operator
5
+ from functools import partial
6
+ from itertools import product
7
+
8
+ import numpy as np
9
+
10
+ from dask_array._new_collection import new_collection
11
+
12
+
13
+ # Local implementations of merge/scan functions (copied from dask.array.reductions)
14
+ def _cumsum_merge(a, b):
15
+ if isinstance(a, np.ma.masked_array) or isinstance(b, np.ma.masked_array):
16
+ values = np.ma.getdata(a) + np.ma.getdata(b)
17
+ return np.ma.masked_array(values, mask=np.ma.getmaskarray(b))
18
+ return a + b
19
+
20
+
21
+ def _cumprod_merge(a, b):
22
+ if isinstance(a, np.ma.masked_array) or isinstance(b, np.ma.masked_array):
23
+ values = np.ma.getdata(a) * np.ma.getdata(b)
24
+ return np.ma.masked_array(values, mask=np.ma.getmaskarray(b))
25
+ return a * b
26
+
27
+
28
+ def _prefixscan_first(func, x, axis, dtype):
29
+ """Compute the prefix scan (e.g., cumsum) on the first block."""
30
+ return func(x, axis=axis, dtype=dtype)
31
+
32
+
33
+ def _prefixscan_combine(func, binop, pre, x, axis, dtype):
34
+ """Combine results of a parallel prefix scan such as cumsum.
35
+
36
+ Parameters
37
+ ----------
38
+ func : callable
39
+ Cumulative function (e.g. ``np.cumsum``)
40
+ binop : callable
41
+ Associative function (e.g. ``add``)
42
+ pre : np.array
43
+ The value calculated in parallel from ``preop``.
44
+ For example, the sum of all the previous blocks.
45
+ x : np.array
46
+ Current block
47
+ axis : int
48
+ dtype : dtype
49
+
50
+ Returns
51
+ -------
52
+ np.array
53
+ """
54
+ return binop(pre, func(x, axis=axis, dtype=dtype))
55
+
56
+
57
+ from dask_array._expr import ArrayExpr
58
+ from dask_array._utils import validate_axis
59
+ from dask.tokenize import _tokenize_deterministic
60
+ from dask.utils import cached_property, funcname
61
+
62
+
63
+ def _prepare_cumulative(x, axis):
64
+ """Prepare array for cumulative reduction.
65
+
66
+ When axis=None, flatten and rechunk the array to a 1D array with
67
+ npartitions chunks, then set axis=0.
68
+
69
+ Returns (array, axis) tuple.
70
+ """
71
+ from dask_array._collection import Array
72
+
73
+ if not isinstance(x, Array):
74
+ from dask_array.core._conversion import asarray
75
+
76
+ x = asarray(x)
77
+
78
+ if axis is None:
79
+ if x.ndim > 1:
80
+ x = x.flatten().rechunk(chunks=x.npartitions)
81
+ axis = 0
82
+
83
+ return x, axis
84
+
85
+
86
+ class CumReduction(ArrayExpr):
87
+ """Expression for cumulative reductions (cumsum, cumprod, etc.).
88
+
89
+ Uses the sequential algorithm: apply the cumulative function to each block,
90
+ then combine blocks by adding the last element of previous blocks.
91
+ """
92
+
93
+ _parameters = ["array", "func", "binop", "ident", "axis", "_dtype"]
94
+ _defaults = {"_dtype": None}
95
+
96
+ @cached_property
97
+ def _name(self):
98
+ return f"{funcname(self.func)}-{_tokenize_deterministic(*self.operands)}"
99
+
100
+ @cached_property
101
+ def dtype(self):
102
+ if self._dtype is not None:
103
+ return np.dtype(self._dtype)
104
+ # Infer dtype from the function
105
+ return getattr(self.func(np.ones((0,), dtype=self.array.dtype)), "dtype", object)
106
+
107
+ @cached_property
108
+ def _meta(self):
109
+ # Return meta with the correct dtype
110
+ meta = self.array._meta
111
+ if hasattr(meta, "dtype") and meta.dtype != self.dtype:
112
+ return meta.astype(self.dtype)
113
+ return meta
114
+
115
+ @cached_property
116
+ def chunks(self):
117
+ return self.array.chunks
118
+
119
+ def _layer(self):
120
+ from functools import partial
121
+
122
+ from dask.utils import apply
123
+
124
+ x = self.array
125
+ axis = self.axis
126
+ func = self.func
127
+ binop = self.binop
128
+ ident = self.ident
129
+ dtype = self.dtype
130
+
131
+ # Apply cumulative function to each block
132
+ # We'll use a two-phase approach:
133
+ # 1. First, apply the cumulative function to each block (via map_blocks expression)
134
+ # 2. Then, build the correction tasks that add previous block totals
135
+
136
+ dsk = {}
137
+
138
+ # Phase 1: Apply cumulative function per block
139
+ # We create intermediate keys for the per-block cumulative results
140
+ per_block_name = self._name + "-chunk"
141
+
142
+ # Determine if we need to pass dtype to the function
143
+ use_dtype = False
144
+ try:
145
+ import inspect
146
+
147
+ func_params = inspect.signature(func).parameters
148
+ use_dtype = "dtype" in func_params
149
+ except ValueError:
150
+ try:
151
+ # Workaround for numpy ufunc.accumulate
152
+ if isinstance(func.__self__, np.ufunc) and func.__name__ == "accumulate":
153
+ use_dtype = True
154
+ except AttributeError:
155
+ pass
156
+
157
+ # Create per-block cumulative tasks
158
+ for key in product(*map(range, x.numblocks)):
159
+ if use_dtype:
160
+ dsk[(per_block_name,) + key] = (
161
+ partial(func, axis=axis, dtype=dtype),
162
+ (x.name,) + key,
163
+ )
164
+ else:
165
+ dsk[(per_block_name,) + key] = (
166
+ partial(func, axis=axis),
167
+ (x.name,) + key,
168
+ )
169
+
170
+ # Phase 2: Build the sequential combination
171
+ n = x.numblocks[axis]
172
+ full = slice(None, None, None)
173
+ slc = (full,) * axis + (slice(-1, None),) + (full,) * (x.ndim - axis - 1)
174
+
175
+ # For each position along the axis, we need to track the cumulative
176
+ # last values from all previous blocks
177
+ indices = list(product(*[range(nb) if i != axis else [0] for i, nb in enumerate(x.numblocks)]))
178
+
179
+ # Initialize "extra" values (cumulative sums of previous blocks) to identity
180
+ for ind in indices:
181
+ shape = tuple(x.chunks[i][ii] if i != axis else 1 for i, ii in enumerate(ind))
182
+ dsk[(self._name, "extra") + ind] = (
183
+ apply,
184
+ np.full_like,
185
+ (x._meta, ident, dtype),
186
+ {"shape": shape},
187
+ )
188
+ # First block along axis: just use per-block result
189
+ dsk[(self._name,) + ind] = (per_block_name,) + ind
190
+
191
+ # For subsequent blocks, add the cumulative total from previous blocks
192
+ for i in range(1, n):
193
+ last_indices = indices
194
+ indices = list(product(*[range(nb) if ii != axis else [i] for ii, nb in enumerate(x.numblocks)]))
195
+ for old, ind in zip(last_indices, indices):
196
+ this_extra = (self._name, "extra") + ind
197
+ # Combine previous extra with the last element of the previous block
198
+ dsk[this_extra] = (
199
+ binop,
200
+ (self._name, "extra") + old,
201
+ (operator.getitem, (per_block_name,) + old, slc),
202
+ )
203
+ # Add the extra to this block's result
204
+ dsk[(self._name,) + ind] = (
205
+ binop,
206
+ this_extra,
207
+ (per_block_name,) + ind,
208
+ )
209
+
210
+ return dsk
211
+
212
+
213
+ class CumReductionBlelloch(ArrayExpr):
214
+ """Expression for parallel cumulative reductions using Blelloch's algorithm.
215
+
216
+ This is a work-efficient parallel scan that uses O(log n) parallel steps.
217
+ """
218
+
219
+ _parameters = ["array", "func", "preop", "binop", "axis", "_dtype"]
220
+ _defaults = {"_dtype": None}
221
+
222
+ @cached_property
223
+ def _name(self):
224
+ return f"{funcname(self.func)}-{_tokenize_deterministic(*self.operands)}"
225
+
226
+ @cached_property
227
+ def dtype(self):
228
+ if self._dtype is not None:
229
+ return np.dtype(self._dtype)
230
+ return getattr(self.func(np.ones((0,), dtype=self.array.dtype)), "dtype", object)
231
+
232
+ @cached_property
233
+ def _meta(self):
234
+ # Return meta with the correct dtype
235
+ meta = self.array._meta
236
+ if hasattr(meta, "dtype") and meta.dtype != self.dtype:
237
+ return meta.astype(self.dtype)
238
+ return meta
239
+
240
+ @cached_property
241
+ def chunks(self):
242
+ return self.array.chunks
243
+
244
+ def _layer(self):
245
+ import builtins as py_builtins
246
+
247
+ x = self.array
248
+ axis = self.axis
249
+ func = self.func
250
+ preop = self.preop
251
+ binop = self.binop
252
+ dtype = self.dtype
253
+ base_key = (self._name,)
254
+
255
+ dsk = {}
256
+
257
+ # Phase 1: Compute prefix values (sum/product of each block)
258
+ batches_name = self._name + "-batch"
259
+ for key in product(*map(range, x.numblocks)):
260
+ dsk[(batches_name,) + key] = (
261
+ partial(preop, axis=axis, keepdims=True),
262
+ (x.name,) + key,
263
+ )
264
+
265
+ # Build indices for each position along the axis
266
+ full_indices = [
267
+ list(product(*[range(nb) if j != axis else [i] for j, nb in enumerate(x.numblocks)]))
268
+ for i in range(x.numblocks[axis])
269
+ ]
270
+
271
+ if not full_indices:
272
+ return dsk
273
+
274
+ *indices, last_index = full_indices
275
+ prefix_vals = [[(batches_name,) + index for index in vals] for vals in indices]
276
+
277
+ n_vals = len(prefix_vals)
278
+ level = 0
279
+
280
+ if n_vals >= 2:
281
+ # Upsweep
282
+ stride = 1
283
+ stride2 = 2
284
+ while stride2 <= n_vals:
285
+ for i in range(stride2 - 1, n_vals, stride2):
286
+ new_vals = []
287
+ for index, left_val, right_val in zip(indices[i], prefix_vals[i - stride], prefix_vals[i]):
288
+ key = base_key + index + (level, i)
289
+ dsk[key] = (binop, left_val, right_val)
290
+ new_vals.append(key)
291
+ prefix_vals[i] = new_vals
292
+ stride = stride2
293
+ stride2 *= 2
294
+ level += 1
295
+
296
+ # Downsweep
297
+ stride2 = py_builtins.max(2, 2 ** math.ceil(math.log2(n_vals // 2)))
298
+ stride = stride2 // 2
299
+ while stride > 0:
300
+ for i in range(stride2 + stride - 1, n_vals, stride2):
301
+ new_vals = []
302
+ for index, left_val, right_val in zip(indices[i], prefix_vals[i - stride], prefix_vals[i]):
303
+ key = base_key + index + (level, i)
304
+ dsk[key] = (binop, left_val, right_val)
305
+ new_vals.append(key)
306
+ prefix_vals[i] = new_vals
307
+ stride2 = stride
308
+ stride //= 2
309
+ level += 1
310
+
311
+ # Phase 2: Apply cumulative function and combine with prefix sums
312
+ # First blocks: just apply the cumulative function
313
+ for index in full_indices[0]:
314
+ dsk[base_key + index] = (
315
+ _prefixscan_first,
316
+ func,
317
+ (x.name,) + index,
318
+ axis,
319
+ dtype,
320
+ )
321
+
322
+ # Remaining blocks: apply cumulative function and add prefix sum
323
+ for indexes, vals in zip(full_indices[1:], prefix_vals):
324
+ for index, val in zip(indexes, vals):
325
+ dsk[base_key + index] = (
326
+ _prefixscan_combine,
327
+ func,
328
+ binop,
329
+ val,
330
+ (x.name,) + index,
331
+ axis,
332
+ dtype,
333
+ )
334
+
335
+ return dsk
336
+
337
+
338
+ def _cumreduction_expr(func, binop, ident, x, axis, dtype, out, method, preop):
339
+ """Create cumulative reduction expression."""
340
+ from dask_array._collection import Array
341
+ from dask_array.core._blockwise_funcs import _handle_out
342
+
343
+ if not isinstance(x, Array):
344
+ from dask_array.core._conversion import asarray
345
+
346
+ x = asarray(x)
347
+
348
+ x, axis = _prepare_cumulative(x, axis)
349
+ axis = validate_axis(axis, x.ndim)
350
+
351
+ if method == "blelloch":
352
+ if preop is None:
353
+ raise TypeError('cumreduction with "blelloch" method requires `preop=` argument')
354
+ expr = CumReductionBlelloch(x.expr, func, preop, binop, axis, dtype)
355
+ elif method == "sequential":
356
+ expr = CumReduction(x.expr, func, binop, ident, axis, dtype)
357
+ else:
358
+ raise ValueError(f'Invalid method for cumreduction. Expected "sequential" or "blelloch". Got: {method!r}')
359
+
360
+ result = new_collection(expr)
361
+ return _handle_out(out, result)
362
+
363
+
364
+ def cumsum(x, axis=None, dtype=None, out=None, method="sequential"):
365
+ """Return the cumulative sum of the elements along a given axis.
366
+
367
+ Parameters
368
+ ----------
369
+ x : array_like
370
+ Input array.
371
+ axis : int, optional
372
+ Axis along which the cumulative sum is computed. The default
373
+ (None) is to compute the cumsum over the flattened array.
374
+ dtype : dtype, optional
375
+ Type of the returned array and of the accumulator in which the
376
+ elements are summed.
377
+ out : ndarray, optional
378
+ Not implemented for Dask arrays.
379
+ method : {'sequential', 'blelloch'}, optional
380
+ Algorithm to use for the cumulative sum. Default is 'sequential'.
381
+
382
+ Returns
383
+ -------
384
+ cumsum_along_axis : dask array
385
+ A new array holding the result.
386
+ """
387
+ return _cumreduction_expr(
388
+ np.cumsum,
389
+ _cumsum_merge,
390
+ 0,
391
+ x,
392
+ axis,
393
+ dtype,
394
+ out=out,
395
+ method=method,
396
+ preop=np.sum,
397
+ )
398
+
399
+
400
+ def cumprod(x, axis=None, dtype=None, out=None, method="sequential"):
401
+ """Return the cumulative product of elements along a given axis.
402
+
403
+ Parameters
404
+ ----------
405
+ x : array_like
406
+ Input array.
407
+ axis : int, optional
408
+ Axis along which the cumulative product is computed. The default
409
+ (None) is to compute the cumprod over the flattened array.
410
+ dtype : dtype, optional
411
+ Type of the returned array and of the accumulator in which the
412
+ elements are multiplied.
413
+ out : ndarray, optional
414
+ Not implemented for Dask arrays.
415
+ method : {'sequential', 'blelloch'}, optional
416
+ Algorithm to use for the cumulative product. Default is 'sequential'.
417
+
418
+ Returns
419
+ -------
420
+ cumprod_along_axis : dask array
421
+ A new array holding the result.
422
+ """
423
+ return _cumreduction_expr(
424
+ np.cumprod,
425
+ _cumprod_merge,
426
+ 1,
427
+ x,
428
+ axis,
429
+ dtype,
430
+ out=out,
431
+ method=method,
432
+ preop=np.prod,
433
+ )
434
+
435
+
436
+ def nancumsum(x, axis, dtype=None, out=None, *, method="sequential"):
437
+ """Return the cumulative sum of array elements treating NaNs as zero.
438
+
439
+ Parameters
440
+ ----------
441
+ x : array_like
442
+ Input array.
443
+ axis : int
444
+ Axis along which the cumulative sum is computed.
445
+ dtype : dtype, optional
446
+ Type of the returned array and of the accumulator in which the
447
+ elements are summed.
448
+ out : ndarray, optional
449
+ Not implemented for Dask arrays.
450
+ method : {'sequential', 'blelloch'}, optional
451
+ Algorithm to use for the cumulative sum. Default is 'sequential'.
452
+
453
+ Returns
454
+ -------
455
+ nancumsum_along_axis : dask array
456
+ A new array holding the result.
457
+ """
458
+ from dask_array import _chunk as chunk_module
459
+
460
+ return _cumreduction_expr(
461
+ chunk_module.nancumsum,
462
+ operator.add,
463
+ 0,
464
+ x,
465
+ axis,
466
+ dtype,
467
+ out=out,
468
+ method=method,
469
+ preop=np.nansum,
470
+ )
471
+
472
+
473
+ def nancumprod(x, axis, dtype=None, out=None, *, method="sequential"):
474
+ """Return the cumulative product of array elements treating NaNs as one.
475
+
476
+ Parameters
477
+ ----------
478
+ x : array_like
479
+ Input array.
480
+ axis : int
481
+ Axis along which the cumulative product is computed.
482
+ dtype : dtype, optional
483
+ Type of the returned array and of the accumulator in which the
484
+ elements are multiplied.
485
+ out : ndarray, optional
486
+ Not implemented for Dask arrays.
487
+ method : {'sequential', 'blelloch'}, optional
488
+ Algorithm to use for the cumulative product. Default is 'sequential'.
489
+
490
+ Returns
491
+ -------
492
+ nancumprod_along_axis : dask array
493
+ A new array holding the result.
494
+ """
495
+ from dask_array import _chunk as chunk_module
496
+
497
+ return _cumreduction_expr(
498
+ chunk_module.nancumprod,
499
+ operator.mul,
500
+ 1,
501
+ x,
502
+ axis,
503
+ dtype,
504
+ out=out,
505
+ method=method,
506
+ preop=np.nanprod,
507
+ )
508
+
509
+
510
+ def cumreduction(
511
+ func,
512
+ binop,
513
+ ident,
514
+ x,
515
+ axis=None,
516
+ dtype=None,
517
+ out=None,
518
+ method="sequential",
519
+ preop=None,
520
+ ):
521
+ """Generic cumulative reduction. See dask.array.reductions.cumreduction."""
522
+ return _cumreduction_expr(func, binop, ident, x, axis, dtype, out=out, method=method, preop=preop)