|
| 1 | +# ***************************************************************************** |
| 2 | +# Copyright (c) 2026, Intel Corporation |
| 3 | +# All rights reserved. |
| 4 | +# |
| 5 | +# Redistribution and use in source and binary forms, with or without |
| 6 | +# modification, are permitted provided that the following conditions are met: |
| 7 | +# - Redistributions of source code must retain the above copyright notice, |
| 8 | +# this list of conditions and the following disclaimer. |
| 9 | +# - Redistributions in binary form must reproduce the above copyright notice, |
| 10 | +# this list of conditions and the following disclaimer in the documentation |
| 11 | +# and/or other materials provided with the distribution. |
| 12 | +# - Neither the name of the copyright holder nor the names of its contributors |
| 13 | +# may be used to endorse or promote products derived from this software |
| 14 | +# without specific prior written permission. |
| 15 | +# |
| 16 | +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| 17 | +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 18 | +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| 19 | +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
| 20 | +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| 21 | +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| 22 | +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| 23 | +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| 24 | +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| 25 | +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
| 26 | +# THE POSSIBILITY OF SUCH DAMAGE. |
| 27 | +# ***************************************************************************** |
| 28 | + |
| 29 | +"""Utilities that manipulate strides to achieve desirable effects.""" |
| 30 | + |
| 31 | +import dpnp |
| 32 | + |
| 33 | +__all__ = ["as_strided"] |
| 34 | + |
| 35 | + |
| 36 | +def as_strided( |
| 37 | + x, |
| 38 | + shape=None, |
| 39 | + strides=None, |
| 40 | + subok=False, |
| 41 | + writeable=True, |
| 42 | + *, |
| 43 | + check_bounds=None, |
| 44 | +): |
| 45 | + """ |
| 46 | + Create a view into the array with the given shape and strides. |
| 47 | +
|
| 48 | + For full documentation refer to :obj:`numpy.lib.stride_tricks.as_strided`. |
| 49 | +
|
| 50 | + Warnings |
| 51 | + -------- |
| 52 | + This function has to be used with extreme care, see notes. |
| 53 | +
|
| 54 | + Parameters |
| 55 | + ---------- |
| 56 | + x : {dpnp.ndarray, usm_ndarray} |
| 57 | + Array to create a new view from. |
| 58 | + shape : {None, sequence of ints}, optional |
| 59 | + The shape of the new array. |
| 60 | +
|
| 61 | + Default: ``x.shape``. |
| 62 | + strides : {None, sequence of ints}, optional |
| 63 | + The strides of the new array, expressed in bytes. |
| 64 | +
|
| 65 | + Default: ``x.strides``. |
| 66 | + writeable : bool, optional |
| 67 | + If set to ``False``, the returned array will always be read-only. |
| 68 | + Otherwise it will be writable if the original array was. |
| 69 | +
|
| 70 | + Default: ``True``. |
| 71 | + check_bounds : {None, bool}, optional |
| 72 | + Ignored as no effect, the underlying USM array cannot be constructed |
| 73 | + over out-of-bounds memory. |
| 74 | +
|
| 75 | + Default: ``None``. |
| 76 | +
|
| 77 | + Returns |
| 78 | + ------- |
| 79 | + view : dpnp.ndarray |
| 80 | + A view into the memory of `x` with the requested `shape` and `strides`, |
| 81 | + sharing the same data. |
| 82 | +
|
| 83 | + Limitations |
| 84 | + ----------- |
| 85 | + Parameter `subok` is supported with default value. |
| 86 | + Otherwise ``NotImplementedError`` exception will be raised. |
| 87 | +
|
| 88 | + See Also |
| 89 | + -------- |
| 90 | + :obj:`dpnp.broadcast_to` : Broadcast an array to a given shape. |
| 91 | + :obj:`dpnp.reshape` : Give a new shape to an array without changing its |
| 92 | + data. |
| 93 | +
|
| 94 | + Notes |
| 95 | + ----- |
| 96 | + :obj:`dpnp.lib.stride_tricks.as_strided` creates a view into the array |
| 97 | + given the exact strides and shape. This means it manipulates the internal |
| 98 | + data structure of the array and, if done incorrectly, the array elements |
| 99 | + can point to the wrong data and silently produce incorrect results. It is |
| 100 | + advisable to always use the original ``x.strides`` when calculating new |
| 101 | + strides to avoid reliance on a contiguous memory layout. |
| 102 | +
|
| 103 | + Furthermore, arrays created with this function often contain self |
| 104 | + overlapping memory, so that two elements are identical. Writing to a shared |
| 105 | + element then changes every position that references it, so element-wise |
| 106 | + write operations on such arrays are typically unpredictable. A bulk write |
| 107 | + over an overlapping view is rejected, because it would address more memory |
| 108 | + than the base allocation holds. |
| 109 | +
|
| 110 | + Since writing to these arrays has to be tested and done with great care, |
| 111 | + you may want to use ``writeable=False`` to avoid accidental write |
| 112 | + operations. |
| 113 | +
|
| 114 | + For these reasons it is advisable to avoid |
| 115 | + :obj:`dpnp.lib.stride_tricks.as_strided` when possible. |
| 116 | +
|
| 117 | + Examples |
| 118 | + -------- |
| 119 | + >>> import dpnp as np |
| 120 | + >>> x = np.array([1, 2, 3, 4], dtype=np.int32) |
| 121 | +
|
| 122 | + Downsample the array by taking every second element: |
| 123 | +
|
| 124 | + >>> np.lib.stride_tricks.as_strided(x, shape=(2,), |
| 125 | + ... strides=(2 * x.itemsize,)) |
| 126 | + array([1, 3], dtype=int32) |
| 127 | +
|
| 128 | + Broadcast the array along a new leading axis using a zero stride: |
| 129 | +
|
| 130 | + >>> np.lib.stride_tricks.as_strided(x, shape=(3, 4), |
| 131 | + ... strides=(0, x.itemsize)) |
| 132 | + array([[1, 2, 3, 4], |
| 133 | + [1, 2, 3, 4], |
| 134 | + [1, 2, 3, 4]], dtype=int32) |
| 135 | +
|
| 136 | + Build a self-overlapping sliding-window view, where a single element maps |
| 137 | + onto several positions. Here a length-5 array yields a ``3x3`` window in |
| 138 | + which each value repeats along the anti-diagonals: |
| 139 | +
|
| 140 | + >>> y = np.arange(5, dtype=np.int32) |
| 141 | + >>> np.lib.stride_tricks.as_strided(y, shape=(3, 3), |
| 142 | + ... strides=(y.itemsize, y.itemsize)) |
| 143 | + array([[0, 1, 2], |
| 144 | + [1, 2, 3], |
| 145 | + [2, 3, 4]], dtype=int32) |
| 146 | +
|
| 147 | + Attempting to create an out-of-bounds view: |
| 148 | +
|
| 149 | + >>> np.lib.stride_tricks.as_strided(y, shape=(10,), |
| 150 | + ... strides=(y.itemsize,)) |
| 151 | + Traceback (most recent call last): |
| 152 | + ... |
| 153 | + ValueError: buffer='[0 1 2 3 4]' can not accommodate the requested array. |
| 154 | +
|
| 155 | + """ |
| 156 | + |
| 157 | + dpnp.check_supported_arrays_type(x) |
| 158 | + dpnp.check_limitations(subok=subok) |
| 159 | + |
| 160 | + shape = x.shape if shape is None else tuple(shape) |
| 161 | + strides = x.strides if strides is None else tuple(strides) |
| 162 | + |
| 163 | + view = dpnp.ndarray( |
| 164 | + shape, |
| 165 | + dtype=x.dtype, |
| 166 | + buffer=x, |
| 167 | + strides=strides, |
| 168 | + ) |
| 169 | + |
| 170 | + if view.flags.writable and not writeable: |
| 171 | + view.flags.writable = False |
| 172 | + return view |
0 commit comments