Repository: dask/dask

Issue:
Add "empty" mode for `da.pad`
NumPy 1.17.0 adds a new mode to `np.pad` called "empty", which pads the provided array with uninitialized arrays (like `np.empty`). Would be good to add this to `da.pad` as well.


Discussion:

Resolving patch:
diff --git a/dask/array/creation.py b/dask/array/creation.py
--- a/dask/array/creation.py
+++ b/dask/array/creation.py
@@ -945,6 +945,11 @@ def pad_edge(array, pad_width, mode, *args):
                         zip(pad_arrays, end_values, pad_width[d], pad_chunks)
                     )
                 ]
+        elif mode == "empty":
+            pad_arrays = [
+                empty(s, dtype=array.dtype, chunks=c)
+                for s, c in zip(pad_shapes, pad_chunks)
+            ]
 
         result = concatenate([pad_arrays[0], result, pad_arrays[1]], axis=d)
 
@@ -1119,7 +1124,7 @@ def pad(array, pad_width, mode, **kwargs):
         kwargs.setdefault("end_values", 0)
     elif mode in ["reflect", "symmetric"]:
         kwargs.setdefault("reflect_type", "even")
-    elif mode in ["edge", "wrap"]:
+    elif mode in ["edge", "wrap", "empty"]:
         if kwargs:
             raise TypeError("Got unsupported keyword arguments.")
     elif callable(mode):
@@ -1132,7 +1137,7 @@ def pad(array, pad_width, mode, **kwargs):
 
     if mode in ["maximum", "mean", "median", "minimum"]:
         return pad_stats(array, pad_width, mode, *kwargs.values())
-    elif mode in ["constant", "edge", "linear_ramp"]:
+    elif mode in ["constant", "edge", "linear_ramp", "empty"]:
         return pad_edge(array, pad_width, mode, *kwargs.values())
     elif mode in ["reflect", "symmetric", "wrap"]:
         return pad_reuse(array, pad_width, mode, *kwargs.values())


Test patch:
diff --git a/dask/array/tests/test_creation.py b/dask/array/tests/test_creation.py
--- a/dask/array/tests/test_creation.py
+++ b/dask/array/tests/test_creation.py
@@ -640,6 +640,16 @@ def test_tile_np_kroncompare_examples(shape, reps):
         ((10, 11), (4, 5), 0, "reflect", {}),
         ((10, 11), (4, 5), 0, "symmetric", {}),
         ((10, 11), (4, 5), 0, "wrap", {}),
+        pytest.param(
+            (10, 11),
+            (4, 5),
+            0,
+            "empty",
+            {},
+            marks=pytest.mark.skipif(
+                not _numpy_117, reason="requires NumPy>=1.17 for empty mode support"
+            ),
+        ),
     ],
 )
 def test_pad_0_width(shape, chunks, pad_width, mode, kwargs):
@@ -683,6 +693,16 @@ def test_pad_0_width(shape, chunks, pad_width, mode, kwargs):
         ((10,), (3,), ((2, 3)), "maximum", {"stat_length": (1, 2)}),
         ((10, 11), (4, 5), ((1, 4), (2, 3)), "mean", {"stat_length": ((3, 4), (2, 1))}),
         ((10,), (3,), ((2, 3)), "minimum", {"stat_length": (2, 3)}),
+        pytest.param(
+            (10,),
+            (3,),
+            1,
+            "empty",
+            {},
+            marks=pytest.mark.skipif(
+                not _numpy_117, reason="requires NumPy>=1.17 for empty mode support"
+            ),
+        ),
     ],
 )
 def test_pad(shape, chunks, pad_width, mode, kwargs):
@@ -692,7 +712,11 @@ def test_pad(shape, chunks, pad_width, mode, kwargs):
     np_r = np.pad(np_a, pad_width, mode, **kwargs)
     da_r = da.pad(da_a, pad_width, mode, **kwargs)
 
-    assert_eq(np_r, da_r)
+    if mode == "empty":
+        # empty pads lead to undefined values which may be different
+        assert_eq(np_r[pad_width:-pad_width], da_r[pad_width:-pad_width])
+    else:
+        assert_eq(np_r, da_r)
 
 
 @pytest.mark.parametrize("kwargs", [{}, {"scaler": 2}])