diff --git a/CHANGELOG.md b/CHANGELOG.md index fa60d94bdfe..4fe8515d134 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -74,6 +74,7 @@ This release is compatible with NumPy 2.3.3. * Fixed tests for the rounding functions to depend on minimum required numpy version [#2589](https://github.com/IntelPython/dpnp/pull/2589) * Fixed tests for the ufuncs to depend on minimum required numpy version [#2590](https://github.com/IntelPython/dpnp/pull/2590) * Added missing permission definition in `Autoupdate pre-commit` GitHub workflow [#2591](https://github.com/IntelPython/dpnp/pull/2591) +* Resolved issue with the cyclic import in `linalg` submodule [#2608](https://github.com/IntelPython/dpnp/pull/2608) ### Security diff --git a/dpnp/__init__.py b/dpnp/__init__.py index 744200ca13f..4fbac978ec9 100644 --- a/dpnp/__init__.py +++ b/dpnp/__init__.py @@ -70,10 +70,14 @@ from .dpnp_iface_utils import * from .dpnp_iface_utils import __all__ as _ifaceutils__all__ from ._version import get_versions +from . import linalg as linalg __all__ = _iface__all__ __all__ += _ifaceutils__all__ +# add submodules +__all__ += ["linalg"] + __version__ = get_versions()["version"] del get_versions diff --git a/dpnp/backend/extensions/lapack/lapack_py.cpp b/dpnp/backend/extensions/lapack/lapack_py.cpp index 46471cc2f36..5e072bfeac8 100644 --- a/dpnp/backend/extensions/lapack/lapack_py.cpp +++ b/dpnp/backend/extensions/lapack/lapack_py.cpp @@ -83,10 +83,9 @@ PYBIND11_MODULE(_lapack_impl, m) .value("C", oneapi::mkl::transpose::C) .export_values(); // Optional, allows access like `Transpose.N` - // Register a custom LinAlgError exception in the dpnp.linalg submodule - py::module_ linalg_module = py::module_::import("dpnp.linalg"); - py::register_exception( - linalg_module, "LinAlgError", PyExc_ValueError); + // Register a LinAlgError exception in the current submodule + py::register_exception(m, "LinAlgError", + PyExc_ValueError); init_dispatch_vectors(); init_dispatch_tables(); diff --git a/dpnp/dpnp_iface.py b/dpnp/dpnp_iface.py index 4b354d54702..f7f4f470be4 100644 --- a/dpnp/dpnp_iface.py +++ b/dpnp/dpnp_iface.py @@ -52,7 +52,6 @@ from dpnp.dpnp_algo import * from dpnp.dpnp_array import dpnp_array from dpnp.fft import * -from dpnp.linalg import * from dpnp.memory import * from dpnp.random import * from dpnp.special import * diff --git a/dpnp/linalg/__init__.py b/dpnp/linalg/__init__.py index fd315e5ed43..3139f5dfb84 100644 --- a/dpnp/linalg/__init__.py +++ b/dpnp/linalg/__init__.py @@ -34,7 +34,44 @@ """ -from dpnp.linalg.dpnp_iface_linalg import * -from dpnp.linalg.dpnp_iface_linalg import __all__ as __all__linalg +from .dpnp_iface_linalg import ( + LinAlgError, +) +from .dpnp_iface_linalg import __all__ as __all__linalg +from .dpnp_iface_linalg import ( + cholesky, + cond, + cross, + det, + diagonal, + eig, + eigh, + eigvals, + eigvalsh, + inv, + lstsq, + lu_factor, + lu_solve, + matmul, + matrix_norm, + matrix_power, + matrix_rank, + matrix_transpose, + multi_dot, + norm, + outer, + pinv, + qr, + slogdet, + solve, + svd, + svdvals, + tensordot, + tensorinv, + tensorsolve, + trace, + vecdot, + vector_norm, +) __all__ = __all__linalg diff --git a/dpnp/linalg/dpnp_iface_linalg.py b/dpnp/linalg/dpnp_iface_linalg.py index f73443229c4..c31a56c2495 100644 --- a/dpnp/linalg/dpnp_iface_linalg.py +++ b/dpnp/linalg/dpnp_iface_linalg.py @@ -38,6 +38,7 @@ # pylint: disable=invalid-name # pylint: disable=no-member +# pylint: disable=no-name-in-module from typing import NamedTuple @@ -45,6 +46,7 @@ from dpctl.tensor._numpy_helper import normalize_axis_tuple import dpnp +from dpnp.backend.extensions.lapack._lapack_impl import LinAlgError from .dpnp_utils_linalg import ( assert_2d, @@ -70,6 +72,7 @@ ) __all__ = [ + "LinAlgError", "cholesky", "cond", "cross", @@ -105,6 +108,9 @@ "vector_norm", ] +# Need to set the module explicitly, since exposed by LAPACK pybind11 extension +LinAlgError.__module__ = "dpnp.linalg" + # pylint:disable=missing-class-docstring class EigResult(NamedTuple): @@ -2330,7 +2336,7 @@ def tensorsolve(a, b, axes=None): prod = numpy.prod(old_shape) if a.size != prod**2: - raise dpnp.linalg.LinAlgError( + raise LinAlgError( "Input arrays must satisfy the requirement " "prod(a.shape[b.ndim:]) == prod(a.shape[:b.ndim])" ) diff --git a/dpnp/linalg/dpnp_utils_linalg.py b/dpnp/linalg/dpnp_utils_linalg.py index 44a3816cc16..ce36b39f86a 100644 --- a/dpnp/linalg/dpnp_utils_linalg.py +++ b/dpnp/linalg/dpnp_utils_linalg.py @@ -50,7 +50,6 @@ import dpnp import dpnp.backend.extensions.lapack._lapack_impl as li from dpnp.dpnp_utils import get_usm_allocations -from dpnp.linalg import LinAlgError as LinAlgError __all__ = [ "assert_2d", @@ -943,7 +942,7 @@ def _check_lapack_dev_info(dev_info, error_msg=None): if any(dev_info): error_msg = error_msg or "Singular matrix" - raise LinAlgError(error_msg) + raise li.LinAlgError(error_msg) def _common_type(*arrays): @@ -1879,7 +1878,7 @@ def assert_2d(*arrays): for a in arrays: if a.ndim != 2: - raise LinAlgError( + raise li.LinAlgError( f"{a.ndim}-dimensional array given. The input " "array must be exactly two-dimensional" ) @@ -1906,7 +1905,7 @@ def assert_stacked_2d(*arrays): for a in arrays: if a.ndim < 2: - raise LinAlgError( + raise li.LinAlgError( f"{a.ndim}-dimensional array given. The input " "array must be at least two-dimensional" ) @@ -1942,7 +1941,7 @@ def assert_stacked_square(*arrays): for a in arrays: m, n = a.shape[-2:] if m != n: - raise LinAlgError( + raise li.LinAlgError( "Last 2 dimensions of the input array must be square" ) @@ -2086,7 +2085,7 @@ def dpnp_cond(x, p=None): """Compute the condition number of a matrix.""" if _is_empty_2d(x): - raise LinAlgError("cond is not defined on empty arrays") + raise li.LinAlgError("cond is not defined on empty arrays") if p is None or p == 2 or p == -2: s = dpnp.linalg.svd(x, compute_uv=False) if p == -2: @@ -2340,7 +2339,7 @@ def dpnp_lstsq(a, b, rcond=None): """ if b.ndim > 2: - raise LinAlgError( + raise li.LinAlgError( f"{b.ndim}-dimensional array given. The input " "array must be exactly two-dimensional" ) @@ -2348,7 +2347,7 @@ def dpnp_lstsq(a, b, rcond=None): m, n = a.shape[-2:] m2 = b.shape[0] if m != m2: - raise LinAlgError("Incompatible dimensions") + raise li.LinAlgError("Incompatible dimensions") u, s, vh = dpnp_svd(a, full_matrices=False, related_arrays=[b]) @@ -2669,20 +2668,20 @@ def dpnp_multi_dot(n, arrays, out=None): """Compute dot product of two or more arrays in a single function call.""" if not arrays[0].ndim in [1, 2]: - raise LinAlgError( + raise li.LinAlgError( f"{arrays[0].ndim}-dimensional array given. " "First array must be 1-D or 2-D." ) if not arrays[-1].ndim in [1, 2]: - raise LinAlgError( + raise li.LinAlgError( f"{arrays[-1].ndim}-dimensional array given. " "Last array must be 1-D or 2-D." ) for arr in arrays[1:-1]: if arr.ndim != 2: - raise LinAlgError( + raise li.LinAlgError( f"{arr.ndim}-dimensional array given. Inner arrays must be 2-D." )