diff --git a/.mypyignore b/.mypyignore index 16471471..133577ac 100644 --- a/.mypyignore +++ b/.mypyignore @@ -1,6 +1,9 @@ # scipy-stubs internal helper types (typecheck-only) scipy(\.\w+)?\._typing +# https://github.com/python/mypy/issues/17251 +scipy\.sparse\.linalg(\._?interface|\.matfuncs)?\.LinearOperator\.__init__ + # scipy tests scipy\.conftest scipy\.((_|\w)+\.)+(__test__|test|tests(\..+)?) diff --git a/scipy-stubs/sparse/linalg/_interface.pyi b/scipy-stubs/sparse/linalg/_interface.pyi index e79831ae..8e5771bf 100644 --- a/scipy-stubs/sparse/linalg/_interface.pyi +++ b/scipy-stubs/sparse/linalg/_interface.pyi @@ -1,7 +1,7 @@ # mypy: disable-error-code="override" from collections.abc import Callable, Iterable -from typing import Any, ClassVar, Final, Generic, Literal, Protocol, Self, TypeAlias, final, overload, type_check_only +from typing import Any, ClassVar, Final, Generic, Protocol, Self, TypeAlias, final, overload, type_check_only from typing_extensions import TypeVar, override import numpy as np @@ -21,65 +21,211 @@ _SCT2_co = TypeVar("_SCT2_co", bound=Numeric, default=_SCT1_co, covariant=True) _InexactT = TypeVar("_InexactT", bound=npc.inexact) _FunMatVecT_co = TypeVar("_FunMatVecT_co", bound=_FunMatVec, default=_FunMatVec, covariant=True) +_LinearOperatorT = TypeVar("_LinearOperatorT", bound=LinearOperator[Any]) +_LinearOperatorT_co = TypeVar("_LinearOperatorT_co", bound=LinearOperator[Any], covariant=True) + _ToShape: TypeAlias = Iterable[op.CanIndex] _Real: TypeAlias = np.bool_ | npc.integer | npc.floating -_Array1or2D: TypeAlias = onp.ArrayND[_SCT, tuple[int] | tuple[int, int]] -_FunMatVec: TypeAlias = Callable[[_Array1or2D[npc.number]], onp.ToComplex1D | onp.ToComplex2D] -_FunMatMat: TypeAlias = Callable[[onp.Array2D[npc.number]], onp.ToComplex2D] +_FunMatVec: TypeAlias = Callable[[onp.ArrayND[Any]], onp.ToComplex1D | onp.ToComplex2D] +_FunMatMat: TypeAlias = Callable[[onp.Array2D[Any]], onp.ToComplex2D] + +@type_check_only +class _CanAdjoint(Protocol[_LinearOperatorT_co]): + def _adjoint(self, /) -> _LinearOperatorT_co: ... + +@type_check_only +class _HasShapeAndMatVec(Protocol[_SCT_co]): + shape: tuple[int, int] + @overload + def matvec(self, /, x: onp.CanArray1D[np.float64]) -> onp.CanArray1D[_SCT_co]: ... + @overload + def matvec(self, /, x: onp.CanArray2D[np.float64]) -> onp.CanArray2D[_SCT_co]: ... + @overload + def matvec(self, /, x: onp.CanArray1D[np.complex128]) -> onp.ToComplex1D: ... + @overload + def matvec(self, /, x: onp.CanArray2D[np.complex128]) -> onp.ToComplex2D: ... + +@type_check_only +class _HasShapeAndDTypeAndMatVec(Protocol[_SCT_co]): + shape: tuple[int, int] + @property + def dtype(self, /) -> np.dtype[_SCT_co]: ... + @overload + def matvec(self, /, x: onp.CanArray1D[np.float64] | onp.CanArray1D[np.complex128]) -> onp.ToComplex1D: ... + @overload + def matvec(self, /, x: onp.CanArray2D[np.float64] | onp.CanArray2D[np.complex128]) -> onp.ToComplex2D: ... ### class LinearOperator(Generic[_SCT_co]): - __array_ufunc__: ClassVar[None] + __array_ufunc__: ClassVar[None] = None + ndim: ClassVar[int] = 2 - ndim: ClassVar[Literal[2]] = 2 shape: Final[tuple[int, int]] dtype: np.dtype[_SCT_co] + # keep in sync with `_CustomLinearOperator.__init__` + @overload # no dtype + def __new__( # type: ignore[overload-overlap] + cls, + shape: _ToShape, + matvec: _FunMatVec, + rmatvec: _FunMatVec | None = None, + matmat: _FunMatMat | None = None, + dtype: None = None, + rmatmat: _FunMatMat | None = None, + ) -> _CustomLinearOperator[np.int8 | Any]: ... + @overload # dtype known (positional) + def __new__( + cls, + shape: _ToShape, + matvec: _FunMatVec, + rmatvec: _FunMatVec | None, + matmat: _FunMatMat | None, + dtype: onp.ToDType[_SCT], + rmatmat: _FunMatMat | None = None, + ) -> _CustomLinearOperator[_SCT]: ... + @overload # dtype known (keyword) + def __new__( + cls, + shape: _ToShape, + matvec: _FunMatVec, + rmatvec: _FunMatVec | None = None, + matmat: _FunMatMat | None = None, + *, + dtype: onp.ToDType[_SCT], + rmatmat: _FunMatMat | None = None, + ) -> _CustomLinearOperator[_SCT]: ... + @overload # dtype-like int_ (positional) + def __new__( + cls, + shape: _ToShape, + matvec: _FunMatVec, + rmatvec: _FunMatVec | None, + matmat: _FunMatMat | None, + dtype: onp.AnyIntDType, + rmatmat: _FunMatMat | None = None, + ) -> _CustomLinearOperator[np.int_]: ... + @overload # dtype-like int_ (keyword) + def __new__( + cls, + shape: _ToShape, + matvec: _FunMatVec, + rmatvec: _FunMatVec | None = None, + matmat: _FunMatMat | None = None, + *, + dtype: onp.AnyIntDType, + rmatmat: _FunMatMat | None = None, + ) -> _CustomLinearOperator[np.int_]: ... + @overload # dtype-like float64 (positional) + def __new__( + cls, + shape: _ToShape, + matvec: _FunMatVec, + rmatvec: _FunMatVec | None, + matmat: _FunMatMat | None, + dtype: onp.AnyFloat64DType, + rmatmat: _FunMatMat | None = None, + ) -> _CustomLinearOperator[np.float64]: ... + @overload # dtype-like float64 (positional) + def __new__( + cls, + shape: _ToShape, + matvec: _FunMatVec, + rmatvec: _FunMatVec | None = None, + matmat: _FunMatMat | None = None, + *, + dtype: onp.AnyFloat64DType, + rmatmat: _FunMatMat | None = None, + ) -> _CustomLinearOperator[np.float64]: ... + @overload # dtype-like complex128 (positional) + def __new__( + cls, + shape: _ToShape, + matvec: _FunMatVec, + rmatvec: _FunMatVec | None, + matmat: _FunMatMat | None, + dtype: onp.AnyComplex128DType, + rmatmat: _FunMatMat | None = None, + ) -> _CustomLinearOperator[np.complex128]: ... + @overload # dtype-like complex128 (keyword) + def __new__( + cls, + shape: _ToShape, + matvec: _FunMatVec, + rmatvec: _FunMatVec | None = None, + matmat: _FunMatMat | None = None, + *, + dtype: onp.AnyComplex128DType, + rmatmat: _FunMatMat | None = None, + ) -> _CustomLinearOperator[np.complex128]: ... + @overload # unknown dtype + def __new__( + cls, + shape: _ToShape, + matvec: _FunMatVec, + rmatvec: _FunMatVec | None = None, + matmat: _FunMatMat | None = None, + dtype: type | str | None = None, + rmatmat: _FunMatMat | None = None, + ) -> _CustomLinearOperator[Any]: ... + + # NOTE: the `__init__` method cannot be annotated, because it will cause mypy to `__new__`: + # https://github.com/python/mypy/issues/17251 + + # ruff: noqa: ERA001 + + # @overload + # def __init__(self, /, dtype: onp.ToDType[_SCT_co], shape: _ToShape) -> None: ... + # @overload + # def __init__(self: LinearOperator[np.int_], /, dtype: onp.AnyIntDType, shape: _ToShape) -> None: ... + # @overload + # def __init__(self: LinearOperator[np.float64], /, dtype: onp.AnyFloat64DType, shape: _ToShape) -> None: ... + # @overload + # def __init__(self: LinearOperator[np.complex128], /, dtype: onp.AnyComplex128DType, shape: _ToShape) -> None: ... + # @overload + # def __init__(self: LinearOperator[Any], /, dtype: type | str | None, shape: _ToShape) -> None: ... + # @property - def H(self, /) -> _AdjointLinearOperator[_SCT_co]: ... - def adjoint(self, /) -> _AdjointLinearOperator[_SCT_co]: ... + def H(self: _CanAdjoint[_LinearOperatorT], /) -> _LinearOperatorT: ... + @final + def adjoint(self: _CanAdjoint[_LinearOperatorT], /) -> _LinearOperatorT: ... + def _adjoint(self, /) -> LinearOperator[_SCT_co]: ... + + # @property def T(self, /) -> _TransposedLinearOperator[_SCT_co]: ... def transpose(self, /) -> _TransposedLinearOperator[_SCT_co]: ... - # - def __new__(cls, *args: Any, **kwargs: Any) -> Self: ... - - # - @overload - def __init__(self, /, dtype: onp.ToDType[_SCT_co], shape: _ToShape) -> None: ... - @overload - def __init__(self: LinearOperator[np.int_], /, dtype: onp.AnyIntDType, shape: _ToShape) -> None: ... - @overload - def __init__(self: LinearOperator[np.float64], /, dtype: onp.AnyFloat64DType, shape: _ToShape) -> None: ... - @overload - def __init__(self: LinearOperator[np.complex128], /, dtype: onp.AnyComplex128DType, shape: _ToShape) -> None: ... - @overload - def __init__(self, /, dtype: onp.AnyInexactDType | None, shape: _ToShape) -> None: ... - # @overload # float array 1d def matvec(self, /, x: onp.ToFloatStrict1D) -> onp.Array1D[_SCT_co]: ... @overload # float matrix def matvec(self, /, x: onp.Matrix[_Real]) -> onp.Matrix[_SCT_co]: ... @overload # complex matrix - def matvec(self, /, x: onp.Matrix[npc.number]) -> onp.Matrix[_SCT_co | np.complex128]: ... + def matvec(self, /, x: onp.Matrix[np.complex128]) -> onp.Matrix[np.complex128]: ... @overload # float array 2d def matvec(self, /, x: onp.ToFloatStrict2D) -> onp.Array2D[_SCT_co]: ... @overload # complex array 1d - def matvec(self, /, x: onp.ToComplexStrict1D) -> onp.Array1D[_SCT_co | np.complex128]: ... + def matvec(self, /, x: onp.ToJustComplex128Strict1D) -> onp.Array1D[np.complex128]: ... @overload # complex array 2d - def matvec(self, /, x: onp.ToComplexStrict2D) -> onp.Array2D[_SCT_co | np.complex128]: ... + def matvec(self, /, x: onp.ToJustComplex128Strict2D) -> onp.Array2D[np.complex128]: ... @overload # float array - def matvec(self, /, x: onp.ToFloat2D) -> onp.Array1D[_SCT_co] | onp.Array2D[_SCT_co]: ... + def matvec(self, /, x: onp.ToFloat2D) -> onp.ArrayND[_SCT_co]: ... @overload # complex array - def matvec(self, /, x: onp.ToComplex2D) -> _Array1or2D[_SCT_co | np.complex128]: ... + def matvec(self, /, x: onp.ToJustComplex128_2D) -> onp.ArrayND[np.complex128]: ... + @overload # unknown array + def matvec(self, /, x: onp.ToComplex2D) -> onp.ArrayND[Any]: ... rmatvec = matvec # - def matmat(self, /, X: onp.ToComplex2D) -> onp.Array[tuple[int, int], _SCT_co | np.complex128]: ... + @overload + def matmat(self, /, X: onp.ToFloat2D) -> onp.Array2D[_SCT_co]: ... + @overload + def matmat(self, /, X: onp.ToJustComplex128_2D) -> onp.Array2D[np.complex128]: ... + @overload + def matmat(self, /, X: onp.ToComplex2D) -> onp.Array2D[Any]: ... rmatmat = matmat # @@ -88,19 +234,19 @@ class LinearOperator(Generic[_SCT_co]): @overload def dot(self, /, x: onp.ToFloat) -> _ScaledLinearOperator[_SCT_co]: ... @overload - def dot(self, /, x: onp.ToComplex) -> _ScaledLinearOperator[_SCT_co | np.complex128]: ... + def dot(self, /, x: onp.ToJustComplex128) -> _ScaledLinearOperator[np.complex128]: ... @overload def dot(self, /, x: onp.ToFloatStrict1D) -> onp.Array1D[_SCT_co]: ... @overload - def dot(self, /, x: onp.ToComplexStrict1D) -> onp.Array1D[_SCT_co | np.complex128]: ... + def dot(self, /, x: onp.ToJustComplex128Strict1D) -> onp.Array1D[np.complex128]: ... @overload def dot(self, /, x: onp.ToFloatStrict2D) -> onp.Array2D[_SCT_co]: ... @overload - def dot(self, /, x: onp.ToComplexStrict2D) -> onp.Array2D[_SCT_co | np.complex128]: ... + def dot(self, /, x: onp.ToJustComplex128Strict2D) -> onp.Array2D[np.complex128]: ... @overload - def dot(self, /, x: onp.ToFloatND) -> onp.Array1D[_SCT_co] | onp.Array2D[_SCT_co]: ... + def dot(self, /, x: onp.ToFloatND) -> onp.ArrayND[_SCT_co]: ... @overload - def dot(self, /, x: onp.ToComplexND) -> _Array1or2D[_SCT_co | np.complex128]: ... + def dot(self, /, x: onp.ToComplexND) -> onp.ArrayND[Any]: ... __mul__ = dot __rmul__ = dot __call__ = dot @@ -111,22 +257,24 @@ class LinearOperator(Generic[_SCT_co]): @overload def __matmul__(self, /, x: onp.ToFloatStrict1D) -> onp.Array1D[_SCT_co]: ... @overload - def __matmul__(self, /, x: onp.ToComplexStrict1D) -> onp.Array1D[_SCT_co | np.complex128]: ... + def __matmul__(self, /, x: onp.ToJustComplex128Strict1D) -> onp.Array1D[np.complex128]: ... @overload def __matmul__(self, /, x: onp.ToFloatStrict2D) -> onp.Array2D[_SCT_co]: ... @overload - def __matmul__(self, /, x: onp.ToComplexStrict2D) -> onp.Array2D[_SCT_co | np.complex128]: ... + def __matmul__(self, /, x: onp.ToJustComplex128Strict2D) -> onp.Array2D[np.complex128]: ... @overload - def __matmul__(self, /, x: onp.ToFloatND) -> onp.Array1D[_SCT_co] | onp.Array2D[_SCT_co]: ... + def __matmul__(self, /, x: onp.ToFloatND) -> onp.ArrayND[_SCT_co]: ... @overload - def __matmul__(self, /, x: onp.ToComplexND) -> _Array1or2D[_SCT_co | np.complex128]: ... + def __matmul__(self, /, x: onp.ToComplexND) -> onp.ArrayND[Any]: ... __rmatmul__ = __matmul__ # @overload def __truediv__(self, other: onp.ToFloat, /) -> _ScaledLinearOperator[_SCT_co]: ... @overload - def __truediv__(self, other: onp.ToComplex, /) -> _ScaledLinearOperator[_SCT_co | np.complex128]: ... + def __truediv__(self, other: onp.ToJustComplex128, /) -> _ScaledLinearOperator[np.complex128]: ... + @overload + def __truediv__(self, other: onp.ToComplex, /) -> _ScaledLinearOperator[Any]: ... # def __neg__(self, /) -> _ScaledLinearOperator[_SCT_co]: ... @@ -138,9 +286,10 @@ class LinearOperator(Generic[_SCT_co]): class _CustomLinearOperator(LinearOperator[_SCT_co], Generic[_SCT_co, _FunMatVecT_co]): args: tuple[()] + # @overload # no dtype def __init__( - self, + self: _CustomLinearOperator[np.int8 | Any], /, shape: _ToShape, matvec: _FunMatVec, @@ -172,6 +321,29 @@ class _CustomLinearOperator(LinearOperator[_SCT_co], Generic[_SCT_co, _FunMatVec dtype: onp.ToDType[_SCT_co], rmatmat: _FunMatMat | None = None, ) -> None: ... + @overload # dtype-like int_ (positional) + def __init__( + self: _CustomLinearOperator[np.int_], + /, + shape: _ToShape, + matvec: _FunMatVec, + rmatvec: _FunMatVec | None, + matmat: _FunMatMat | None, + dtype: onp.AnyIntDType, + rmatmat: _FunMatMat | None = None, + ) -> None: ... + @overload # dtype-like int_ (keyword) + def __init__( + self: _CustomLinearOperator[np.int_], + /, + shape: _ToShape, + matvec: _FunMatVec, + rmatvec: _FunMatVec | None = None, + matmat: _FunMatMat | None = None, + *, + dtype: onp.AnyIntDType, + rmatmat: _FunMatMat | None = None, + ) -> None: ... @overload # dtype-like float64 (positional) def __init__( self: _CustomLinearOperator[np.float64], @@ -218,13 +390,32 @@ class _CustomLinearOperator(LinearOperator[_SCT_co], Generic[_SCT_co, _FunMatVec dtype: onp.AnyComplex128DType, rmatmat: _FunMatMat | None = None, ) -> None: ... + @overload # unknown dtype + def __init__( + self: _CustomLinearOperator[Any], + /, + shape: _ToShape, + matvec: _FunMatVec, + rmatvec: _FunMatVec | None = None, + matmat: _FunMatMat | None = None, + dtype: type | str | None = None, + rmatmat: _FunMatMat | None = None, + ) -> None: ... + + # + @override + def _adjoint(self, /) -> Self: ... @type_check_only class _UnaryLinearOperator(LinearOperator[_SCT_co], Generic[_SCT_co]): A: LinearOperator[_SCT_co] args: tuple[LinearOperator[_SCT_co]] + # + def __new__(cls, A: LinearOperator[_SCT_co]) -> Self: ... def __init__(self, /, A: LinearOperator[_SCT_co]) -> None: ... + @override + def _adjoint(self, /) -> _AdjointLinearOperator[_SCT_co]: ... @final class _AdjointLinearOperator(_UnaryLinearOperator[_SCT_co], Generic[_SCT_co]): ... @@ -236,83 +427,109 @@ class _TransposedLinearOperator(_UnaryLinearOperator[_SCT_co], Generic[_SCT_co]) class _SumLinearOperator(LinearOperator[_SCT1_co | _SCT2_co], Generic[_SCT1_co, _SCT2_co]): args: tuple[LinearOperator[_SCT1_co], LinearOperator[_SCT2_co]] + def __new__(cls, A: LinearOperator[_SCT1_co], B: LinearOperator[_SCT2_co]) -> Self: ... def __init__(self, /, A: LinearOperator[_SCT1_co], B: LinearOperator[_SCT2_co]) -> None: ... + @override + def _adjoint(self, /) -> Self: ... @final class _ProductLinearOperator(LinearOperator[_SCT1_co | _SCT2_co], Generic[_SCT1_co, _SCT2_co]): args: tuple[LinearOperator[_SCT1_co], LinearOperator[_SCT2_co]] + # + def __new__(cls, A: LinearOperator[_SCT1_co], B: LinearOperator[_SCT2_co]) -> Self: ... def __init__(self, /, A: LinearOperator[_SCT1_co], B: LinearOperator[_SCT2_co]) -> None: ... + @override + def _adjoint(self, /) -> Self: ... @final class _ScaledLinearOperator(LinearOperator[_SCT_co], Generic[_SCT_co]): args: tuple[LinearOperator[_SCT_co], _SCT_co | complex] + + # + @overload + def __new__(cls, A: LinearOperator[_SCT_co], alpha: _SCT_co | complex) -> Self: ... # type: ignore[overload-overlap] + @overload + def __new__(cls, A: LinearOperator[npc.floating], alpha: onp.ToFloat64) -> _ScaledLinearOperator[np.float64]: ... + @overload + def __new__(cls, A: LinearOperator[npc.complexfloating], alpha: onp.ToComplex128) -> _ScaledLinearOperator[np.complex128]: ... + + # @overload def __init__(self, /, A: LinearOperator[_SCT_co], alpha: _SCT_co | complex) -> None: ... @overload - def __init__(self: _ScaledLinearOperator[np.float64], /, A: LinearOperator[npc.floating], alpha: float) -> None: ... + def __init__(self: _ScaledLinearOperator[np.float64], /, A: LinearOperator[npc.floating], alpha: onp.ToFloat64) -> None: ... @overload - def __init__(self: _ScaledLinearOperator[np.complex128], /, A: LinearOperator, alpha: complex) -> None: ... + def __init__(self: _ScaledLinearOperator[np.complex128], /, A: LinearOperator, alpha: onp.ToComplex128) -> None: ... + + # + @override + def _adjoint(self, /) -> Self: ... @final class _PowerLinearOperator(LinearOperator[_SCT_co], Generic[_SCT_co]): args: tuple[LinearOperator[_SCT_co], op.CanIndex] + def __new__(cls, A: LinearOperator[_SCT_co], p: op.CanIndex) -> Self: ... def __init__(self, /, A: LinearOperator[_SCT_co], p: op.CanIndex) -> None: ... + @override + def _adjoint(self, /) -> Self: ... class MatrixLinearOperator(LinearOperator[_SCT_co], Generic[_SCT_co]): A: _spbase | onp.Array2D[_SCT_co] args: tuple[_spbase | onp.Array2D[_SCT_co]] + def __new__(cls, A: _spbase | onp.ArrayND[_SCT_co]) -> Self: ... def __init__(self, /, A: _spbase | onp.ArrayND[_SCT_co]) -> None: ... + @override + def _adjoint(self, /) -> _AdjointMatrixOperator[_SCT_co]: ... @final class _AdjointMatrixOperator(MatrixLinearOperator[_SCT_co], Generic[_SCT_co]): args: tuple[MatrixLinearOperator[_SCT_co]] # type: ignore[assignment] # pyright: ignore[reportIncompatibleVariableOverride] + @property @override def dtype(self, /) -> np.dtype[_SCT_co]: ... # pyright: ignore[reportIncompatibleVariableOverride] - def __init__(self, /, adjoint_array: LinearOperator) -> None: ... + + # + def __new__(cls, adjoint_array: LinearOperator[_SCT_co]) -> Self: ... + def __init__(self, /, adjoint_array: LinearOperator[_SCT_co]) -> None: ... + @override + def _adjoint(self, /) -> MatrixLinearOperator[_SCT_co]: ... # pyright: ignore[reportIncompatibleMethodOverride] class IdentityOperator(LinearOperator[_SCT_co], Generic[_SCT_co]): @overload - def __init__(self, /, shape: _ToShape, dtype: onp.ToDType[_SCT_co]) -> None: ... + def __new__(cls, shape: _ToShape, dtype: onp.ToDType[_SCT_co]) -> Self: ... @overload - def __init__(self: IdentityOperator[np.float64], /, shape: _ToShape, dtype: onp.AnyFloat64DType | None = None) -> None: ... + def __new__(cls, shape: _ToShape, dtype: onp.AnyFloat64DType | None = None) -> IdentityOperator[np.float64]: ... @overload - def __init__(self: IdentityOperator[np.complex128], /, shape: _ToShape, dtype: onp.AnyComplex128DType) -> None: ... + def __new__(cls, shape: _ToShape, dtype: onp.AnyComplex128DType) -> IdentityOperator[np.complex128]: ... @overload - def __init__(self, /, shape: _ToShape, dtype: onp.AnyInexactDType) -> None: ... + def __new__(cls, shape: _ToShape, dtype: str) -> IdentityOperator[Any]: ... -@type_check_only -class _HasShapeAndMatVec(Protocol[_SCT_co]): - shape: tuple[int, int] + # @overload - def matvec(self, /, x: onp.CanArray1D[np.float64]) -> onp.CanArray1D[_SCT_co]: ... + def __init__(self, /, shape: _ToShape, dtype: onp.ToDType[_SCT_co]) -> None: ... @overload - def matvec(self, /, x: onp.CanArray2D[np.float64]) -> onp.CanArray2D[_SCT_co]: ... + def __init__(self: IdentityOperator[np.float64], /, shape: _ToShape, dtype: onp.AnyFloat64DType | None = None) -> None: ... @overload - def matvec(self, /, x: onp.CanArray1D[np.complex128]) -> onp.ToComplex1D: ... + def __init__(self: IdentityOperator[np.complex128], /, shape: _ToShape, dtype: onp.AnyComplex128DType) -> None: ... @overload - def matvec(self, /, x: onp.CanArray2D[np.complex128]) -> onp.ToComplex2D: ... + def __init__(self: IdentityOperator[Any], /, shape: _ToShape, dtype: str) -> None: ... -@type_check_only -class _HasShapeAndDTypeAndMatVec(Protocol[_SCT_co]): - shape: tuple[int, int] - @property - def dtype(self, /) -> np.dtype[_SCT_co]: ... - @overload - def matvec(self, /, x: onp.CanArray1D[np.float64] | onp.CanArray1D[np.complex128]) -> onp.ToComplex1D: ... - @overload - def matvec(self, /, x: onp.CanArray2D[np.float64] | onp.CanArray2D[np.complex128]) -> onp.ToComplex2D: ... + # + @override + def _adjoint(self, /) -> Self: ... +# @overload def aslinearoperator(A: onp.CanArrayND[_InexactT]) -> MatrixLinearOperator[_InexactT]: ... @overload def aslinearoperator(A: _spbase[_InexactT]) -> MatrixLinearOperator[_InexactT]: ... @overload def aslinearoperator( - A: onp.ArrayND[np.bool_ | npc.integer] | _spbase[np.bool_ | npc.integer], + A: onp.ArrayND[np.bool_ | npc.integer | np.float64] | _spbase[np.bool_ | npc.integer | np.float64], ) -> MatrixLinearOperator[np.float64]: ... @overload def aslinearoperator(A: _HasShapeAndDTypeAndMatVec[_InexactT]) -> MatrixLinearOperator[_InexactT]: ... diff --git a/tests/sparse/linalg/test_dsolve.pyi b/tests/sparse/linalg/test__dsolve.pyi similarity index 100% rename from tests/sparse/linalg/test_dsolve.pyi rename to tests/sparse/linalg/test__dsolve.pyi diff --git a/tests/sparse/linalg/test_expm_multiply.pyi b/tests/sparse/linalg/test__expm_multiply.pyi similarity index 100% rename from tests/sparse/linalg/test_expm_multiply.pyi rename to tests/sparse/linalg/test__expm_multiply.pyi diff --git a/tests/sparse/linalg/test__interface.pyi b/tests/sparse/linalg/test__interface.pyi new file mode 100644 index 00000000..d04e8225 --- /dev/null +++ b/tests/sparse/linalg/test__interface.pyi @@ -0,0 +1,24 @@ +from typing import Any, assert_type + +import numpy as np +import numpy.typing as npt + +from scipy.sparse.linalg import LinearOperator +from scipy.sparse.linalg._interface import _CustomLinearOperator + +int_type: type[int] +float_type: type[float] +complex_type: type[complex] + +### + +def mv(v: npt.NDArray[np.float64 | np.complex128]) -> npt.NDArray[np.float64 | np.complex128]: ... + +assert_type(LinearOperator((2, 2), matvec=mv, dtype=np.int16), _CustomLinearOperator[np.int16]) +assert_type(LinearOperator((2, 2), matvec=mv, dtype=int), _CustomLinearOperator[np.int_]) +assert_type(LinearOperator((2, 2), matvec=mv, dtype=float), _CustomLinearOperator[np.float64]) +assert_type(LinearOperator((2, 2), matvec=mv, dtype=complex), _CustomLinearOperator[np.complex128]) +assert_type(LinearOperator((2, 2), matvec=mv), _CustomLinearOperator[np.int8 | Any]) + +# TODO(jorenham): add more tests for `LinearOperator` +# TODO(jorenham): add tests for `aslinearoperator`