diff --git a/doc/_static/dpnp-custom.css b/doc/_static/dpnp-custom.css index 6c4495a213ba..c39eb9f307d3 100644 --- a/doc/_static/dpnp-custom.css +++ b/doc/_static/dpnp-custom.css @@ -57,14 +57,8 @@ dt.sig.sig-object .sig-param > .n * { font-weight: 700 !important; } -/* Parameter/return descriptions: indented block on new line (via custom.js) */ -dl.field-list dd .param-desc { - display: block; - padding-left: 1.5em; -} - -/* Parameter lists: no bullets, keep indentation */ -dl.field-list dd ul.simple { - list-style: none !important; - padding-left: 1.2em !important; +/* numpydoc param/return types: italic like NumPy, not bold */ +.classifier { + font-style: italic; + font-weight: 400; } diff --git a/doc/_static/dpnp-custom.js b/doc/_static/dpnp-custom.js deleted file mode 100644 index b40ef0365036..000000000000 --- a/doc/_static/dpnp-custom.js +++ /dev/null @@ -1,67 +0,0 @@ -(function() { -var separators = [ " – ", " -- " ]; - -function findSeparator(container) -{ - var walker = document.createTreeWalker(container, NodeFilter.SHOW_TEXT); - var node; - while ((node = walker.nextNode())) { - for (var i = 0; i < separators.length; i++) { - var idx = node.nodeValue.indexOf(separators[i]); - if (idx !== -1) - return {node : node, offset : idx, sep : separators[i]}; - } - } - return null; -} - -// Splits

at the separator; wraps everything after it in .param-desc. -function reformatP(p) -{ - var found = findSeparator(p); - if (!found) - return null; - - var afterNode = found.node.splitText(found.offset); - afterNode.nodeValue = afterNode.nodeValue.slice(found.sep.length); - - var range = document.createRange(); - range.setStartBefore(afterNode); - range.setEndAfter(p.lastChild); - - var desc = document.createElement("span"); - desc.className = "param-desc"; - desc.appendChild(range.extractContents()); - p.appendChild(desc); - return desc; -} - -// Browsers auto-close nested

tags, so multi-paragraph descriptions -// arrive as sibling

elements inside

  • . Fold them into the same desc. -function reformatEntry(container) -{ - var firstP = container.querySelector(":scope > p"); - if (!firstP) - return; - - var desc = reformatP(firstP); - if (!desc) - return; - - var sibling; - while ((sibling = firstP.nextElementSibling) && sibling.tagName === "P") { - if (desc.textContent.trim()) - desc.appendChild(document.createElement("br")); - while (sibling.firstChild) - desc.appendChild(sibling.firstChild); - sibling.remove(); - } -} - -document.querySelectorAll("dl.field-list dd ul.simple li") - .forEach(reformatEntry); -document.querySelectorAll("dl.field-list dd").forEach(function(dd) { - if (!dd.querySelector("ul.simple")) - reformatEntry(dd); -}); -}()); diff --git a/doc/conf.py b/doc/conf.py index 0c31bc413caa..eaa6f7ce4070 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -8,8 +8,10 @@ from datetime import datetime from urllib.parse import urljoin +from jinja2.sandbox import SandboxedEnvironment +from numpydoc.docscrape import NumpyDocString +from numpydoc.docscrape_sphinx import SphinxDocString from sphinx.ext.autodoc import FunctionDocumenter -from sphinx.ext.napoleon import NumpyDocstring, docstring from dpnp.dpnp_algo.dpnp_elementwise_common import ( DPNPBinaryFunc, @@ -64,7 +66,7 @@ "sphinx.ext.viewcode", "sphinx.ext.githubpages", "sphinx.ext.intersphinx", - "sphinx.ext.napoleon", + "numpydoc", "sphinx.ext.autodoc", "sphinx.ext.autosummary", "sphinx_copybutton", @@ -238,66 +240,64 @@ def _can_document_member(member, *args, **kwargs): # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = True -# Napoleon settings -napoleon_use_ivar = True -napoleon_include_special_with_doc = True -napoleon_custom_sections = ["limitations"] - - -# Napoleon extension can't properly render "Returns" section in case of -# namedtuple as a return type. That patch proposes to extend the parse logic -# which allows text in a header of "Returns" section. -def _parse_returns_section_patched(self, section: str) -> list[str]: - fields = self._consume_returns_section() - multi = len(fields) > 1 - use_rtype = False if multi else self._config.napoleon_use_rtype - lines: list[str] = [] - header: list[str] = [] - is_logged_header = False - - for _name, _type, _desc in fields: - # self._consume_returns_section() stores the header block - # into `_type` argument, while `_name` has to be empty string and - # `_desc` has to be empty list of strings - if _name == "" and (not _desc or len(_desc) == 1 and _desc[0] == ""): - if not is_logged_header: - docstring.logger.info( - "parse a header block of 'Returns' section", - location=self._get_location(), - ) - is_logged_header = True - - # build a list with lines of the header block - header.extend([_type]) - continue +# Members come from autosummary; don't let numpydoc duplicate them +numpydoc_show_class_members = False + +# Keep the dpnp-only "Limitations" section (numpydoc drops unknown sections): +# register it and give it a slot in the template below +NumpyDocString.sections.setdefault("Limitations", []) + +_NUMPYDOC_TEMPLATE = """\ +{{index}} +{{summary}} +{{extended_summary}} +{{parameters}} +{{attributes}} +{{methods}} +{{returns}} +{{yields}} +{{receives}} +{{other_parameters}} +{{raises}} +{{warns}} +{{warnings}} +{{limitations}} +{{see_also}} +{{notes}} +{{references}} +{{examples}} +""" + +_orig_load_config = SphinxDocString.load_config + + +def _load_config_with_limitations(self, config): + _orig_load_config(self, config) + # Use our template with the "limitations" slot + self.template = SandboxedEnvironment().from_string(_NUMPYDOC_TEMPLATE) + + +SphinxDocString.load_config = _load_config_with_limitations + +_orig_str = SphinxDocString.__str__ + + +def _str_with_limitations(self, indent=0, func_role="obj"): + # Wrap render() to fill the "limitations" slot (a rubric, like "Notes") + orig_render = self.template.render + + def render(**ns): + ns["limitations"] = "\n".join(self._str_section("Limitations")) + return orig_render(**ns) + + self.template.render = render + try: + return _orig_str(self, indent=indent, func_role=func_role) + finally: + self.template.render = orig_render + - if use_rtype: - field = self._format_field(_name, "", _desc) - else: - field = self._format_field(_name, _type, _desc) - - if multi: - if lines: - lines.extend(self._format_block(" * ", field)) - else: - if header: - # add the header block + the 1st parameter stored in `field` - lines.extend([":returns:", ""]) - lines.extend(self._format_block(" " * 4, header)) - lines.extend(self._format_block(" * ", field)) - else: - lines.extend(self._format_block(":returns: * ", field)) - else: - if any(field): # only add :returns: if there's something to say - lines.extend(self._format_block(":returns: ", field)) - if _type and use_rtype: - lines.extend([f":rtype: {_type}", ""]) - if lines and lines[-1]: - lines.append("") - return lines - - -NumpyDocstring._parse_returns_section = _parse_returns_section_patched +SphinxDocString.__str__ = _str_with_limitations # TODO: Remove once dpnp.tensor docs are generated in dpnp diff --git a/dpnp/dpnp_array.py b/dpnp/dpnp_array.py index b225fb2c7329..5a93c8018a82 100644 --- a/dpnp/dpnp_array.py +++ b/dpnp/dpnp_array.py @@ -1986,8 +1986,8 @@ def sort( :obj:`dpnp.searchsorted` : Find elements in a sorted array. :obj:`dpnp.partition` : Partial sort. - Note - ---- + Notes + ----- `axis` in :obj:`dpnp.sort` could be integer or ``None``. If ``None``, the array is flattened before sorting. However, `axis` in :obj:`dpnp.ndarray.sort` can only be integer since it sorts an array diff --git a/dpnp/dpnp_iface_linearalgebra.py b/dpnp/dpnp_iface_linearalgebra.py index b8c01bdc854b..1d0843847631 100644 --- a/dpnp/dpnp_iface_linearalgebra.py +++ b/dpnp/dpnp_iface_linearalgebra.py @@ -257,7 +257,7 @@ def einsum( The calculation based on the Einstein summation convention. See Also - ------- + -------- :obj:`dpnp.einsum_path` : Evaluates the lowest cost contraction order for an einsum expression. :obj:`dpnp.dot` : Returns the dot product of two arrays. diff --git a/dpnp/dpnp_iface_manipulation.py b/dpnp/dpnp_iface_manipulation.py index 8d3050da4e0e..dd53033db279 100644 --- a/dpnp/dpnp_iface_manipulation.py +++ b/dpnp/dpnp_iface_manipulation.py @@ -749,7 +749,7 @@ def asarray_chkfinite( already an ndarray. Raises - ------- + ------ ValueError Raises ``ValueError`` if `a` contains NaN (Not a Number) or Inf (Infinity). @@ -4537,8 +4537,6 @@ def unique_all(x, /): Returns ------- - A namedtuple with the following attributes: - values : dpnp.ndarray The unique elements of an input array. indices : dpnp.ndarray @@ -4591,8 +4589,6 @@ def unique_counts(x, /): Returns ------- - A namedtuple with the following attributes: - values : dpnp.ndarray The unique elements of an input array. counts : dpnp.ndarray @@ -4637,8 +4633,6 @@ def unique_inverse(x, /): Returns ------- - A namedtuple with the following attributes: - values : dpnp.ndarray The unique elements of an input array. inverse_indices : dpnp.ndarray diff --git a/dpnp/dpnp_iface_mathematical.py b/dpnp/dpnp_iface_mathematical.py index c66d827faff6..7b7ffba9dbfa 100644 --- a/dpnp/dpnp_iface_mathematical.py +++ b/dpnp/dpnp_iface_mathematical.py @@ -2329,7 +2329,7 @@ def ediff1d(ary, to_end=None, to_begin=None): returned array is determined by the Type Promotion Rules. Limitations ----------- +----------- Parameters `where` and `subok` are supported with their default values. Keyword argument `kwargs` is currently unsupported. Otherwise ``NotImplementedError`` exception will be raised. @@ -3929,8 +3929,8 @@ def _check_nan_inf(val, val_dt): :obj:`dpnp.negative` : Return the numerical negative of each element of `x`. :obj:`dpnp.copysign` : Change the sign of `x1` to that of `x2`, element-wise. -Note ----- +Notes +----- Equivalent to `x.copy()`, but only defined for types that support arithmetic. Examples diff --git a/dpnp/dpnp_iface_nanfunctions.py b/dpnp/dpnp_iface_nanfunctions.py index 10fffb342305..b528316b485f 100644 --- a/dpnp/dpnp_iface_nanfunctions.py +++ b/dpnp/dpnp_iface_nanfunctions.py @@ -129,8 +129,6 @@ def nanargmax(a, axis=None, out=None, *, keepdims=False): the user is recommended to filter NaNs themselves and use `dpnp.argmax` on the filtered array. - Warnings - -------- The results cannot be trusted if a slice contains only NaNs and -Infs. @@ -213,8 +211,6 @@ def nanargmin(a, axis=None, out=None, *, keepdims=False): the user is recommended to filter NaNs themselves and use `dpnp.argmax` on the filtered array. - Warnings - -------- The results cannot be trusted if a slice contains only NaNs and -Infs. diff --git a/dpnp/dpnp_iface_trigonometric.py b/dpnp/dpnp_iface_trigonometric.py index ee3462ab610e..b82f090de476 100644 --- a/dpnp/dpnp_iface_trigonometric.py +++ b/dpnp/dpnp_iface_trigonometric.py @@ -907,8 +907,8 @@ def cumlogsumexp( :obj:`dpnp.logsumexp` : Logarithm of the sum of elements of the inputs, element-wise. - Note - ---- + Notes + ----- This function is equivalent of `numpy.logaddexp.accumulate`. Examples @@ -1889,8 +1889,8 @@ def logsumexp(x, /, *, axis=None, dtype=None, keepdims=False, out=None): :obj:`dpnp.cumlogsumexp` : Cumulative the natural logarithm of the sum of elements in the input array. - Note - ---- + Notes + ----- This function is equivalent of `numpy.logaddexp.reduce`. Examples @@ -2171,8 +2171,8 @@ def reduce_hypot(x, /, *, axis=None, dtype=None, keepdims=False, out=None): -------- :obj:`dpnp.hypot` : Calculates :math:`\sqrt{x1^2 + x2^2}`, element-wise. - Note - ---- + Notes + ----- This function is equivalent of `numpy.hypot.reduce`. Examples diff --git a/dpnp/fft/dpnp_iface_fft.py b/dpnp/fft/dpnp_iface_fft.py index 90e1a112bdaf..0a5c3cc4351d 100644 --- a/dpnp/fft/dpnp_iface_fft.py +++ b/dpnp/fft/dpnp_iface_fft.py @@ -559,7 +559,7 @@ def hfft(a, n=None, axis=-1, norm=None, out=None): -------- :obj:`dpnp.fft` : For definition of the DFT and conventions used. :obj:`dpnp.fft.rfft` : The one-dimensional FFT of real input. - :obj:`dpnp.fft.ihfft` :The inverse of :obj:`dpnp.fft.hfft`. + :obj:`dpnp.fft.ihfft` : The inverse of :obj:`dpnp.fft.hfft`. Notes @@ -1101,7 +1101,7 @@ def irfft(a, n=None, axis=-1, norm=None, out=None): :obj:`dpnp.fft.rfft` : The one-dimensional FFT of real input, of which :obj:`dpnp.fft.irfft` is inverse. :obj:`dpnp.fft.fft` : The one-dimensional FFT of general (complex) input. - :obj:`dpnp.fft.irfft2` :The inverse of the two-dimensional FFT of + :obj:`dpnp.fft.irfft2` : The inverse of the two-dimensional FFT of real input. :obj:`dpnp.fft.irfftn` : The inverse of the *N*-dimensional FFT of real input. diff --git a/dpnp/linalg/dpnp_iface_linalg.py b/dpnp/linalg/dpnp_iface_linalg.py index 76910692ea0c..74fe3b65febc 100644 --- a/dpnp/linalg/dpnp_iface_linalg.py +++ b/dpnp/linalg/dpnp_iface_linalg.py @@ -468,8 +468,6 @@ def eig(a): Returns ------- - A namedtuple with the following attributes: - eigenvalues : (..., M) dpnp.ndarray The eigenvalues, each repeated according to its multiplicity. The eigenvalues are not necessarily ordered. The resulting array is @@ -481,8 +479,8 @@ def eig(a): ``eigenvectors[:,i]`` is the eigenvector corresponding to the eigenvalue ``eigenvalues[i]``. - Note - ---- + Notes + ----- Since there is no proper OneMKL LAPACK function, DPNP will calculate through a fallback on NumPy call. @@ -584,8 +582,6 @@ def eigh(a, UPLO="L"): Returns ------- - A namedtuple with the following attributes: - eigenvalues : (..., M) dpnp.ndarray The eigenvalues in ascending order, each repeated according to its multiplicity. @@ -645,8 +641,8 @@ def eigvals(a): They are not necessarily ordered, nor are they necessarily real for real matrices. - Note - ---- + Notes + ----- Since there is no proper OneMKL LAPACK function, DPNP will calculate through a fallback on NumPy call. @@ -1591,9 +1587,6 @@ def qr(a, mode="reduced"): Returns ------- - When mode is "reduced" or "complete", the result will be a namedtuple with - the attributes `Q` and `R`: - Q : dpnp.ndarray of float or complex, optional A matrix with orthonormal columns. When mode is ``"complete"`` the result is an orthogonal/unitary matrix @@ -1609,6 +1602,12 @@ def qr(a, mode="reduced"): along with `R`. The `tau` array contains scaling factors for the reflectors. + Notes + ----- + When `mode` is ``"reduced"`` or ``"complete"``, the result is a namedtuple + with the attributes `Q` and `R`. When `mode` is ``"raw"``, it returns + ``(h, tau)``. + Examples -------- >>> import dpnp as np @@ -1749,9 +1748,6 @@ def svd(a, full_matrices=True, compute_uv=True, hermitian=False): Returns ------- - When `compute_uv` is ``True``, the result is a namedtuple with the - following attribute names: - U : { (…, M, M), (…, M, K) } dpnp.ndarray Unitary matrix, where M is the number of rows of the input array `a`. The shape of the matrix `U` depends on the value of `full_matrices`. @@ -1769,6 +1765,12 @@ def svd(a, full_matrices=True, compute_uv=True, hermitian=False): If `full_matrices` is ``False``, `Vh` has the shape (…, K, N). If `compute_uv` is ``False``, neither `U` or `Vh` are computed. + Notes + ----- + When `compute_uv` is ``True``, the result is a namedtuple with the + attribute names `U`, `S`, and `Vh`. When `compute_uv` is ``False``, only + the singular values `S` are returned. + Examples -------- >>> import dpnp as np @@ -1895,8 +1897,6 @@ def slogdet(a): Returns ------- - A namedtuple with the following attributes: - sign : (...) dpnp.ndarray A number representing the sign of the determinant. For a real matrix, this is 1, 0, or -1. For a complex matrix, this is a complex number diff --git a/dpnp/linalg/dpnp_utils_linalg.py b/dpnp/linalg/dpnp_utils_linalg.py index ac1c87a61b5b..5639de12ae5a 100644 --- a/dpnp/linalg/dpnp_utils_linalg.py +++ b/dpnp/linalg/dpnp_utils_linalg.py @@ -736,7 +736,7 @@ def _calculate_determinant_sign(ipiv, diag, res_type, n): values. Parameters - ----------- + ---------- ipiv : {dpnp.ndarray, usm_ndarray} The pivot indices from LU decomposition. diag : {dpnp.ndarray, usm_ndarray} diff --git a/dpnp/scipy/linalg/_decomp_lu.py b/dpnp/scipy/linalg/_decomp_lu.py index f96d56b0e423..5a57e962937e 100644 --- a/dpnp/scipy/linalg/_decomp_lu.py +++ b/dpnp/scipy/linalg/_decomp_lu.py @@ -95,9 +95,6 @@ def lu( Returns ------- - The tuple ``(p, l, u)`` is returned if ``permute_l`` is ``False`` - (default), else the tuple ``(pl, u)`` is returned, where: - p : (..., M, M) dpnp.ndarray or (..., M) dpnp.ndarray Permutation matrix or permutation indices. If `p_indices` is ``False`` (default), a permutation matrix. @@ -116,6 +113,9 @@ def lu( Notes ----- + The tuple ``(p, l, u)`` is returned if `permute_l` is ``False`` (default), + else the tuple ``(pl, u)`` is returned. + Permutation matrices are costly since they are nothing but row reorder of ``L`` and hence indices are strongly recommended to be used instead if the permutation is required. The relation in the 2D case then becomes simply diff --git a/environments/building_docs.yml b/environments/building_docs.yml index 6afaf65c9514..79407e4fdbcd 100644 --- a/environments/building_docs.yml +++ b/environments/building_docs.yml @@ -6,5 +6,6 @@ dependencies: - cupy - sphinx - furo + - numpydoc - pip: - -r base_build_docs.txt diff --git a/pyproject.toml b/pyproject.toml index 0cf3e9721872..a9519d1744aa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -89,6 +89,7 @@ docs = [ "Cython", "cupy", "furo", + "numpydoc", "sphinx", "sphinx-copybutton", "sphinx-design",