Skip to content

Commit a41fd86

Browse files
authored
Add implementation of dpnp.lib.stride_tricks.as_strided (#2991)
This PR adds a new `dpnp.lib` namespace and implements `dpnp.lib.stride_tricks.as_strided`. The function creates a view into an array with a caller-specified shape and strides, sharing the base array's memory. It closes #2969.
1 parent b05c07a commit a41fd86

10 files changed

Lines changed: 389 additions & 7 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ This release is compatible with NumPy 2.5.
1515
* Added support for buffer protocol objects as advanced index keys in `dpnp.ndarray` [#2889](https://github.com/IntelPython/dpnp/pull/2889)
1616
* Added `--includes` and `--include-dir` options to the `dpnp` CLI [#2916](https://github.com/IntelPython/dpnp/pull/2916)
1717
* Added `dpnp-config.cmake` to make `find_package(Dpnp)` work out of the box, and an example which uses it [#2941](https://github.com/IntelPython/dpnp/pull/2941)
18+
* Added implementation of `dpnp.lib.stride_tricks.as_strided` [#2991](https://github.com/IntelPython/dpnp/pull/2991)
1819

1920
### Changed
2021

doc/reference/lib.rst

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
.. _routines.lib:
2+
.. module:: dpnp.lib
3+
4+
Lib module (:mod:`dpnp.lib`)
5+
============================
6+
7+
.. hint:: `NumPy API Reference: Lib module (numpy.lib) <https://numpy.org/doc/stable/reference/routines.lib.html>`_
8+
9+
Submodules
10+
----------
11+
12+
.. autosummary::
13+
:toctree: generated/
14+
15+
stride_tricks

doc/reference/routines.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ These functions cover a subset of
2020
functional
2121
io
2222
indexing
23+
lib
2324
linalg
2425
logic
2526
math

dpnp/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
from ._version import get_versions
6868
from . import exceptions as exceptions
6969
from . import fft as fft
70+
from . import lib as lib
7071
from . import linalg as linalg
7172
from . import random as random
7273
from . import scipy as scipy
@@ -1056,7 +1057,7 @@
10561057
]
10571058

10581059
# add submodules
1059-
__all__ += ["exceptions", "fft", "linalg", "random", "scipy"]
1060+
__all__ += ["exceptions", "fft", "lib", "linalg", "random", "scipy"]
10601061

10611062

10621063
__version__ = get_versions()["version"]

dpnp/lib/__init__.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
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+
"""
30+
``dpnp.lib``
31+
============
32+
33+
The library of stride-manipulation utilities of DPNP.
34+
35+
This namespace mimics parts of ``numpy.lib`` on top of DPNP functionality.
36+
"""
37+
38+
from . import stride_tricks
39+
40+
__all__ = ["stride_tricks"]

dpnp/lib/stride_tricks.py

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
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

Comments
 (0)