|
49 | 49 | import dpnp
|
50 | 50 |
|
51 | 51 | from .dpnp_array import dpnp_array
|
| 52 | + |
| 53 | +# pylint: disable=no-name-in-module |
| 54 | +from .dpnp_utils import get_usm_allocations |
52 | 55 | from .dpnp_utils.dpnp_utils_pad import dpnp_pad
|
53 | 56 |
|
54 | 57 | __all__ = [
|
|
66 | 69 | "concat",
|
67 | 70 | "concatenate",
|
68 | 71 | "copyto",
|
| 72 | + "delete", |
69 | 73 | "dsplit",
|
70 | 74 | "dstack",
|
71 | 75 | "expand_dims",
|
@@ -115,6 +119,135 @@ def _check_stack_arrays(arrays):
|
115 | 119 | )
|
116 | 120 |
|
117 | 121 |
|
| 122 | +def _delete_with_slice(a, obj, axis): |
| 123 | + """Utility function for ``dpnp.delete`` when obj is slice.""" |
| 124 | + |
| 125 | + a, a_ndim, order, axis, slobj, n, a_shape = _calc_parameters(a, axis) |
| 126 | + start, stop, step = obj.indices(n) |
| 127 | + xr = range(start, stop, step) |
| 128 | + num_del = len(xr) |
| 129 | + |
| 130 | + if num_del <= 0: |
| 131 | + return a.copy(order=order) |
| 132 | + |
| 133 | + # Invert if step is negative: |
| 134 | + if step < 0: |
| 135 | + step = -step |
| 136 | + start = xr[-1] |
| 137 | + stop = xr[0] + 1 |
| 138 | + |
| 139 | + a_shape[axis] -= num_del |
| 140 | + new = dpnp.empty( |
| 141 | + a_shape, |
| 142 | + dtype=a.dtype, |
| 143 | + order=order, |
| 144 | + sycl_queue=a.sycl_queue, |
| 145 | + usm_type=a.usm_type, |
| 146 | + ) |
| 147 | + # copy initial chunk |
| 148 | + if start == 0: |
| 149 | + pass |
| 150 | + else: |
| 151 | + slobj[axis] = slice(None, start) |
| 152 | + new[tuple(slobj)] = a[tuple(slobj)] |
| 153 | + # copy end chunk |
| 154 | + if stop == n: |
| 155 | + pass |
| 156 | + else: |
| 157 | + slobj[axis] = slice(stop - num_del, None) |
| 158 | + slobj2 = [slice(None)] * a_ndim |
| 159 | + slobj2[axis] = slice(stop, None) |
| 160 | + new[tuple(slobj)] = a[tuple(slobj2)] |
| 161 | + # copy middle pieces |
| 162 | + if step == 1: |
| 163 | + pass |
| 164 | + else: # use array indexing. |
| 165 | + keep = dpnp.ones( |
| 166 | + stop - start, |
| 167 | + dtype=dpnp.bool, |
| 168 | + sycl_queue=a.sycl_queue, |
| 169 | + usm_type=a.usm_type, |
| 170 | + ) |
| 171 | + keep[: stop - start : step] = False |
| 172 | + slobj[axis] = slice(start, stop - num_del) |
| 173 | + slobj2 = [slice(None)] * a_ndim |
| 174 | + slobj2[axis] = slice(start, stop) |
| 175 | + a = a[tuple(slobj2)] |
| 176 | + slobj2[axis] = keep |
| 177 | + new[tuple(slobj)] = a[tuple(slobj2)] |
| 178 | + |
| 179 | + return new |
| 180 | + |
| 181 | + |
| 182 | +def _delete_without_slice(a, obj, axis, single_value, exec_q, usm_type): |
| 183 | + """Utility function for ``dpnp.delete`` when obj is int or array of int.""" |
| 184 | + |
| 185 | + a, a_ndim, order, axis, slobj, n, a_shape = _calc_parameters(a, axis) |
| 186 | + if single_value: |
| 187 | + # optimization for a single value |
| 188 | + if obj < -n or obj >= n: |
| 189 | + raise IndexError( |
| 190 | + f"index {obj} is out of bounds for axis {axis} with " |
| 191 | + f"size {n}" |
| 192 | + ) |
| 193 | + if obj < 0: |
| 194 | + obj += n |
| 195 | + a_shape[axis] -= 1 |
| 196 | + new = dpnp.empty( |
| 197 | + a_shape, |
| 198 | + dtype=a.dtype, |
| 199 | + order=order, |
| 200 | + sycl_queue=exec_q, |
| 201 | + usm_type=usm_type, |
| 202 | + ) |
| 203 | + slobj[axis] = slice(None, obj) |
| 204 | + new[tuple(slobj)] = a[tuple(slobj)] |
| 205 | + slobj[axis] = slice(obj, None) |
| 206 | + slobj2 = [slice(None)] * a_ndim |
| 207 | + slobj2[axis] = slice(obj + 1, None) |
| 208 | + new[tuple(slobj)] = a[tuple(slobj2)] |
| 209 | + else: |
| 210 | + if obj.dtype == dpnp.bool: |
| 211 | + if obj.shape != (n,): |
| 212 | + raise ValueError( |
| 213 | + "boolean array argument `obj` to delete must be " |
| 214 | + f"one-dimensional and match the axis length of {n}" |
| 215 | + ) |
| 216 | + |
| 217 | + # optimization, the other branch is slower |
| 218 | + keep = ~obj |
| 219 | + else: |
| 220 | + keep = dpnp.ones( |
| 221 | + n, dtype=dpnp.bool, sycl_queue=exec_q, usm_type=usm_type |
| 222 | + ) |
| 223 | + keep[obj,] = False |
| 224 | + |
| 225 | + slobj[axis] = keep |
| 226 | + new = a[tuple(slobj)] |
| 227 | + |
| 228 | + return new |
| 229 | + |
| 230 | + |
| 231 | +def _calc_parameters(a, axis): |
| 232 | + """Utility function for ``dpnp.delete`` and ``dpnp.insert``.""" |
| 233 | + |
| 234 | + a_ndim = a.ndim |
| 235 | + order = "F" if a.flags.fnc else "C" |
| 236 | + if axis is None: |
| 237 | + if a_ndim != 1: |
| 238 | + a = dpnp.ravel(a) |
| 239 | + a_ndim = 1 |
| 240 | + axis = 0 |
| 241 | + else: |
| 242 | + axis = normalize_axis_index(axis, a_ndim) |
| 243 | + |
| 244 | + slobj = [slice(None)] * a_ndim |
| 245 | + n = a.shape[axis] |
| 246 | + a_shape = list(a.shape) |
| 247 | + |
| 248 | + return a, a_ndim, order, axis, slobj, n, a_shape |
| 249 | + |
| 250 | + |
118 | 251 | def _unique_1d(
|
119 | 252 | ar,
|
120 | 253 | return_index=False,
|
@@ -1206,6 +1339,109 @@ def copyto(dst, src, casting="same_kind", where=True):
|
1206 | 1339 | dst_usm[mask_usm] = src_usm[mask_usm]
|
1207 | 1340 |
|
1208 | 1341 |
|
| 1342 | +def delete(arr, obj, axis=None): |
| 1343 | + """ |
| 1344 | + Return a new array with sub-arrays along an axis deleted. For a one |
| 1345 | + dimensional array, this returns those entries not returned by |
| 1346 | + ``arr[obj]``. |
| 1347 | +
|
| 1348 | + For full documentation refer to :obj:`numpy.delete`. |
| 1349 | +
|
| 1350 | + Parameters |
| 1351 | + ---------- |
| 1352 | + arr : {dpnp.ndarray, usm_ndarray} |
| 1353 | + Input array. |
| 1354 | + obj : {slice, int, array-like of ints or boolean} |
| 1355 | + Indicate indices of sub-arrays to remove along the specified axis. |
| 1356 | + Boolean indices are treated as a mask of elements to remove. |
| 1357 | + axis : {None, int}, optional |
| 1358 | + The axis along which to delete the subarray defined by `obj`. |
| 1359 | + If `axis` is ``None``, `obj` is applied to the flattened array. |
| 1360 | + Default: ``None``. |
| 1361 | +
|
| 1362 | + Returns |
| 1363 | + ------- |
| 1364 | + out : dpnp.ndarray |
| 1365 | + A copy of `arr` with the elements specified by `obj` removed. Note |
| 1366 | + that `delete` does not occur in-place. If `axis` is ``None``, `out` is |
| 1367 | + a flattened array. |
| 1368 | +
|
| 1369 | + See Also |
| 1370 | + -------- |
| 1371 | + :obj:`dpnp.insert` : Insert elements into an array. |
| 1372 | + :obj:`dpnp.append` : Append elements at the end of an array. |
| 1373 | +
|
| 1374 | + Notes |
| 1375 | + ----- |
| 1376 | + Often it is preferable to use a boolean mask. For example: |
| 1377 | +
|
| 1378 | + >>> import dpnp as np |
| 1379 | + >>> arr = np.arange(12) + 1 |
| 1380 | + >>> mask = np.ones(len(arr), dtype=np.bool) |
| 1381 | + >>> mask[0] = mask[2] = mask[4] = False |
| 1382 | + >>> result = arr[mask,...] |
| 1383 | +
|
| 1384 | + is equivalent to ``np.delete(arr, [0, 2, 4], axis=0)``, but allows further |
| 1385 | + use of `mask`. |
| 1386 | +
|
| 1387 | + Examples |
| 1388 | + -------- |
| 1389 | + >>> import dpnp as np |
| 1390 | + >>> arr = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]) |
| 1391 | + >>> arr |
| 1392 | + array([[ 1, 2, 3, 4], |
| 1393 | + [ 5, 6, 7, 8], |
| 1394 | + [ 9, 10, 11, 12]]) |
| 1395 | + >>> np.delete(arr, 1, 0) |
| 1396 | + array([[ 1, 2, 3, 4], |
| 1397 | + [ 9, 10, 11, 12]]) |
| 1398 | +
|
| 1399 | + >>> np.delete(arr, slice(None, None, 2), 1) |
| 1400 | + array([[ 2, 4], |
| 1401 | + [ 6, 8], |
| 1402 | + [10, 12]]) |
| 1403 | + >>> np.delete(arr, [1, 3, 5], None) |
| 1404 | + array([ 1, 3, 5, 7, 8, 9, 10, 11, 12]) |
| 1405 | +
|
| 1406 | + """ |
| 1407 | + |
| 1408 | + dpnp.check_supported_arrays_type(arr) |
| 1409 | + |
| 1410 | + if isinstance(obj, slice): |
| 1411 | + return _delete_with_slice(arr, obj, axis) |
| 1412 | + |
| 1413 | + if dpnp.is_supported_array_type(obj): |
| 1414 | + usm_type, exec_q = get_usm_allocations([arr, obj]) |
| 1415 | + else: |
| 1416 | + usm_type, exec_q = arr.usm_type, arr.sycl_queue |
| 1417 | + |
| 1418 | + if isinstance(obj, (int, dpnp.integer)) and not isinstance(obj, bool): |
| 1419 | + single_value = True |
| 1420 | + indices = obj |
| 1421 | + else: |
| 1422 | + single_value = False |
| 1423 | + is_array = isinstance(obj, (dpnp_array, numpy.ndarray, dpt.usm_ndarray)) |
| 1424 | + indices = dpnp.asarray(obj, sycl_queue=exec_q, usm_type=usm_type) |
| 1425 | + # if `obj` is originally an empty list, after converting it into |
| 1426 | + # an array, it will have float dtype, so we need to change its dtype |
| 1427 | + # to integer. However, if `obj` is originally an empty array with |
| 1428 | + # float dtype, it is a mistake by user and it will raise an error later |
| 1429 | + if indices.size == 0 and not is_array: |
| 1430 | + indices = indices.astype(dpnp.intp) |
| 1431 | + elif indices.size == 1 and indices.dtype.kind in "ui": |
| 1432 | + # For a size 1 integer array we can use the single-value path |
| 1433 | + # (most dtypes, except boolean, should just fail later). |
| 1434 | + if isinstance(obj, (dpnp_array, dpt.usm_ndarray)): |
| 1435 | + indices = indices.item() |
| 1436 | + else: |
| 1437 | + indices = numpy.asarray(obj).item() |
| 1438 | + single_value = True |
| 1439 | + |
| 1440 | + return _delete_without_slice( |
| 1441 | + arr, indices, axis, single_value, exec_q, usm_type |
| 1442 | + ) |
| 1443 | + |
| 1444 | + |
1209 | 1445 | def dsplit(ary, indices_or_sections):
|
1210 | 1446 | """
|
1211 | 1447 | Split array into multiple sub-arrays along the 3rd axis (depth).
|
|
0 commit comments