Skip to content

Commit 4b163bf

Browse files
Add cross-reference fallback for dpnp.tensor to dpctl 0.21.1 docs (#2848)
This PR proposes to replace `dpctl.tensor` with `dpnp.tensor` across the error messages. Add a Sphinx handler to redirect dpnp.tensor.* cross-references to dpctl 0.21.1 docs and `tensor.rst` page linking to the dpctl API reference
1 parent 728ae42 commit 4b163bf

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+319
-190
lines changed

doc/conf.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
# http://www.sphinx-doc.org/en/master/config
77

88
from datetime import datetime
9+
from urllib.parse import urljoin
910

1011
from sphinx.ext.autodoc import FunctionDocumenter
1112
from sphinx.ext.napoleon import NumpyDocstring, docstring
@@ -231,6 +232,9 @@ def _can_document_member(member, *args, **kwargs):
231232

232233
autosummary_generate = True
233234

235+
_DPCTL_021_BASE = "https://intelpython.github.io/dpctl/0.21.1/"
236+
_DPCTL_021_INV = urljoin(_DPCTL_021_BASE, "objects.inv")
237+
234238
intersphinx_mapping = {
235239
"python": ("https://docs.python.org/3/", None),
236240
"numpy": ("https://numpy.org/doc/stable/", None),
@@ -302,3 +306,65 @@ def _parse_returns_section_patched(self, section: str) -> list[str]:
302306

303307

304308
NumpyDocstring._parse_returns_section = _parse_returns_section_patched
309+
310+
311+
# TODO: Remove once dpnp.tensor docs are generated in dpnp
312+
def _load_dpctl_tensor_inventory(app):
313+
"""Load dpctl 0.21.1 inventory for dpnp.tensor fallback only."""
314+
from sphinx.ext.intersphinx import fetch_inventory
315+
from sphinx.util import logging
316+
317+
logger = logging.getLogger(__name__)
318+
319+
try:
320+
inv = fetch_inventory(app, _DPCTL_021_BASE, _DPCTL_021_INV)
321+
except Exception as exc:
322+
logger.warning(
323+
"Failed to load dpctl 0.21.1 inventory from %s: %s",
324+
_DPCTL_021_INV,
325+
exc,
326+
)
327+
inv = {}
328+
329+
app.builder.env._dpctl_tensor_021_inventory = inv
330+
331+
332+
# TODO: Remove once dpnp.tensor docs are generated in dpnp
333+
def _resolve_dpnp_tensor_refs(app, env, node, contnode):
334+
"""Resolve dpnp.tensor.* references to dpctl 0.21.1 documentation.
335+
336+
This temporary workaround is needed because dpnp.tensor documentation
337+
is not generated yet, while the corresponding API is still documented
338+
in dpctl 0.21.1.
339+
"""
340+
from docutils import nodes as docutils_nodes
341+
342+
target = node.get("reftarget", "")
343+
if not target.startswith("dpnp.tensor"):
344+
return None
345+
346+
dpctl_target = target.replace("dpnp.tensor", "dpctl.tensor", 1)
347+
dpctl_tensor_inv = getattr(env, "_dpctl_tensor_021_inventory", {})
348+
349+
for _objtype, objects in dpctl_tensor_inv.items():
350+
if dpctl_target not in objects:
351+
continue
352+
353+
item = objects[dpctl_target]
354+
location = item.uri
355+
if location.endswith("$"):
356+
location = location[:-1] + dpctl_target
357+
358+
refuri = urljoin(_DPCTL_021_BASE, location)
359+
newnode = docutils_nodes.reference(
360+
"", "", internal=False, refuri=refuri
361+
)
362+
newnode += contnode.deepcopy()
363+
return newnode
364+
365+
return None
366+
367+
368+
def setup(app):
369+
app.connect("builder-inited", _load_dpctl_tensor_inventory, priority=400)
370+
app.connect("missing-reference", _resolve_dpnp_tensor_refs, priority=400)

doc/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ Data Parallel Extension for NumPy*
1313
overview
1414
quick_start_guide
1515
reference/index
16+
tensor
1617

1718
.. toctree::
1819
:maxdepth: 1

doc/reference/exceptions.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ Exceptions
2020
.. exception:: DLPackCreationError
2121

2222
Given when constructing DLPack capsule from either :class:`dpnp.ndarray` or
23-
:class:`dpctl.tensor.usm_ndarray` based on a USM allocation
23+
:class:`dpnp.tensor.usm_ndarray` based on a USM allocation
2424
on a partitioned SYCL device.
2525

2626
.. rubric:: Examples

doc/tensor.rst

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
.. _tensor:
2+
3+
Tensor (``dpnp.tensor``)
4+
========================
5+
6+
``dpnp.tensor`` provides a reference implementation of the
7+
`Python Array API <https://data-apis.org/array-api/latest/>`_ specification.
8+
The implementation uses data-parallel algorithms suitable for execution on
9+
accelerators, such as GPUs.
10+
11+
It also provides the underlying Array API-compliant implementation
12+
used by ``dpnp``.
13+
14+
``dpnp.tensor`` is written using C++ and
15+
`SYCL <https://registry.khronos.org/SYCL/specs/sycl-2020/html/sycl-2020.html>`_
16+
and oneAPI extensions implemented in
17+
`Intel(R) oneAPI DPC++ compiler <https://www.intel.com/content/www/us/en/developer/tools/oneapi/dpc-compiler.html>`_.
18+
19+
Design and Motivation
20+
---------------------
21+
22+
The tensor implementation was originally developed as a standalone project and
23+
later integrated into the `dpctl <https://intelpython.github.io/dpctl/latest/index.html>`_
24+
library as ``dpctl.tensor``. It has since been migrated into ``dpnp``,
25+
making ``dpnp`` the primary owner and development location of the tensor implementation.
26+
27+
This change simplifies maintenance, reduces cross-project
28+
dependencies, and enables independent development and release cycles.
29+
30+
Relationship to ``dpnp.ndarray``
31+
--------------------------------
32+
33+
:class:`dpnp.ndarray` is a high-level array object built on top of
34+
``dpnp.tensor.usm_ndarray``, storing array data in Unified Shared Memory
35+
(USM) allocated on a SYCL device. Most users interact with
36+
:class:`dpnp.ndarray` directly; ``dpnp.tensor.usm_ndarray`` may appear in error
37+
messages or type signatures when working with device placement or
38+
interoperability.
39+
40+
Relationship to ``dpctl``
41+
-------------------------
42+
43+
The migration of ``dpctl.tensor`` into ``dpnp.tensor`` does not replace
44+
`dpctl <https://intelpython.github.io/dpctl/latest/index.html>`_ itself.
45+
``dpctl`` remains responsible for device and queue management
46+
(:class:`dpctl.SyclDevice`, :class:`dpctl.SyclQueue`) as well as USM memory
47+
allocation. ``dpnp`` builds on top of these capabilities.
48+
49+
Example
50+
-------
51+
52+
.. code-block:: python
53+
54+
import dpnp
55+
import dpnp.tensor as dpt
56+
57+
# Create a tensor array on the default device
58+
x = dpt.asarray([1.0, 2.0, 3.0])
59+
60+
# dpnp.ndarray wraps the underlying usm_ndarray
61+
a = dpnp.asarray([1.0, 2.0, 3.0])
62+
assert isinstance(a.get_array(), dpt.usm_ndarray)
63+
64+
.. note::
65+
66+
The ``dpnp.tensor`` API documentation will be added in a future release.
67+
68+
The current implementation remains compatible with the original
69+
``dpctl.tensor`` API. For the complete API reference, see the
70+
`dpctl 0.21.1 tensor documentation <https://intelpython.github.io/dpctl/0.21.1/api_reference/dpctl/tensor.html>`_.

dpnp/dpnp_algo/dpnp_arraycreation.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545

4646

4747
def _as_usm_ndarray(a, usm_type, sycl_queue):
48-
"""Converts input object to `dpctl.tensor.usm_ndarray`"""
48+
"""Converts input object to `dpnp.tensor.usm_ndarray`"""
4949

5050
if isinstance(a, dpnp_array):
5151
a = a.get_array()

dpnp/dpnp_algo/dpnp_elementwise_common.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ class DPNPUnaryFunc(UnaryElementwiseFunc):
119119
sycl_dev - The :class:`dpctl.SyclDevice` where the function
120120
evaluation is carried out.
121121
The function is invoked when the argument of the unary function
122-
requires casting, e.g. the argument of `dpctl.tensor.log` is an
122+
requires casting, e.g. the argument of `dpnp.tensor.log` is an
123123
array with integral data type.
124124
125125
"""
@@ -137,7 +137,7 @@ def __init__(
137137
def _call_func(src, dst, sycl_queue, depends=None):
138138
"""
139139
A callback to register in UnaryElementwiseFunc class of
140-
dpctl.tensor
140+
dpnp.tensor
141141
"""
142142

143143
if depends is None:
@@ -588,7 +588,7 @@ class DPNPBinaryFunc(BinaryElementwiseFunc):
588588
evaluation is carried out.
589589
The function is only called when both arguments of the binary
590590
function require casting, e.g. both arguments of
591-
`dpctl.tensor.logaddexp` are arrays with integral data type.
591+
`dpnp.tensor.logaddexp` are arrays with integral data type.
592592
weak_type_resolver : {None, callable}, optional
593593
Function to influence type promotion behavior for Python scalar types
594594
of this binary function. The function takes 3 arguments:
@@ -615,7 +615,7 @@ def __init__(
615615
def _call_func(src1, src2, dst, sycl_queue, depends=None):
616616
"""
617617
A callback to register in UnaryElementwiseFunc class of
618-
dpctl.tensor
618+
dpnp.tensor
619619
"""
620620

621621
if depends is None:

dpnp/dpnp_array.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ class dpnp_array:
7272
An array object represents a multidimensional tensor of numeric elements
7373
stored in a USM allocation on a SYCL device.
7474
75-
This is a wrapper around :class:`dpctl.tensor.usm_ndarray` that provides
75+
This is a wrapper around :class:`dpnp.tensor.usm_ndarray` that provides
7676
methods to be compliant with original NumPy.
7777
7878
"""
@@ -609,12 +609,12 @@ def __usm_ndarray__(self):
609609
"""
610610
Property to support ``__usm_ndarray__`` protocol.
611611
612-
It assumes to return :class:`dpctl.tensor.usm_ndarray` instance
612+
It assumes to return :class:`dpnp.tensor.usm_ndarray` instance
613613
corresponding to the content of the object.
614614
615615
This property is intended to speed-up conversion from
616-
:class:`dpnp.ndarray` to :class:`dpctl.tensor.usm_ndarray` passed into
617-
:func:`dpctl.tensor.asarray` function. The input object that implements
616+
:class:`dpnp.ndarray` to :class:`dpnp.tensor.usm_ndarray` passed into
617+
:func:`dpnp.tensor.asarray` function. The input object that implements
618618
``__usm_ndarray__`` protocol is recognized as owner of USM allocation
619619
that is managed by a smart pointer, and asynchronous deallocation
620620
will not involve GIL.
@@ -631,13 +631,13 @@ def __xor__(self, other, /):
631631
def _create_from_usm_ndarray(usm_ary: dpt.usm_ndarray):
632632
"""
633633
Return :class:`dpnp.ndarray` instance from USM allocation providing
634-
by an instance of :class:`dpctl.tensor.usm_ndarray`.
634+
by an instance of :class:`dpnp.tensor.usm_ndarray`.
635635
636636
"""
637637

638638
if not isinstance(usm_ary, dpt.usm_ndarray):
639639
raise TypeError(
640-
f"Expected dpctl.tensor.usm_ndarray, got {type(usm_ary)}"
640+
f"Expected dpnp.tensor.usm_ndarray, got {type(usm_ary)}"
641641
)
642642
res = dpnp_array.__new__(dpnp_array)
643643
res._array_obj = usm_ary
@@ -956,7 +956,7 @@ def astype(
956956
`device` can be ``None``, a oneAPI filter selector string,
957957
an instance of :class:`dpctl.SyclDevice` corresponding to
958958
a non-partitioned SYCL device, an instance of
959-
:class:`dpctl.SyclQueue`, or a :class:`dpctl.tensor.Device` object
959+
:class:`dpctl.SyclQueue`, or a :class:`dpnp.tensor.Device` object
960960
returned by :attr:`dpnp.ndarray.device`.
961961
If the value is ``None``, returned array is created on the same
962962
device as that array.
@@ -1067,7 +1067,7 @@ def copy(
10671067
`device` can be ``None``, a oneAPI filter selector string,
10681068
an instance of :class:`dpctl.SyclDevice` corresponding to
10691069
a non-partitioned SYCL device, an instance of
1070-
:class:`dpctl.SyclQueue`, or a :class:`dpctl.tensor.Device` object
1070+
:class:`dpctl.SyclQueue`, or a :class:`dpnp.tensor.Device` object
10711071
returned by :attr:`dpnp.ndarray.device`.
10721072
10731073
Default: ``None``.
@@ -1162,7 +1162,7 @@ def data(self):
11621162
@property
11631163
def device(self):
11641164
"""
1165-
Return :class:`dpctl.tensor.Device` object representing residence of
1165+
Return :class:`dpnp.tensor.Device` object representing residence of
11661166
the array data.
11671167
11681168
The ``Device`` object represents Array API notion of the device, and
@@ -1329,7 +1329,7 @@ def flatten(self, /, order="C"):
13291329
return self.reshape(-1, order=order, copy=True)
13301330

13311331
def get_array(self):
1332-
"""Get :class:`dpctl.tensor.usm_ndarray` object."""
1332+
"""Get :class:`dpnp.tensor.usm_ndarray` object."""
13331333
return self._array_obj
13341334

13351335
# 'getfield',
@@ -2182,7 +2182,7 @@ def to_device(self, device, /, *, stream=None):
21822182
`device` can be ``None``, a oneAPI filter selector string,
21832183
an instance of :class:`dpctl.SyclDevice` corresponding to
21842184
a non-partitioned SYCL device, an instance of
2185-
:class:`dpctl.SyclQueue`, or a :class:`dpctl.tensor.Device` object
2185+
:class:`dpctl.SyclQueue`, or a :class:`dpnp.tensor.Device` object
21862186
returned by :attr:`dpnp.ndarray.device`.
21872187
stream : {SyclQueue, None}, optional
21882188
Execution queue to synchronize with. If ``None``, synchronization

dpnp/dpnp_container.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ def arange(
6464
usm_type="device",
6565
sycl_queue=None,
6666
):
67-
"""Validate input parameters before passing them into `dpctl.tensor` module"""
67+
"""Validate input parameters before passing them into `dpnp.tensor` module"""
6868
dpt.validate_usm_type(usm_type, allow_none=False)
6969
sycl_queue_normalized = dpnp.get_normalized_queue_device(
7070
sycl_queue=sycl_queue, device=device
@@ -151,7 +151,7 @@ def empty(
151151
usm_type="device",
152152
sycl_queue=None,
153153
):
154-
"""Validate input parameters before passing them into `dpctl.tensor` module"""
154+
"""Validate input parameters before passing them into `dpnp.tensor` module"""
155155
dpt.validate_usm_type(usm_type, allow_none=False)
156156
sycl_queue_normalized = dpnp.get_normalized_queue_device(
157157
sycl_queue=sycl_queue, device=device
@@ -182,7 +182,7 @@ def eye(
182182
usm_type="device",
183183
sycl_queue=None,
184184
):
185-
"""Validate input parameters before passing them into `dpctl.tensor` module"""
185+
"""Validate input parameters before passing them into `dpnp.tensor` module"""
186186
dpt.validate_usm_type(usm_type, allow_none=False)
187187
sycl_queue_normalized = dpnp.get_normalized_queue_device(
188188
sycl_queue=sycl_queue, device=device
@@ -213,7 +213,7 @@ def full(
213213
usm_type=None,
214214
sycl_queue=None,
215215
):
216-
"""Validate input parameters before passing them into `dpctl.tensor` module"""
216+
"""Validate input parameters before passing them into `dpnp.tensor` module"""
217217
dpt.validate_usm_type(usm_type, allow_none=True)
218218

219219
sycl_queue_normalized = dpnp.get_normalized_queue_device(
@@ -246,7 +246,7 @@ def ones(
246246
usm_type="device",
247247
sycl_queue=None,
248248
):
249-
"""Validate input parameters before passing them into `dpctl.tensor` module"""
249+
"""Validate input parameters before passing them into `dpnp.tensor` module"""
250250
dpt.validate_usm_type(usm_type, allow_none=False)
251251
sycl_queue_normalized = dpnp.get_normalized_queue_device(
252252
sycl_queue=sycl_queue, device=device
@@ -286,7 +286,7 @@ def zeros(
286286
usm_type="device",
287287
sycl_queue=None,
288288
):
289-
"""Validate input parameters before passing them into `dpctl.tensor` module"""
289+
"""Validate input parameters before passing them into `dpnp.tensor` module"""
290290
dpt.validate_usm_type(usm_type, allow_none=False)
291291
sycl_queue_normalized = dpnp.get_normalized_queue_device(
292292
sycl_queue=sycl_queue, device=device

0 commit comments

Comments
 (0)